@cloudcli-ai/cloudcli 1.30.0 → 1.31.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/dist/assets/{index-DI22MZMb.css → index-DF8TTVHl.css} +1 -1
- package/dist/assets/index-DbZAcc9z.js +1262 -0
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +115 -955
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/middleware/auth.js +1 -1
- package/dist-server/server/middleware/auth.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +125 -0
- package/dist-server/server/modules/database/connection.js.map +1 -0
- package/dist-server/server/modules/database/index.js +13 -0
- package/dist-server/server/modules/database/index.js.map +1 -0
- package/dist-server/server/modules/database/init-db.js +18 -0
- package/dist-server/server/modules/database/init-db.js.map +1 -0
- package/dist-server/server/modules/database/migrations.js +370 -0
- package/dist-server/server/modules/database/migrations.js.map +1 -0
- package/dist-server/server/modules/database/repositories/api-keys.js +72 -0
- package/dist-server/server/modules/database/repositories/api-keys.js.map +1 -0
- package/dist-server/server/modules/database/repositories/app-config.js +47 -0
- package/dist-server/server/modules/database/repositories/app-config.js.map +1 -0
- package/dist-server/server/modules/database/repositories/credentials.js +68 -0
- package/dist-server/server/modules/database/repositories/credentials.js.map +1 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js +54 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js.map +1 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js +72 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js +64 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.js +160 -0
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js +49 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js.map +1 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js +31 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +109 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/users.js +88 -0
- package/dist-server/server/modules/database/repositories/users.js.map +1 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js +38 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +143 -0
- package/dist-server/server/modules/database/schema.js.map +1 -0
- package/dist-server/server/modules/projects/index.js +4 -0
- package/dist-server/server/modules/projects/index.js.map +1 -0
- package/dist-server/server/modules/projects/projects.routes.js +188 -0
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +219 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js +65 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js +93 -0
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-star.service.js +60 -0
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js +167 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +169 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +126 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js +85 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js +95 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js +87 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/index.js +4 -0
- package/dist-server/server/modules/providers/index.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +63 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +144 -7
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +71 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +225 -13
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +127 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +2 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +296 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +277 -10
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +2 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +172 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +868 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +56 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +234 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions.service.js +78 -3
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +3 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +188 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +52 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +334 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js +36 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js +14 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js.map +1 -0
- package/dist-server/server/routes/agent.js +10 -17
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/auth.js +3 -1
- package/dist-server/server/routes/auth.js.map +1 -1
- package/dist-server/server/routes/commands.js +1 -1
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/gemini.js +2 -2
- package/dist-server/server/routes/gemini.js.map +1 -1
- package/dist-server/server/routes/git.js +33 -29
- package/dist-server/server/routes/git.js.map +1 -1
- package/dist-server/server/routes/settings.js +1 -1
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +97 -110
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/dist-server/server/routes/user.js +1 -1
- package/dist-server/server/routes/user.js.map +1 -1
- package/dist-server/server/services/notification-orchestrator.js +2 -2
- package/dist-server/server/services/notification-orchestrator.js.map +1 -1
- package/dist-server/server/services/vapid-keys.js +2 -1
- package/dist-server/server/services/vapid-keys.js.map +1 -1
- package/dist-server/server/shared/types.js +0 -1
- package/dist-server/server/shared/types.js.map +1 -1
- package/dist-server/server/shared/utils.js +408 -7
- package/dist-server/server/shared/utils.js.map +1 -1
- package/dist-server/server/utils/taskmaster-websocket.js +18 -12
- package/dist-server/server/utils/taskmaster-websocket.js.map +1 -1
- package/dist-server/shared/modelConstants.js +2 -1
- package/dist-server/shared/modelConstants.js.map +1 -1
- package/package.json +3 -1
- package/server/index.js +146 -1012
- package/server/middleware/auth.js +1 -1
- package/server/modules/database/connection.ts +143 -0
- package/server/modules/database/index.ts +12 -0
- package/server/modules/database/init-db.ts +17 -0
- package/server/modules/database/migrations.ts +442 -0
- package/server/modules/database/repositories/api-keys.ts +119 -0
- package/server/modules/database/repositories/app-config.ts +53 -0
- package/server/modules/database/repositories/credentials.ts +106 -0
- package/server/modules/database/repositories/github-tokens.ts +100 -0
- package/server/modules/database/repositories/notification-preferences.ts +103 -0
- package/server/modules/database/repositories/projects.db.integration.test.ts +72 -0
- package/server/modules/database/repositories/projects.db.ts +183 -0
- package/server/modules/database/repositories/push-subscriptions.ts +80 -0
- package/server/modules/database/repositories/scan-state.db.ts +42 -0
- package/server/modules/database/repositories/sessions.db.ts +174 -0
- package/server/modules/database/repositories/users.ts +140 -0
- package/server/modules/database/repositories/vapid-keys.ts +57 -0
- package/server/modules/database/schema.ts +152 -0
- package/server/modules/projects/index.ts +6 -0
- package/server/modules/projects/projects.routes.ts +247 -0
- package/server/modules/projects/services/project-clone.service.ts +321 -0
- package/server/modules/projects/services/project-delete.service.ts +75 -0
- package/server/modules/projects/services/project-management.service.ts +150 -0
- package/server/modules/projects/services/project-star.service.ts +78 -0
- package/server/modules/projects/services/projects-has-taskmaster.service.ts +248 -0
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +285 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +183 -0
- package/server/modules/projects/tests/project-management.service.test.ts +117 -0
- package/server/modules/projects/tests/project-star.service.test.ts +123 -0
- package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +105 -0
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +110 -0
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +182 -13
- package/server/modules/providers/list/claude/claude.provider.ts +3 -1
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +119 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +262 -17
- package/server/modules/providers/list/codex/codex.provider.ts +3 -1
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +176 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +3 -1
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +401 -0
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +327 -13
- package/server/modules/providers/list/gemini/gemini.provider.ts +3 -1
- package/server/modules/providers/provider.routes.ts +220 -12
- package/server/modules/providers/services/session-conversations-search.service.ts +1167 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +74 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +283 -0
- package/server/modules/providers/services/sessions.service.ts +89 -4
- package/server/modules/providers/shared/base/abstract.provider.ts +8 -1
- package/server/modules/websocket/README.md +267 -0
- package/server/modules/websocket/index.ts +2 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +271 -0
- package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +453 -0
- package/server/modules/websocket/services/websocket-auth.service.ts +54 -0
- package/server/modules/websocket/services/websocket-server.service.ts +58 -0
- package/server/modules/websocket/services/websocket-state.service.ts +16 -0
- package/server/modules/websocket/services/websocket-writer.service.ts +38 -0
- package/server/routes/agent.js +11 -16
- package/server/routes/auth.js +4 -2
- package/server/routes/commands.js +1 -1
- package/server/routes/gemini.js +3 -2
- package/server/routes/git.js +33 -28
- package/server/routes/settings.js +1 -1
- package/server/routes/taskmaster.js +108 -111
- package/server/routes/user.js +1 -1
- package/server/services/notification-orchestrator.js +3 -2
- package/server/services/vapid-keys.js +2 -1
- package/server/shared/interfaces.ts +39 -1
- package/server/shared/types.ts +185 -30
- package/server/shared/utils.ts +485 -9
- package/server/utils/taskmaster-websocket.js +19 -13
- package/shared/modelConstants.js +2 -1
- package/dist/assets/index-RX-W_g_Q.js +0 -1262
- package/dist-server/server/database/db.js +0 -535
- package/dist-server/server/database/db.js.map +0 -1
- package/dist-server/server/database/schema.js +0 -97
- package/dist-server/server/database/schema.js.map +0 -1
- package/dist-server/server/projects.js +0 -2325
- package/dist-server/server/projects.js.map +0 -1
- package/dist-server/server/routes/codex.js +0 -18
- package/dist-server/server/routes/codex.js.map +0 -1
- package/dist-server/server/routes/messages.js +0 -56
- package/dist-server/server/routes/messages.js.map +0 -1
- package/dist-server/server/routes/projects.js +0 -505
- package/dist-server/server/routes/projects.js.map +0 -1
- package/server/database/db.js +0 -593
- package/server/database/schema.js +0 -102
- package/server/projects.js +0 -2555
- package/server/routes/codex.js +0 -19
- package/server/routes/messages.js +0 -61
- package/server/routes/projects.js +0 -548
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { sessionsDb } from '@/modules/database/index.js';
|
|
5
|
+
import {
|
|
6
|
+
buildLookupMap,
|
|
7
|
+
extractFirstValidJsonlData,
|
|
8
|
+
findFilesRecursivelyCreatedAfter,
|
|
9
|
+
normalizeSessionName,
|
|
10
|
+
readFileTimestamps,
|
|
11
|
+
} from '@/shared/utils.js';
|
|
12
|
+
import type { IProviderSessionSynchronizer } from '@/shared/interfaces.js';
|
|
13
|
+
|
|
14
|
+
type ParsedSession = {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
projectPath: string;
|
|
17
|
+
sessionName?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Session indexer for Codex transcript artifacts.
|
|
22
|
+
*/
|
|
23
|
+
export class CodexSessionSynchronizer implements IProviderSessionSynchronizer {
|
|
24
|
+
private readonly provider = 'codex' as const;
|
|
25
|
+
private readonly codexHome = path.join(os.homedir(), '.codex');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Scans ~/.codex/sessions and upserts discovered sessions into DB.
|
|
29
|
+
*/
|
|
30
|
+
async synchronize(since?: Date): Promise<number> {
|
|
31
|
+
const nameMap = await buildLookupMap(path.join(this.codexHome, 'session_index.jsonl'), 'id', 'thread_name');
|
|
32
|
+
const files = await findFilesRecursivelyCreatedAfter(
|
|
33
|
+
path.join(this.codexHome, 'sessions'),
|
|
34
|
+
'.jsonl',
|
|
35
|
+
since ?? null
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
let processed = 0;
|
|
39
|
+
for (const filePath of files) {
|
|
40
|
+
const parsed = await this.processSessionFile(filePath, nameMap);
|
|
41
|
+
if (!parsed) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const existingSession = sessionsDb.getSessionById(parsed.sessionId);
|
|
46
|
+
if (existingSession) {
|
|
47
|
+
// If session name is untitled and we now have a name, update it
|
|
48
|
+
if (existingSession.custom_name === 'Untitled Codex Session' && parsed.sessionName && parsed.sessionName !== 'Untitled Codex Session') {
|
|
49
|
+
sessionsDb.updateSessionCustomName(parsed.sessionId, parsed.sessionName);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const timestamps = await readFileTimestamps(filePath);
|
|
54
|
+
sessionsDb.createSession(
|
|
55
|
+
parsed.sessionId,
|
|
56
|
+
this.provider,
|
|
57
|
+
parsed.projectPath,
|
|
58
|
+
parsed.sessionName,
|
|
59
|
+
timestamps.createdAt,
|
|
60
|
+
timestamps.updatedAt,
|
|
61
|
+
filePath
|
|
62
|
+
);
|
|
63
|
+
processed += 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return processed;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Parses and upserts one Codex session JSONL file.
|
|
71
|
+
*/
|
|
72
|
+
async synchronizeFile(filePath: string): Promise<string | null> {
|
|
73
|
+
if (!filePath.endsWith('.jsonl')) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const nameMap = await buildLookupMap(path.join(this.codexHome, 'session_index.jsonl'), 'id', 'thread_name');
|
|
78
|
+
const parsed = await this.processSessionFile(filePath, nameMap);
|
|
79
|
+
if (!parsed) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const timestamps = await readFileTimestamps(filePath);
|
|
84
|
+
return sessionsDb.createSession(
|
|
85
|
+
parsed.sessionId,
|
|
86
|
+
this.provider,
|
|
87
|
+
parsed.projectPath,
|
|
88
|
+
parsed.sessionName,
|
|
89
|
+
timestamps.createdAt,
|
|
90
|
+
timestamps.updatedAt,
|
|
91
|
+
filePath
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Extracts session metadata from one Codex JSONL session file.
|
|
97
|
+
*/
|
|
98
|
+
private async processSessionFile(
|
|
99
|
+
filePath: string,
|
|
100
|
+
nameMap: Map<string, string>
|
|
101
|
+
): Promise<ParsedSession | null> {
|
|
102
|
+
return extractFirstValidJsonlData(filePath, (rawData) => {
|
|
103
|
+
const data = rawData as Record<string, unknown>;
|
|
104
|
+
const payload = data.payload as Record<string, unknown> | undefined;
|
|
105
|
+
const sessionId = typeof payload?.id === 'string' ? payload.id : undefined;
|
|
106
|
+
const projectPath = typeof payload?.cwd === 'string' ? payload.cwd : undefined;
|
|
107
|
+
|
|
108
|
+
if (!sessionId || !projectPath) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
sessionId,
|
|
114
|
+
projectPath,
|
|
115
|
+
sessionName: normalizeSessionName(nameMap.get(sessionId), 'Untitled Codex Session'),
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fsSync from 'node:fs';
|
|
2
|
+
import readline from 'node:readline';
|
|
3
|
+
|
|
4
|
+
import { sessionsDb } from '@/modules/database/index.js';
|
|
2
5
|
import type { IProviderSessions } from '@/shared/interfaces.js';
|
|
3
6
|
import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
|
|
4
7
|
import { createNormalizedMessage, generateMessageId, readObjectRecord } from '@/shared/utils.js';
|
|
@@ -11,14 +14,250 @@ type CodexHistoryResult =
|
|
|
11
14
|
messages?: AnyRecord[];
|
|
12
15
|
total?: number;
|
|
13
16
|
hasMore?: boolean;
|
|
17
|
+
offset?: number;
|
|
18
|
+
limit?: number | null;
|
|
14
19
|
tokenUsage?: unknown;
|
|
15
20
|
};
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
function isVisibleCodexUserMessage(payload: AnyRecord | null | undefined): boolean {
|
|
23
|
+
if (!payload || payload.type !== 'user_message') {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (payload.kind && payload.kind !== 'plain') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return typeof payload.message === 'string' && payload.message.trim().length > 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function extractCodexTextContent(content: unknown): string {
|
|
35
|
+
if (!Array.isArray(content)) {
|
|
36
|
+
return typeof content === 'string' ? content : '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return content
|
|
40
|
+
.map((item) => {
|
|
41
|
+
if (!item || typeof item !== 'object') {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const record = item as AnyRecord;
|
|
46
|
+
if (
|
|
47
|
+
(record.type === 'input_text' || record.type === 'output_text' || record.type === 'text')
|
|
48
|
+
&& typeof record.text === 'string'
|
|
49
|
+
) {
|
|
50
|
+
return record.text;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return '';
|
|
54
|
+
})
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
.join('\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function getCodexSessionMessages(
|
|
18
60
|
sessionId: string,
|
|
19
|
-
limit: number | null,
|
|
20
|
-
offset
|
|
21
|
-
)
|
|
61
|
+
limit: number | null = null,
|
|
62
|
+
offset = 0,
|
|
63
|
+
): Promise<CodexHistoryResult> {
|
|
64
|
+
try {
|
|
65
|
+
const sessionFilePath = sessionsDb.getSessionById(sessionId)?.jsonl_path;
|
|
66
|
+
|
|
67
|
+
if (!sessionFilePath) {
|
|
68
|
+
console.warn(`Codex session file not found for session ${sessionId}`);
|
|
69
|
+
return { messages: [], total: 0, hasMore: false };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const messages: AnyRecord[] = [];
|
|
73
|
+
let tokenUsage: AnyRecord | null = null;
|
|
74
|
+
const fileStream = fsSync.createReadStream(sessionFilePath);
|
|
75
|
+
const rl = readline.createInterface({
|
|
76
|
+
input: fileStream,
|
|
77
|
+
crlfDelay: Infinity,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
for await (const line of rl) {
|
|
81
|
+
if (!line.trim()) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const entry = JSON.parse(line) as AnyRecord;
|
|
87
|
+
|
|
88
|
+
if (entry.type === 'event_msg' && entry.payload?.type === 'token_count' && entry.payload?.info) {
|
|
89
|
+
const info = entry.payload.info as AnyRecord;
|
|
90
|
+
if (info.total_token_usage) {
|
|
91
|
+
const usage = info.total_token_usage as AnyRecord;
|
|
92
|
+
tokenUsage = {
|
|
93
|
+
used: usage.total_tokens || 0,
|
|
94
|
+
total: info.model_context_window || 200000,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (entry.type === 'event_msg' && isVisibleCodexUserMessage(entry.payload as AnyRecord)) {
|
|
100
|
+
messages.push({
|
|
101
|
+
type: 'user',
|
|
102
|
+
timestamp: entry.timestamp,
|
|
103
|
+
message: {
|
|
104
|
+
role: 'user',
|
|
105
|
+
content: entry.payload.message,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
entry.type === 'response_item' &&
|
|
112
|
+
entry.payload?.type === 'message' &&
|
|
113
|
+
entry.payload.role === 'assistant'
|
|
114
|
+
) {
|
|
115
|
+
const textContent = extractCodexTextContent(entry.payload.content);
|
|
116
|
+
if (textContent.trim()) {
|
|
117
|
+
messages.push({
|
|
118
|
+
type: 'assistant',
|
|
119
|
+
timestamp: entry.timestamp,
|
|
120
|
+
message: {
|
|
121
|
+
role: 'assistant',
|
|
122
|
+
content: textContent,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (entry.type === 'response_item' && entry.payload?.type === 'reasoning') {
|
|
129
|
+
const summaryText = Array.isArray(entry.payload.summary)
|
|
130
|
+
? entry.payload.summary
|
|
131
|
+
.map((item: AnyRecord) => item?.text)
|
|
132
|
+
.filter(Boolean)
|
|
133
|
+
.join('\n')
|
|
134
|
+
: '';
|
|
135
|
+
|
|
136
|
+
if (summaryText.trim()) {
|
|
137
|
+
messages.push({
|
|
138
|
+
type: 'thinking',
|
|
139
|
+
timestamp: entry.timestamp,
|
|
140
|
+
message: {
|
|
141
|
+
role: 'assistant',
|
|
142
|
+
content: summaryText,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (entry.type === 'response_item' && entry.payload?.type === 'function_call') {
|
|
149
|
+
let toolName = entry.payload.name;
|
|
150
|
+
let toolInput = entry.payload.arguments;
|
|
151
|
+
|
|
152
|
+
if (toolName === 'shell_command') {
|
|
153
|
+
toolName = 'Bash';
|
|
154
|
+
try {
|
|
155
|
+
const args = JSON.parse(entry.payload.arguments) as AnyRecord;
|
|
156
|
+
toolInput = JSON.stringify({ command: args.command });
|
|
157
|
+
} catch {
|
|
158
|
+
// Keep original arguments when parsing fails.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
messages.push({
|
|
163
|
+
type: 'tool_use',
|
|
164
|
+
timestamp: entry.timestamp,
|
|
165
|
+
toolName,
|
|
166
|
+
toolInput,
|
|
167
|
+
toolCallId: entry.payload.call_id,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (entry.type === 'response_item' && entry.payload?.type === 'function_call_output') {
|
|
172
|
+
messages.push({
|
|
173
|
+
type: 'tool_result',
|
|
174
|
+
timestamp: entry.timestamp,
|
|
175
|
+
toolCallId: entry.payload.call_id,
|
|
176
|
+
output: entry.payload.output,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (entry.type === 'response_item' && entry.payload?.type === 'custom_tool_call') {
|
|
181
|
+
const toolName = entry.payload.name || 'custom_tool';
|
|
182
|
+
const input = entry.payload.input || '';
|
|
183
|
+
|
|
184
|
+
if (toolName === 'apply_patch') {
|
|
185
|
+
const fileMatch = String(input).match(/\*\*\* Update File: (.+)/);
|
|
186
|
+
const filePath = fileMatch ? fileMatch[1].trim() : 'unknown';
|
|
187
|
+
const lines = String(input).split('\n');
|
|
188
|
+
const oldLines: string[] = [];
|
|
189
|
+
const newLines: string[] = [];
|
|
190
|
+
|
|
191
|
+
for (const lineContent of lines) {
|
|
192
|
+
if (lineContent.startsWith('-') && !lineContent.startsWith('---')) {
|
|
193
|
+
oldLines.push(lineContent.slice(1));
|
|
194
|
+
} else if (lineContent.startsWith('+') && !lineContent.startsWith('+++')) {
|
|
195
|
+
newLines.push(lineContent.slice(1));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
messages.push({
|
|
200
|
+
type: 'tool_use',
|
|
201
|
+
timestamp: entry.timestamp,
|
|
202
|
+
toolName: 'Edit',
|
|
203
|
+
toolInput: JSON.stringify({
|
|
204
|
+
file_path: filePath,
|
|
205
|
+
old_string: oldLines.join('\n'),
|
|
206
|
+
new_string: newLines.join('\n'),
|
|
207
|
+
}),
|
|
208
|
+
toolCallId: entry.payload.call_id,
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
messages.push({
|
|
212
|
+
type: 'tool_use',
|
|
213
|
+
timestamp: entry.timestamp,
|
|
214
|
+
toolName,
|
|
215
|
+
toolInput: input,
|
|
216
|
+
toolCallId: entry.payload.call_id,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (entry.type === 'response_item' && entry.payload?.type === 'custom_tool_call_output') {
|
|
222
|
+
messages.push({
|
|
223
|
+
type: 'tool_result',
|
|
224
|
+
timestamp: entry.timestamp,
|
|
225
|
+
toolCallId: entry.payload.call_id,
|
|
226
|
+
output: entry.payload.output || '',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
} catch {
|
|
230
|
+
// Skip malformed lines.
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
messages.sort(
|
|
235
|
+
(a, b) => new Date(a.timestamp || 0).getTime() - new Date(b.timestamp || 0).getTime(),
|
|
236
|
+
);
|
|
237
|
+
const total = messages.length;
|
|
238
|
+
|
|
239
|
+
if (limit !== null) {
|
|
240
|
+
const startIndex = Math.max(0, total - offset - limit);
|
|
241
|
+
const endIndex = total - offset;
|
|
242
|
+
const paginatedMessages = messages.slice(startIndex, endIndex);
|
|
243
|
+
const hasMore = startIndex > 0;
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
messages: paginatedMessages,
|
|
247
|
+
total,
|
|
248
|
+
hasMore,
|
|
249
|
+
offset,
|
|
250
|
+
limit,
|
|
251
|
+
tokenUsage,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { messages, tokenUsage };
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(`Error reading Codex session messages for ${sessionId}:`, error);
|
|
258
|
+
return { messages: [], total: 0, hasMore: false };
|
|
259
|
+
}
|
|
260
|
+
}
|
|
22
261
|
|
|
23
262
|
export class CodexSessionsProvider implements IProviderSessions {
|
|
24
263
|
/**
|
|
@@ -31,6 +270,23 @@ export class CodexSessionsProvider implements IProviderSessions {
|
|
|
31
270
|
const ts = raw.timestamp || new Date().toISOString();
|
|
32
271
|
const baseId = raw.uuid || generateMessageId('codex');
|
|
33
272
|
|
|
273
|
+
if (raw.type === 'thinking' || raw.isReasoning) {
|
|
274
|
+
const thinkingContent = typeof raw.message?.content === 'string'
|
|
275
|
+
? raw.message.content
|
|
276
|
+
: '';
|
|
277
|
+
if (!thinkingContent.trim()) {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
return [createNormalizedMessage({
|
|
281
|
+
id: baseId,
|
|
282
|
+
sessionId,
|
|
283
|
+
timestamp: ts,
|
|
284
|
+
provider: PROVIDER,
|
|
285
|
+
kind: 'thinking',
|
|
286
|
+
content: thinkingContent,
|
|
287
|
+
})];
|
|
288
|
+
}
|
|
289
|
+
|
|
34
290
|
if (raw.message?.role === 'user') {
|
|
35
291
|
const content = typeof raw.message.content === 'string'
|
|
36
292
|
? raw.message.content
|
|
@@ -77,17 +333,6 @@ export class CodexSessionsProvider implements IProviderSessions {
|
|
|
77
333
|
})];
|
|
78
334
|
}
|
|
79
335
|
|
|
80
|
-
if (raw.type === 'thinking' || raw.isReasoning) {
|
|
81
|
-
return [createNormalizedMessage({
|
|
82
|
-
id: baseId,
|
|
83
|
-
sessionId,
|
|
84
|
-
timestamp: ts,
|
|
85
|
-
provider: PROVIDER,
|
|
86
|
-
kind: 'thinking',
|
|
87
|
-
content: raw.message?.content || '',
|
|
88
|
-
})];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
336
|
if (raw.type === 'tool_use' || raw.toolName) {
|
|
92
337
|
return [createNormalizedMessage({
|
|
93
338
|
id: baseId,
|
|
@@ -275,7 +520,7 @@ export class CodexSessionsProvider implements IProviderSessions {
|
|
|
275
520
|
|
|
276
521
|
let result: CodexHistoryResult;
|
|
277
522
|
try {
|
|
278
|
-
result = await
|
|
523
|
+
result = await getCodexSessionMessages(sessionId, limit, offset);
|
|
279
524
|
} catch (error) {
|
|
280
525
|
const message = error instanceof Error ? error.message : String(error);
|
|
281
526
|
console.warn(`[CodexProvider] Failed to load session ${sessionId}:`, message);
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
|
|
2
2
|
import { CodexProviderAuth } from '@/modules/providers/list/codex/codex-auth.provider.js';
|
|
3
3
|
import { CodexMcpProvider } from '@/modules/providers/list/codex/codex-mcp.provider.js';
|
|
4
|
+
import { CodexSessionSynchronizer } from '@/modules/providers/list/codex/codex-session-synchronizer.provider.js';
|
|
4
5
|
import { CodexSessionsProvider } from '@/modules/providers/list/codex/codex-sessions.provider.js';
|
|
5
|
-
import type { IProviderAuth, IProviderSessions } from '@/shared/interfaces.js';
|
|
6
|
+
import type { IProviderAuth, IProviderSessionSynchronizer, IProviderSessions } from '@/shared/interfaces.js';
|
|
6
7
|
|
|
7
8
|
export class CodexProvider extends AbstractProvider {
|
|
8
9
|
readonly mcp = new CodexMcpProvider();
|
|
9
10
|
readonly auth: IProviderAuth = new CodexProviderAuth();
|
|
10
11
|
readonly sessions: IProviderSessions = new CodexSessionsProvider();
|
|
12
|
+
readonly sessionSynchronizer: IProviderSessionSynchronizer = new CodexSessionSynchronizer();
|
|
11
13
|
|
|
12
14
|
constructor() {
|
|
13
15
|
super('codex');
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import fsp from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import readline from 'node:readline';
|
|
7
|
+
|
|
8
|
+
import { sessionsDb } from '@/modules/database/index.js';
|
|
9
|
+
import {
|
|
10
|
+
extractFirstValidJsonlData,
|
|
11
|
+
findFilesRecursivelyCreatedAfter,
|
|
12
|
+
normalizeSessionName,
|
|
13
|
+
readFileTimestamps,
|
|
14
|
+
} from '@/shared/utils.js';
|
|
15
|
+
import type { IProviderSessionSynchronizer } from '@/shared/interfaces.js';
|
|
16
|
+
|
|
17
|
+
type ParsedSession = {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
projectPath: string;
|
|
20
|
+
sessionName?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns directory entries or an empty list when the folder is missing.
|
|
25
|
+
*/
|
|
26
|
+
async function listDirectoryEntriesSafe(
|
|
27
|
+
directoryPath: string
|
|
28
|
+
): Promise<import('node:fs').Dirent[]> {
|
|
29
|
+
try {
|
|
30
|
+
return await fsp.readdir(directoryPath, { withFileTypes: true });
|
|
31
|
+
} catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Session indexer for Cursor transcript artifacts.
|
|
38
|
+
*/
|
|
39
|
+
export class CursorSessionSynchronizer implements IProviderSessionSynchronizer {
|
|
40
|
+
private readonly provider = 'cursor' as const;
|
|
41
|
+
private readonly cursorHome = path.join(os.homedir(), '.cursor');
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Scans Cursor chats and upserts discovered sessions into DB.
|
|
45
|
+
*/
|
|
46
|
+
async synchronize(since?: Date): Promise<number> {
|
|
47
|
+
const projectsDir = path.join(this.cursorHome, 'projects');
|
|
48
|
+
const projectEntries = await listDirectoryEntriesSafe(projectsDir);
|
|
49
|
+
const seenProjectPaths = new Set<string>();
|
|
50
|
+
|
|
51
|
+
let processed = 0;
|
|
52
|
+
for (const entry of projectEntries) {
|
|
53
|
+
if (!entry.isDirectory()) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const workerLogPath = path.join(projectsDir, entry.name, 'worker.log');
|
|
58
|
+
const projectPath = await this.extractProjectPathFromWorkerLog(workerLogPath);
|
|
59
|
+
if (!projectPath || seenProjectPaths.has(projectPath)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
seenProjectPaths.add(projectPath);
|
|
64
|
+
const projectHash = this.md5(projectPath);
|
|
65
|
+
const chatsDir = path.join(this.cursorHome, 'chats', projectHash);
|
|
66
|
+
const files = await findFilesRecursivelyCreatedAfter(chatsDir, '.jsonl', since ?? null);
|
|
67
|
+
|
|
68
|
+
for (const filePath of files) {
|
|
69
|
+
const parsed = await this.processSessionFile(filePath);
|
|
70
|
+
if (!parsed) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const timestamps = await readFileTimestamps(filePath);
|
|
75
|
+
sessionsDb.createSession(
|
|
76
|
+
parsed.sessionId,
|
|
77
|
+
this.provider,
|
|
78
|
+
parsed.projectPath,
|
|
79
|
+
parsed.sessionName,
|
|
80
|
+
timestamps.createdAt,
|
|
81
|
+
timestamps.updatedAt,
|
|
82
|
+
filePath
|
|
83
|
+
);
|
|
84
|
+
processed += 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return processed;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Parses and upserts one Cursor session JSONL file.
|
|
93
|
+
*/
|
|
94
|
+
async synchronizeFile(filePath: string): Promise<string | null> {
|
|
95
|
+
if (!filePath.endsWith('.jsonl')) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const parsed = await this.processSessionFile(filePath);
|
|
100
|
+
if (!parsed) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const timestamps = await readFileTimestamps(filePath);
|
|
105
|
+
return sessionsDb.createSession(
|
|
106
|
+
parsed.sessionId,
|
|
107
|
+
this.provider,
|
|
108
|
+
parsed.projectPath,
|
|
109
|
+
parsed.sessionName,
|
|
110
|
+
timestamps.createdAt,
|
|
111
|
+
timestamps.updatedAt,
|
|
112
|
+
filePath
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Produces the same project hash Cursor uses in chat directory names.
|
|
118
|
+
*/
|
|
119
|
+
private md5(input: string): string {
|
|
120
|
+
return crypto.createHash('md5').update(input).digest('hex');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Extracts project path from Cursor worker.log.
|
|
125
|
+
*/
|
|
126
|
+
private async extractProjectPathFromWorkerLog(filePath: string): Promise<string | null> {
|
|
127
|
+
try {
|
|
128
|
+
const fileStream = fs.createReadStream(filePath, { encoding: 'utf8' });
|
|
129
|
+
const lineReader = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
130
|
+
|
|
131
|
+
for await (const line of lineReader) {
|
|
132
|
+
const match = line.match(/workspacePath=(.*)$/);
|
|
133
|
+
const projectPath = match?.[1]?.trim();
|
|
134
|
+
if (projectPath) {
|
|
135
|
+
lineReader.close();
|
|
136
|
+
fileStream.close();
|
|
137
|
+
return projectPath;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
// Missing worker logs are valid for partial or incomplete session data.
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Extracts session metadata from one Cursor JSONL session file.
|
|
149
|
+
*/
|
|
150
|
+
private async processSessionFile(filePath: string): Promise<ParsedSession | null> {
|
|
151
|
+
const sessionId = path.basename(filePath, '.jsonl');
|
|
152
|
+
const grandparentDir = path.dirname(path.dirname(filePath));
|
|
153
|
+
const workerLogPath = path.join(grandparentDir, 'worker.log');
|
|
154
|
+
const projectPath = await this.extractProjectPathFromWorkerLog(workerLogPath);
|
|
155
|
+
|
|
156
|
+
if (!projectPath) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return extractFirstValidJsonlData(filePath, (rawData) => {
|
|
161
|
+
const data = rawData as Record<string, any>;
|
|
162
|
+
if (data.role !== 'user') {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const text = typeof data.message?.content?.[0]?.text === 'string' ? data.message.content[0].text : '';
|
|
167
|
+
const firstLine = text.replace(/<\/?user_query>/g, '').trim().split('\n')[0];
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
sessionId,
|
|
171
|
+
projectPath,
|
|
172
|
+
sessionName: normalizeSessionName(firstLine, 'Untitled Cursor Session'),
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
|
|
2
2
|
import { CursorProviderAuth } from '@/modules/providers/list/cursor/cursor-auth.provider.js';
|
|
3
3
|
import { CursorMcpProvider } from '@/modules/providers/list/cursor/cursor-mcp.provider.js';
|
|
4
|
+
import { CursorSessionSynchronizer } from '@/modules/providers/list/cursor/cursor-session-synchronizer.provider.js';
|
|
4
5
|
import { CursorSessionsProvider } from '@/modules/providers/list/cursor/cursor-sessions.provider.js';
|
|
5
|
-
import type { IProviderAuth, IProviderSessions } from '@/shared/interfaces.js';
|
|
6
|
+
import type { IProviderAuth, IProviderSessionSynchronizer, IProviderSessions } from '@/shared/interfaces.js';
|
|
6
7
|
|
|
7
8
|
export class CursorProvider extends AbstractProvider {
|
|
8
9
|
readonly mcp = new CursorMcpProvider();
|
|
9
10
|
readonly auth: IProviderAuth = new CursorProviderAuth();
|
|
10
11
|
readonly sessions: IProviderSessions = new CursorSessionsProvider();
|
|
12
|
+
readonly sessionSynchronizer: IProviderSessionSynchronizer = new CursorSessionSynchronizer();
|
|
11
13
|
|
|
12
14
|
constructor() {
|
|
13
15
|
super('cursor');
|