@cloudcli-ai/cloudcli 1.29.4 → 1.30.0
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.de.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.tr.md +252 -0
- package/README.zh-CN.md +1 -1
- package/dist/assets/index-DI22MZMb.css +32 -0
- package/dist/assets/index-RX-W_g_Q.js +1262 -0
- package/dist/index.html +2 -2
- package/dist-server/server/claude-sdk.js +13 -8
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +7 -7
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +5 -5
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/gemini-response-handler.js +2 -2
- package/dist-server/server/gemini-response-handler.js.map +1 -1
- package/dist-server/server/index.js +27 -25
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +103 -0
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js +103 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +266 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +13 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +84 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js +107 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +282 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +13 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +118 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js +80 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +369 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +13 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +131 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +82 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +207 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +13 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -0
- package/dist-server/server/modules/providers/provider.registry.js +31 -0
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -0
- package/dist-server/server/modules/providers/provider.routes.js +159 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -0
- package/dist-server/server/modules/providers/services/mcp.service.js +69 -0
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js +25 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions.service.js +29 -0
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js +14 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js +102 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +250 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -0
- package/dist-server/server/openai-codex.js +5 -5
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/routes/codex.js +2 -278
- package/dist-server/server/routes/codex.js.map +1 -1
- package/dist-server/server/routes/commands.js +0 -44
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/cursor.js +9 -489
- package/dist-server/server/routes/cursor.js.map +1 -1
- package/dist-server/server/routes/mcp-utils.js +1 -18
- package/dist-server/server/routes/mcp-utils.js.map +1 -1
- package/dist-server/server/routes/messages.js +6 -6
- package/dist-server/server/routes/messages.js.map +1 -1
- package/dist-server/server/routes/settings.js +10 -0
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +0 -459
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/dist-server/server/shared/interfaces.js +2 -0
- package/dist-server/server/shared/interfaces.js.map +1 -0
- package/dist-server/server/shared/types.js +3 -0
- package/dist-server/server/shared/types.js.map +1 -0
- package/dist-server/server/shared/utils.js +150 -0
- package/dist-server/server/shared/utils.js.map +1 -0
- package/dist-server/server/utils/mcp-detector.js +0 -49
- package/dist-server/server/utils/mcp-detector.js.map +1 -1
- package/dist-server/shared/modelConstants.js +1 -0
- package/dist-server/shared/modelConstants.js.map +1 -1
- package/package.json +6 -3
- package/server/claude-sdk.js +14 -8
- package/server/cursor-cli.js +7 -7
- package/server/gemini-cli.js +5 -5
- package/server/gemini-response-handler.js +2 -2
- package/server/index.js +33 -29
- package/server/modules/providers/list/claude/claude-auth.provider.ts +123 -0
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -0
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -0
- package/server/modules/providers/list/claude/claude.provider.ts +15 -0
- package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -0
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -0
- package/server/modules/providers/list/codex/codex.provider.ts +15 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -0
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -0
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +15 -0
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +151 -0
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -0
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +15 -0
- package/server/modules/providers/provider.registry.ts +36 -0
- package/server/modules/providers/provider.routes.ts +217 -0
- package/server/modules/providers/services/mcp.service.ts +94 -0
- package/server/modules/providers/services/provider-auth.service.ts +26 -0
- package/server/modules/providers/services/sessions.service.ts +45 -0
- package/server/modules/providers/shared/base/abstract.provider.ts +20 -0
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -0
- package/server/modules/providers/tests/mcp.test.ts +293 -0
- package/server/openai-codex.js +5 -5
- package/server/routes/codex.js +2 -312
- package/server/routes/commands.js +0 -49
- package/server/routes/cursor.js +15 -527
- package/server/routes/mcp-utils.js +2 -19
- package/server/routes/messages.js +6 -6
- package/server/routes/settings.js +10 -0
- package/server/routes/taskmaster.js +0 -492
- package/server/shared/interfaces.ts +54 -0
- package/server/shared/types.ts +172 -0
- package/server/shared/utils.ts +193 -0
- package/server/tsconfig.json +6 -3
- package/server/utils/mcp-detector.js +0 -51
- package/shared/modelConstants.js +1 -0
- package/dist/assets/index-BBAE1OJ_.css +0 -32
- package/dist/assets/index-Cyya3LhN.js +0 -1262
- package/dist-server/server/providers/claude/adapter.js +0 -266
- package/dist-server/server/providers/claude/adapter.js.map +0 -1
- package/dist-server/server/providers/claude/status.js +0 -121
- package/dist-server/server/providers/claude/status.js.map +0 -1
- package/dist-server/server/providers/codex/adapter.js +0 -232
- package/dist-server/server/providers/codex/adapter.js.map +0 -1
- package/dist-server/server/providers/codex/status.js +0 -70
- package/dist-server/server/providers/codex/status.js.map +0 -1
- package/dist-server/server/providers/cursor/adapter.js +0 -342
- package/dist-server/server/providers/cursor/adapter.js.map +0 -1
- package/dist-server/server/providers/cursor/status.js +0 -118
- package/dist-server/server/providers/cursor/status.js.map +0 -1
- package/dist-server/server/providers/gemini/adapter.js +0 -174
- package/dist-server/server/providers/gemini/adapter.js.map +0 -1
- package/dist-server/server/providers/gemini/status.js +0 -104
- package/dist-server/server/providers/gemini/status.js.map +0 -1
- package/dist-server/server/providers/registry.js +0 -58
- package/dist-server/server/providers/registry.js.map +0 -1
- package/dist-server/server/providers/types.js +0 -117
- package/dist-server/server/providers/types.js.map +0 -1
- package/dist-server/server/providers/utils.js +0 -28
- package/dist-server/server/providers/utils.js.map +0 -1
- package/dist-server/server/routes/cli-auth.js +0 -25
- package/dist-server/server/routes/cli-auth.js.map +0 -1
- package/dist-server/server/routes/mcp.js +0 -480
- package/dist-server/server/routes/mcp.js.map +0 -1
- package/server/providers/claude/adapter.js +0 -278
- package/server/providers/claude/status.js +0 -136
- package/server/providers/codex/adapter.js +0 -248
- package/server/providers/codex/status.js +0 -78
- package/server/providers/cursor/adapter.js +0 -348
- package/server/providers/cursor/status.js +0 -128
- package/server/providers/gemini/adapter.js +0 -186
- package/server/providers/gemini/status.js +0 -111
- package/server/providers/registry.js +0 -67
- package/server/providers/types.js +0 -132
- package/server/providers/utils.js +0 -29
- package/server/routes/cli-auth.js +0 -27
- package/server/routes/mcp.js +0 -552
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex Provider Status
|
|
3
|
-
*
|
|
4
|
-
* Checks whether the user has valid Codex authentication credentials.
|
|
5
|
-
* Codex uses an SDK that makes direct API calls (no external binary),
|
|
6
|
-
* so installation check always returns true if the server is running.
|
|
7
|
-
*
|
|
8
|
-
* @module providers/codex/status
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { promises as fs } from 'fs';
|
|
12
|
-
import path from 'path';
|
|
13
|
-
import os from 'os';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Check if Codex is installed.
|
|
17
|
-
* Codex SDK is bundled with this application — no external binary needed.
|
|
18
|
-
* @returns {boolean}
|
|
19
|
-
*/
|
|
20
|
-
export function checkInstalled() {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Full status check: installation + authentication.
|
|
26
|
-
* @returns {Promise<import('../types.js').ProviderStatus>}
|
|
27
|
-
*/
|
|
28
|
-
export async function checkStatus() {
|
|
29
|
-
const installed = checkInstalled();
|
|
30
|
-
const result = await checkCredentials();
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
installed,
|
|
34
|
-
authenticated: result.authenticated,
|
|
35
|
-
email: result.email || null,
|
|
36
|
-
error: result.error || null
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ─── Internal helpers ───────────────────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
async function checkCredentials() {
|
|
43
|
-
try {
|
|
44
|
-
const authPath = path.join(os.homedir(), '.codex', 'auth.json');
|
|
45
|
-
const content = await fs.readFile(authPath, 'utf8');
|
|
46
|
-
const auth = JSON.parse(content);
|
|
47
|
-
|
|
48
|
-
const tokens = auth.tokens || {};
|
|
49
|
-
|
|
50
|
-
if (tokens.id_token || tokens.access_token) {
|
|
51
|
-
let email = 'Authenticated';
|
|
52
|
-
if (tokens.id_token) {
|
|
53
|
-
try {
|
|
54
|
-
const parts = tokens.id_token.split('.');
|
|
55
|
-
if (parts.length >= 2) {
|
|
56
|
-
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
57
|
-
email = payload.email || payload.user || 'Authenticated';
|
|
58
|
-
}
|
|
59
|
-
} catch {
|
|
60
|
-
email = 'Authenticated';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { authenticated: true, email };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (auth.OPENAI_API_KEY) {
|
|
68
|
-
return { authenticated: true, email: 'API Key Auth' };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return { authenticated: false, email: null, error: 'No valid tokens found' };
|
|
72
|
-
} catch (error) {
|
|
73
|
-
if (error.code === 'ENOENT') {
|
|
74
|
-
return { authenticated: false, email: null, error: 'Codex not configured' };
|
|
75
|
-
}
|
|
76
|
-
return { authenticated: false, email: null, error: error.message };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor provider adapter.
|
|
3
|
-
*
|
|
4
|
-
* Normalizes Cursor CLI session history into NormalizedMessage format.
|
|
5
|
-
* @module adapters/cursor
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import os from 'os';
|
|
10
|
-
import crypto from 'crypto';
|
|
11
|
-
import { createNormalizedMessage, generateMessageId } from '../types.js';
|
|
12
|
-
|
|
13
|
-
const PROVIDER = 'cursor';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Load raw blobs from Cursor's SQLite store.db, parse the DAG structure,
|
|
17
|
-
* and return sorted message blobs in chronological order.
|
|
18
|
-
* @param {string} sessionId
|
|
19
|
-
* @param {string} projectPath - Absolute project path (used to compute cwdId hash)
|
|
20
|
-
* @returns {Promise<Array<{id: string, sequence: number, rowid: number, content: object}>>}
|
|
21
|
-
*/
|
|
22
|
-
async function loadCursorBlobs(sessionId, projectPath) {
|
|
23
|
-
// Lazy-import better-sqlite3 so the module doesn't fail if it's unavailable
|
|
24
|
-
const { default: Database } = await import('better-sqlite3');
|
|
25
|
-
|
|
26
|
-
const cwdId = crypto.createHash('md5').update(projectPath || process.cwd()).digest('hex');
|
|
27
|
-
const storeDbPath = path.join(os.homedir(), '.cursor', 'chats', cwdId, sessionId, 'store.db');
|
|
28
|
-
|
|
29
|
-
const db = new Database(storeDbPath, { readonly: true, fileMustExist: true });
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const allBlobs = db.prepare('SELECT rowid, id, data FROM blobs').all();
|
|
33
|
-
|
|
34
|
-
const blobMap = new Map();
|
|
35
|
-
const parentRefs = new Map();
|
|
36
|
-
const childRefs = new Map();
|
|
37
|
-
const jsonBlobs = [];
|
|
38
|
-
|
|
39
|
-
for (const blob of allBlobs) {
|
|
40
|
-
blobMap.set(blob.id, blob);
|
|
41
|
-
|
|
42
|
-
if (blob.data && blob.data[0] === 0x7B) {
|
|
43
|
-
try {
|
|
44
|
-
const parsed = JSON.parse(blob.data.toString('utf8'));
|
|
45
|
-
jsonBlobs.push({ ...blob, parsed });
|
|
46
|
-
} catch {
|
|
47
|
-
// skip unparseable blobs
|
|
48
|
-
}
|
|
49
|
-
} else if (blob.data) {
|
|
50
|
-
const parents = [];
|
|
51
|
-
let i = 0;
|
|
52
|
-
while (i < blob.data.length - 33) {
|
|
53
|
-
if (blob.data[i] === 0x0A && blob.data[i + 1] === 0x20) {
|
|
54
|
-
const parentHash = blob.data.slice(i + 2, i + 34).toString('hex');
|
|
55
|
-
if (blobMap.has(parentHash)) {
|
|
56
|
-
parents.push(parentHash);
|
|
57
|
-
}
|
|
58
|
-
i += 34;
|
|
59
|
-
} else {
|
|
60
|
-
i++;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (parents.length > 0) {
|
|
64
|
-
parentRefs.set(blob.id, parents);
|
|
65
|
-
for (const parentId of parents) {
|
|
66
|
-
if (!childRefs.has(parentId)) childRefs.set(parentId, []);
|
|
67
|
-
childRefs.get(parentId).push(blob.id);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Topological sort (DFS)
|
|
74
|
-
const visited = new Set();
|
|
75
|
-
const sorted = [];
|
|
76
|
-
function visit(nodeId) {
|
|
77
|
-
if (visited.has(nodeId)) return;
|
|
78
|
-
visited.add(nodeId);
|
|
79
|
-
for (const pid of (parentRefs.get(nodeId) || [])) visit(pid);
|
|
80
|
-
const b = blobMap.get(nodeId);
|
|
81
|
-
if (b) sorted.push(b);
|
|
82
|
-
}
|
|
83
|
-
for (const blob of allBlobs) {
|
|
84
|
-
if (!parentRefs.has(blob.id)) visit(blob.id);
|
|
85
|
-
}
|
|
86
|
-
for (const blob of allBlobs) visit(blob.id);
|
|
87
|
-
|
|
88
|
-
// Order JSON blobs by DAG appearance
|
|
89
|
-
const messageOrder = new Map();
|
|
90
|
-
let orderIndex = 0;
|
|
91
|
-
for (const blob of sorted) {
|
|
92
|
-
if (blob.data && blob.data[0] !== 0x7B) {
|
|
93
|
-
for (const jb of jsonBlobs) {
|
|
94
|
-
try {
|
|
95
|
-
const idBytes = Buffer.from(jb.id, 'hex');
|
|
96
|
-
if (blob.data.includes(idBytes) && !messageOrder.has(jb.id)) {
|
|
97
|
-
messageOrder.set(jb.id, orderIndex++);
|
|
98
|
-
}
|
|
99
|
-
} catch { /* skip */ }
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const sortedJsonBlobs = jsonBlobs.sort((a, b) => {
|
|
105
|
-
const oa = messageOrder.get(a.id) ?? Number.MAX_SAFE_INTEGER;
|
|
106
|
-
const ob = messageOrder.get(b.id) ?? Number.MAX_SAFE_INTEGER;
|
|
107
|
-
return oa !== ob ? oa - ob : a.rowid - b.rowid;
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const messages = [];
|
|
111
|
-
for (let idx = 0; idx < sortedJsonBlobs.length; idx++) {
|
|
112
|
-
const blob = sortedJsonBlobs[idx];
|
|
113
|
-
const parsed = blob.parsed;
|
|
114
|
-
if (!parsed) continue;
|
|
115
|
-
const role = parsed?.role || parsed?.message?.role;
|
|
116
|
-
if (role === 'system') continue;
|
|
117
|
-
messages.push({
|
|
118
|
-
id: blob.id,
|
|
119
|
-
sequence: idx + 1,
|
|
120
|
-
rowid: blob.rowid,
|
|
121
|
-
content: parsed,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return messages;
|
|
126
|
-
} finally {
|
|
127
|
-
db.close();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Normalize a realtime NDJSON event from Cursor CLI into NormalizedMessage(s).
|
|
133
|
-
* History uses normalizeCursorBlobs (SQLite DAG), this handles streaming NDJSON.
|
|
134
|
-
* @param {object|string} raw - A parsed NDJSON event or a raw text line
|
|
135
|
-
* @param {string} sessionId
|
|
136
|
-
* @returns {import('../types.js').NormalizedMessage[]}
|
|
137
|
-
*/
|
|
138
|
-
export function normalizeMessage(raw, sessionId) {
|
|
139
|
-
// Structured assistant message with content array
|
|
140
|
-
if (raw && typeof raw === 'object' && raw.type === 'assistant' && raw.message?.content?.[0]?.text) {
|
|
141
|
-
return [createNormalizedMessage({ kind: 'stream_delta', content: raw.message.content[0].text, sessionId, provider: PROVIDER })];
|
|
142
|
-
}
|
|
143
|
-
// Plain string line (non-JSON output)
|
|
144
|
-
if (typeof raw === 'string' && raw.trim()) {
|
|
145
|
-
return [createNormalizedMessage({ kind: 'stream_delta', content: raw, sessionId, provider: PROVIDER })];
|
|
146
|
-
}
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* @type {import('../types.js').ProviderAdapter}
|
|
152
|
-
*/
|
|
153
|
-
export const cursorAdapter = {
|
|
154
|
-
normalizeMessage,
|
|
155
|
-
/**
|
|
156
|
-
* Fetch session history for Cursor from SQLite store.db.
|
|
157
|
-
*/
|
|
158
|
-
async fetchHistory(sessionId, opts = {}) {
|
|
159
|
-
const { projectPath = '', limit = null, offset = 0 } = opts;
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const blobs = await loadCursorBlobs(sessionId, projectPath);
|
|
163
|
-
const allNormalized = cursorAdapter.normalizeCursorBlobs(blobs, sessionId);
|
|
164
|
-
|
|
165
|
-
// Apply pagination
|
|
166
|
-
if (limit !== null && limit > 0) {
|
|
167
|
-
const start = offset;
|
|
168
|
-
const page = allNormalized.slice(start, start + limit);
|
|
169
|
-
return {
|
|
170
|
-
messages: page,
|
|
171
|
-
total: allNormalized.length,
|
|
172
|
-
hasMore: start + limit < allNormalized.length,
|
|
173
|
-
offset,
|
|
174
|
-
limit,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
messages: allNormalized,
|
|
180
|
-
total: allNormalized.length,
|
|
181
|
-
hasMore: false,
|
|
182
|
-
offset: 0,
|
|
183
|
-
limit: null,
|
|
184
|
-
};
|
|
185
|
-
} catch (error) {
|
|
186
|
-
// DB doesn't exist or is unreadable — return empty
|
|
187
|
-
console.warn(`[CursorAdapter] Failed to load session ${sessionId}:`, error.message);
|
|
188
|
-
return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Normalize raw Cursor blob messages into NormalizedMessage[].
|
|
194
|
-
* @param {any[]} blobs - Raw cursor blobs from store.db ({id, sequence, rowid, content})
|
|
195
|
-
* @param {string} sessionId
|
|
196
|
-
* @returns {import('../types.js').NormalizedMessage[]}
|
|
197
|
-
*/
|
|
198
|
-
normalizeCursorBlobs(blobs, sessionId) {
|
|
199
|
-
const messages = [];
|
|
200
|
-
const toolUseMap = new Map();
|
|
201
|
-
|
|
202
|
-
// Use a fixed base timestamp so messages have stable, monotonically-increasing
|
|
203
|
-
// timestamps based on their sequence number rather than wall-clock time.
|
|
204
|
-
const baseTime = Date.now();
|
|
205
|
-
|
|
206
|
-
for (let i = 0; i < blobs.length; i++) {
|
|
207
|
-
const blob = blobs[i];
|
|
208
|
-
const content = blob.content;
|
|
209
|
-
const ts = new Date(baseTime + (blob.sequence ?? i) * 100).toISOString();
|
|
210
|
-
const baseId = blob.id || generateMessageId('cursor');
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
if (!content?.role || !content?.content) {
|
|
214
|
-
// Try nested message format
|
|
215
|
-
if (content?.message?.role && content?.message?.content) {
|
|
216
|
-
if (content.message.role === 'system') continue;
|
|
217
|
-
const role = content.message.role === 'user' ? 'user' : 'assistant';
|
|
218
|
-
let text = '';
|
|
219
|
-
if (Array.isArray(content.message.content)) {
|
|
220
|
-
text = content.message.content
|
|
221
|
-
.map(p => typeof p === 'string' ? p : p?.text || '')
|
|
222
|
-
.filter(Boolean)
|
|
223
|
-
.join('\n');
|
|
224
|
-
} else if (typeof content.message.content === 'string') {
|
|
225
|
-
text = content.message.content;
|
|
226
|
-
}
|
|
227
|
-
if (text?.trim()) {
|
|
228
|
-
messages.push(createNormalizedMessage({
|
|
229
|
-
id: baseId,
|
|
230
|
-
sessionId,
|
|
231
|
-
timestamp: ts,
|
|
232
|
-
provider: PROVIDER,
|
|
233
|
-
kind: 'text',
|
|
234
|
-
role,
|
|
235
|
-
content: text,
|
|
236
|
-
sequence: blob.sequence,
|
|
237
|
-
rowid: blob.rowid,
|
|
238
|
-
}));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (content.role === 'system') continue;
|
|
245
|
-
|
|
246
|
-
// Tool results
|
|
247
|
-
if (content.role === 'tool') {
|
|
248
|
-
const toolItems = Array.isArray(content.content) ? content.content : [];
|
|
249
|
-
for (const item of toolItems) {
|
|
250
|
-
if (item?.type !== 'tool-result') continue;
|
|
251
|
-
const toolCallId = item.toolCallId || content.id;
|
|
252
|
-
messages.push(createNormalizedMessage({
|
|
253
|
-
id: `${baseId}_tr`,
|
|
254
|
-
sessionId,
|
|
255
|
-
timestamp: ts,
|
|
256
|
-
provider: PROVIDER,
|
|
257
|
-
kind: 'tool_result',
|
|
258
|
-
toolId: toolCallId,
|
|
259
|
-
content: item.result || '',
|
|
260
|
-
isError: false,
|
|
261
|
-
}));
|
|
262
|
-
}
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const role = content.role === 'user' ? 'user' : 'assistant';
|
|
267
|
-
|
|
268
|
-
if (Array.isArray(content.content)) {
|
|
269
|
-
for (let partIdx = 0; partIdx < content.content.length; partIdx++) {
|
|
270
|
-
const part = content.content[partIdx];
|
|
271
|
-
|
|
272
|
-
if (part?.type === 'text' && part?.text) {
|
|
273
|
-
messages.push(createNormalizedMessage({
|
|
274
|
-
id: `${baseId}_${partIdx}`,
|
|
275
|
-
sessionId,
|
|
276
|
-
timestamp: ts,
|
|
277
|
-
provider: PROVIDER,
|
|
278
|
-
kind: 'text',
|
|
279
|
-
role,
|
|
280
|
-
content: part.text,
|
|
281
|
-
sequence: blob.sequence,
|
|
282
|
-
rowid: blob.rowid,
|
|
283
|
-
}));
|
|
284
|
-
} else if (part?.type === 'reasoning' && part?.text) {
|
|
285
|
-
messages.push(createNormalizedMessage({
|
|
286
|
-
id: `${baseId}_${partIdx}`,
|
|
287
|
-
sessionId,
|
|
288
|
-
timestamp: ts,
|
|
289
|
-
provider: PROVIDER,
|
|
290
|
-
kind: 'thinking',
|
|
291
|
-
content: part.text,
|
|
292
|
-
}));
|
|
293
|
-
} else if (part?.type === 'tool-call' || part?.type === 'tool_use') {
|
|
294
|
-
const toolName = (part.toolName || part.name || 'Unknown Tool') === 'ApplyPatch'
|
|
295
|
-
? 'Edit' : (part.toolName || part.name || 'Unknown Tool');
|
|
296
|
-
const toolId = part.toolCallId || part.id || `tool_${i}_${partIdx}`;
|
|
297
|
-
messages.push(createNormalizedMessage({
|
|
298
|
-
id: `${baseId}_${partIdx}`,
|
|
299
|
-
sessionId,
|
|
300
|
-
timestamp: ts,
|
|
301
|
-
provider: PROVIDER,
|
|
302
|
-
kind: 'tool_use',
|
|
303
|
-
toolName,
|
|
304
|
-
toolInput: part.args || part.input,
|
|
305
|
-
toolId,
|
|
306
|
-
}));
|
|
307
|
-
toolUseMap.set(toolId, messages[messages.length - 1]);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
} else if (typeof content.content === 'string' && content.content.trim()) {
|
|
311
|
-
messages.push(createNormalizedMessage({
|
|
312
|
-
id: baseId,
|
|
313
|
-
sessionId,
|
|
314
|
-
timestamp: ts,
|
|
315
|
-
provider: PROVIDER,
|
|
316
|
-
kind: 'text',
|
|
317
|
-
role,
|
|
318
|
-
content: content.content,
|
|
319
|
-
sequence: blob.sequence,
|
|
320
|
-
rowid: blob.rowid,
|
|
321
|
-
}));
|
|
322
|
-
}
|
|
323
|
-
} catch (error) {
|
|
324
|
-
console.warn('Error normalizing cursor blob:', error);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Attach tool results to tool_use messages
|
|
329
|
-
for (const msg of messages) {
|
|
330
|
-
if (msg.kind === 'tool_result' && msg.toolId && toolUseMap.has(msg.toolId)) {
|
|
331
|
-
const toolUse = toolUseMap.get(msg.toolId);
|
|
332
|
-
toolUse.toolResult = {
|
|
333
|
-
content: msg.content,
|
|
334
|
-
isError: msg.isError,
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Sort by sequence/rowid
|
|
340
|
-
messages.sort((a, b) => {
|
|
341
|
-
if (a.sequence !== undefined && b.sequence !== undefined) return a.sequence - b.sequence;
|
|
342
|
-
if (a.rowid !== undefined && b.rowid !== undefined) return a.rowid - b.rowid;
|
|
343
|
-
return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
return messages;
|
|
347
|
-
},
|
|
348
|
-
};
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor Provider Status
|
|
3
|
-
*
|
|
4
|
-
* Checks whether cursor-agent CLI is installed and whether the user
|
|
5
|
-
* is logged in.
|
|
6
|
-
*
|
|
7
|
-
* @module providers/cursor/status
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { execFileSync, spawn } from 'child_process';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if cursor-agent CLI is installed.
|
|
14
|
-
* @returns {boolean}
|
|
15
|
-
*/
|
|
16
|
-
export function checkInstalled() {
|
|
17
|
-
try {
|
|
18
|
-
execFileSync('cursor-agent', ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
19
|
-
return true;
|
|
20
|
-
} catch {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Full status check: installation + authentication.
|
|
27
|
-
* @returns {Promise<import('../types.js').ProviderStatus>}
|
|
28
|
-
*/
|
|
29
|
-
export async function checkStatus() {
|
|
30
|
-
const installed = checkInstalled();
|
|
31
|
-
|
|
32
|
-
if (!installed) {
|
|
33
|
-
return {
|
|
34
|
-
installed,
|
|
35
|
-
authenticated: false,
|
|
36
|
-
email: null,
|
|
37
|
-
error: 'Cursor CLI is not installed'
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const result = await checkCursorLogin();
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
installed,
|
|
45
|
-
authenticated: result.authenticated,
|
|
46
|
-
email: result.email || null,
|
|
47
|
-
error: result.error || null
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ─── Internal helpers ───────────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
function checkCursorLogin() {
|
|
54
|
-
return new Promise((resolve) => {
|
|
55
|
-
let processCompleted = false;
|
|
56
|
-
|
|
57
|
-
const timeout = setTimeout(() => {
|
|
58
|
-
if (!processCompleted) {
|
|
59
|
-
processCompleted = true;
|
|
60
|
-
if (childProcess) {
|
|
61
|
-
childProcess.kill();
|
|
62
|
-
}
|
|
63
|
-
resolve({
|
|
64
|
-
authenticated: false,
|
|
65
|
-
email: null,
|
|
66
|
-
error: 'Command timeout'
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}, 5000);
|
|
70
|
-
|
|
71
|
-
let childProcess;
|
|
72
|
-
try {
|
|
73
|
-
childProcess = spawn('cursor-agent', ['status']);
|
|
74
|
-
} catch {
|
|
75
|
-
clearTimeout(timeout);
|
|
76
|
-
processCompleted = true;
|
|
77
|
-
resolve({
|
|
78
|
-
authenticated: false,
|
|
79
|
-
email: null,
|
|
80
|
-
error: 'Cursor CLI not found or not installed'
|
|
81
|
-
});
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let stdout = '';
|
|
86
|
-
let stderr = '';
|
|
87
|
-
|
|
88
|
-
childProcess.stdout.on('data', (data) => {
|
|
89
|
-
stdout += data.toString();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
childProcess.stderr.on('data', (data) => {
|
|
93
|
-
stderr += data.toString();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
childProcess.on('close', (code) => {
|
|
97
|
-
if (processCompleted) return;
|
|
98
|
-
processCompleted = true;
|
|
99
|
-
clearTimeout(timeout);
|
|
100
|
-
|
|
101
|
-
if (code === 0) {
|
|
102
|
-
const emailMatch = stdout.match(/Logged in as ([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i);
|
|
103
|
-
|
|
104
|
-
if (emailMatch) {
|
|
105
|
-
resolve({ authenticated: true, email: emailMatch[1] });
|
|
106
|
-
} else if (stdout.includes('Logged in')) {
|
|
107
|
-
resolve({ authenticated: true, email: 'Logged in' });
|
|
108
|
-
} else {
|
|
109
|
-
resolve({ authenticated: false, email: null, error: 'Not logged in' });
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
resolve({ authenticated: false, email: null, error: stderr || 'Not logged in' });
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
childProcess.on('error', () => {
|
|
117
|
-
if (processCompleted) return;
|
|
118
|
-
processCompleted = true;
|
|
119
|
-
clearTimeout(timeout);
|
|
120
|
-
|
|
121
|
-
resolve({
|
|
122
|
-
authenticated: false,
|
|
123
|
-
email: null,
|
|
124
|
-
error: 'Cursor CLI not found or not installed'
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
}
|