@cloudcli-ai/cloudcli 1.31.5 → 1.33.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.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.tr.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/api-docs.html +6 -7
- package/dist/assets/index-MOIvpy53.css +32 -0
- package/dist/assets/{index-DqMVUeZS.js → index-skXSjBje.js} +290 -270
- package/dist/index.html +2 -2
- package/dist/modelConstants.js +841 -0
- package/dist-server/server/claude-sdk.js +67 -39
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +6 -9
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +163 -35
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/gemini-response-handler.js +34 -0
- package/dist-server/server/gemini-response-handler.js.map +1 -1
- package/dist-server/server/index.js +131 -19
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/database/index.js +1 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +11 -0
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/projects.db.js +12 -0
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js +61 -0
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +49 -8
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +1 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/projects/projects.routes.js +12 -3
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js +14 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-management.service.js +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +47 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +1 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +27 -7
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +141 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +55 -2
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +153 -14
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +198 -0
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +4 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +84 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +54 -2
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +21 -6
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +50 -0
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +4 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +754 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +8 -30
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +187 -24
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +28 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +4 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +125 -2
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +27 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +24 -8
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +10 -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-skills.provider.js +33 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +4 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js +92 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js +181 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +267 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +115 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +410 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js +62 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +19 -0
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -0
- package/dist-server/server/modules/providers/provider.registry.js +2 -0
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +65 -3
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/mcp.service.js +1 -9
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +199 -0
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +129 -13
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +13 -4
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +81 -7
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/skills.service.js +11 -0
- package/dist-server/server/modules/providers/services/skills.service.js.map +1 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js +45 -0
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +73 -6
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +66 -0
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +262 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +270 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/skills.test.js +259 -0
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +12 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +4 -4
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/openai-codex.js +87 -29
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +287 -0
- package/dist-server/server/opencode-cli.js.map +1 -0
- package/dist-server/server/opencode-cli.test.js +84 -0
- package/dist-server/server/opencode-cli.test.js.map +1 -0
- package/dist-server/server/routes/agent.js +21 -8
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/commands.js +204 -211
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/cursor.js +2 -2
- package/dist-server/server/routes/cursor.js.map +1 -1
- package/dist-server/server/routes/settings.js +0 -10
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/tests/commands.test.js +76 -0
- package/dist-server/server/routes/tests/commands.test.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.js +103 -0
- package/dist-server/server/shared/claude-cli-path.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.test.js +45 -0
- package/dist-server/server/shared/claude-cli-path.test.js.map +1 -0
- package/dist-server/server/{utils → shared}/frontmatter.js +3 -3
- package/dist-server/server/shared/frontmatter.js.map +1 -0
- package/dist-server/server/shared/utils.js +368 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/dist-server/server/utils/commandParser.js +3 -3
- package/dist-server/server/utils/commandParser.js.map +1 -1
- package/package.json +3 -1
- package/public/api-docs.html +878 -0
- package/public/modelConstants.js +841 -0
- package/server/claude-sdk.js +74 -40
- package/server/cursor-cli.js +6 -11
- package/server/gemini-cli.js +197 -43
- package/server/gemini-response-handler.js +38 -0
- package/server/index.js +144 -19
- package/server/modules/database/index.ts +1 -0
- package/server/modules/database/migrations.ts +12 -0
- package/server/modules/database/repositories/projects.db.ts +13 -0
- package/server/modules/database/repositories/sessions.db.integration.test.ts +72 -0
- package/server/modules/database/repositories/sessions.db.ts +60 -9
- package/server/modules/database/schema.ts +1 -0
- package/server/modules/projects/projects.routes.ts +20 -3
- package/server/modules/projects/services/project-delete.service.ts +16 -1
- package/server/modules/projects/services/project-management.service.ts +2 -0
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +71 -1
- package/server/modules/providers/README.md +354 -0
- package/server/modules/providers/index.ts +2 -1
- package/server/modules/providers/list/claude/claude-auth.provider.ts +38 -13
- package/server/modules/providers/list/claude/claude-models.provider.ts +191 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +68 -2
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +175 -14
- package/server/modules/providers/list/claude/claude-skills.provider.ts +257 -0
- package/server/modules/providers/list/claude/claude.provider.ts +11 -1
- package/server/modules/providers/list/codex/codex-models.provider.ts +125 -0
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +62 -2
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +25 -6
- package/server/modules/providers/list/codex/codex-skills.provider.ts +60 -0
- package/server/modules/providers/list/codex/codex.provider.ts +11 -1
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +820 -0
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +17 -40
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +216 -26
- package/server/modules/providers/list/cursor/cursor-skills.provider.ts +31 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +11 -1
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +158 -2
- package/server/modules/providers/list/gemini/gemini-models.provider.ts +42 -0
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +24 -20
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +10 -11
- package/server/modules/providers/list/gemini/gemini-skills.provider.ts +36 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +11 -1
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +111 -0
- package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +228 -0
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +339 -0
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +157 -0
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +506 -0
- package/server/modules/providers/list/opencode/opencode-skills.provider.ts +78 -0
- package/server/modules/providers/list/opencode/opencode.provider.ts +27 -0
- package/server/modules/providers/provider.registry.ts +2 -0
- package/server/modules/providers/provider.routes.ts +97 -4
- package/server/modules/providers/services/mcp.service.ts +1 -12
- package/server/modules/providers/services/provider-models.service.ts +325 -0
- package/server/modules/providers/services/session-conversations-search.service.ts +162 -13
- package/server/modules/providers/services/session-synchronizer.service.ts +1 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +14 -4
- package/server/modules/providers/services/sessions.service.ts +112 -9
- package/server/modules/providers/services/skills.service.ts +15 -0
- package/server/modules/providers/shared/base/abstract.provider.ts +4 -0
- package/server/modules/providers/shared/skills/skills.provider.ts +64 -0
- package/server/modules/providers/tests/mcp.test.ts +93 -6
- package/server/modules/providers/tests/opencode-models.test.ts +73 -0
- package/server/modules/providers/tests/opencode-sessions.test.ts +334 -0
- package/server/modules/providers/tests/provider-models.service.test.ts +318 -0
- package/server/modules/providers/tests/skills.test.ts +512 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +15 -1
- package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +4 -4
- package/server/modules/websocket/services/shell-websocket.service.ts +9 -0
- package/server/openai-codex.js +99 -30
- package/server/opencode-cli.js +336 -0
- package/server/opencode-cli.test.js +95 -0
- package/server/routes/agent.js +22 -8
- package/server/routes/commands.js +256 -233
- package/server/routes/cursor.js +2 -2
- package/server/routes/settings.js +1 -10
- package/server/routes/tests/commands.test.js +82 -0
- package/server/shared/claude-cli-path.test.ts +61 -0
- package/server/shared/claude-cli-path.ts +139 -0
- package/server/{utils/frontmatter.js → shared/frontmatter.ts} +3 -3
- package/server/shared/interfaces.ts +64 -0
- package/server/shared/types.ts +166 -1
- package/server/shared/utils.ts +478 -0
- package/server/utils/commandParser.js +5 -3
- package/dist/assets/index-6rYG_mRo.css +0 -32
- package/dist-server/server/utils/frontmatter.js.map +0 -1
- package/dist-server/shared/modelConstants.js +0 -98
- package/dist-server/shared/modelConstants.js.map +0 -1
- package/shared/modelConstants.js +0 -106
|
@@ -45,44 +45,28 @@ export class CursorSessionSynchronizer implements IProviderSessionSynchronizer {
|
|
|
45
45
|
*/
|
|
46
46
|
async synchronize(since?: Date): Promise<number> {
|
|
47
47
|
const projectsDir = path.join(this.cursorHome, 'projects');
|
|
48
|
-
const projectEntries = await listDirectoryEntriesSafe(projectsDir);
|
|
49
|
-
const seenProjectPaths = new Set<string>();
|
|
50
48
|
|
|
51
49
|
let processed = 0;
|
|
52
|
-
for (const entry of projectEntries) {
|
|
53
|
-
if (!entry.isDirectory()) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
const files = await findFilesRecursivelyCreatedAfter(projectsDir, '.jsonl', since ?? null);
|
|
52
|
+
|
|
53
|
+
for (const filePath of files) {
|
|
54
|
+
const parsed = await this.processSessionFile(filePath);
|
|
55
|
+
if (!parsed) {
|
|
60
56
|
continue;
|
|
61
57
|
}
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
}
|
|
59
|
+
const timestamps = await readFileTimestamps(filePath);
|
|
60
|
+
sessionsDb.createSession(
|
|
61
|
+
parsed.sessionId,
|
|
62
|
+
this.provider,
|
|
63
|
+
parsed.projectPath,
|
|
64
|
+
parsed.sessionName,
|
|
65
|
+
timestamps.createdAt,
|
|
66
|
+
timestamps.updatedAt,
|
|
67
|
+
filePath
|
|
68
|
+
);
|
|
69
|
+
processed += 1;
|
|
86
70
|
}
|
|
87
71
|
|
|
88
72
|
return processed;
|
|
@@ -113,13 +97,6 @@ export class CursorSessionSynchronizer implements IProviderSessionSynchronizer {
|
|
|
113
97
|
);
|
|
114
98
|
}
|
|
115
99
|
|
|
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
100
|
/**
|
|
124
101
|
* Extracts project path from Cursor worker.log.
|
|
125
102
|
*/
|
|
@@ -149,7 +126,7 @@ export class CursorSessionSynchronizer implements IProviderSessionSynchronizer {
|
|
|
149
126
|
*/
|
|
150
127
|
private async processSessionFile(filePath: string): Promise<ParsedSession | null> {
|
|
151
128
|
const sessionId = path.basename(filePath, '.jsonl');
|
|
152
|
-
const grandparentDir = path.dirname(path.dirname(filePath));
|
|
129
|
+
const grandparentDir = path.dirname(path.dirname(path.dirname(filePath)));
|
|
153
130
|
const workerLogPath = path.join(grandparentDir, 'worker.log');
|
|
154
131
|
const projectPath = await this.extractProjectPathFromWorkerLog(workerLogPath);
|
|
155
132
|
|
|
@@ -4,7 +4,12 @@ import path from 'node:path';
|
|
|
4
4
|
|
|
5
5
|
import type { IProviderSessions } from '@/shared/interfaces.js';
|
|
6
6
|
import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createNormalizedMessage,
|
|
9
|
+
generateMessageId,
|
|
10
|
+
readObjectRecord,
|
|
11
|
+
sanitizeLeafDirectoryName,
|
|
12
|
+
} from '@/shared/utils.js';
|
|
8
13
|
|
|
9
14
|
const PROVIDER = 'cursor';
|
|
10
15
|
|
|
@@ -25,19 +30,162 @@ type CursorMessageBlob = {
|
|
|
25
30
|
content: AnyRecord;
|
|
26
31
|
};
|
|
27
32
|
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
throw new Error('Cursor session id is required.');
|
|
33
|
+
function isInternalCursorText(value: unknown): boolean {
|
|
34
|
+
if (typeof value !== 'string') {
|
|
35
|
+
return false;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
) {
|
|
40
|
-
|
|
38
|
+
const normalized = value.trim();
|
|
39
|
+
return normalized.startsWith('<user_info>') || normalized.startsWith('<system_reminder>');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isInternalCursorPart(part: unknown): boolean {
|
|
43
|
+
if (!part || typeof part !== 'object') {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const record = part as AnyRecord;
|
|
48
|
+
const type = typeof record.type === 'string' ? record.type : '';
|
|
49
|
+
if (type === 'user_info' || type === 'system_reminder') {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return isInternalCursorText(record.text);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function unwrapUserQueryText(value: string, role: 'user' | 'assistant'): string {
|
|
57
|
+
if (role !== 'user') {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const normalized = value.trimStart();
|
|
62
|
+
const openTag = '<user_query>';
|
|
63
|
+
const closeTag = '</user_query>';
|
|
64
|
+
if (!normalized.startsWith(openTag)) {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const afterOpen = normalized.slice(openTag.length);
|
|
69
|
+
const closeIndex = afterOpen.lastIndexOf(closeTag);
|
|
70
|
+
const inner = closeIndex >= 0 ? afterOpen.slice(0, closeIndex) : afterOpen;
|
|
71
|
+
return inner.trim();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeToolId(value: unknown): string | null {
|
|
75
|
+
if (typeof value !== 'string') {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const normalized = value.trim();
|
|
80
|
+
return normalized ? normalized : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function extractCursorToolResultContent(item: AnyRecord): string {
|
|
84
|
+
if (typeof item.result === 'string' && item.result.trim()) {
|
|
85
|
+
return item.result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeof item.output === 'string' && item.output.trim()) {
|
|
89
|
+
return item.output;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (Array.isArray(item.experimental_content)) {
|
|
93
|
+
const experimentalText = item.experimental_content
|
|
94
|
+
.map((part: unknown) => {
|
|
95
|
+
if (typeof part === 'string') {
|
|
96
|
+
return part;
|
|
97
|
+
}
|
|
98
|
+
if (part && typeof part === 'object') {
|
|
99
|
+
const record = part as AnyRecord;
|
|
100
|
+
if (typeof record.text === 'string') {
|
|
101
|
+
return record.text;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return '';
|
|
105
|
+
})
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
.join('\n');
|
|
108
|
+
|
|
109
|
+
if (experimentalText.trim()) {
|
|
110
|
+
return experimentalText;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return typeof item.result === 'string' ? item.result : '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function parseCursorToolInput(rawInput: unknown): unknown {
|
|
118
|
+
if (typeof rawInput !== 'string') {
|
|
119
|
+
return rawInput;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const trimmed = rawInput.trim();
|
|
123
|
+
if (!trimmed) {
|
|
124
|
+
return rawInput;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
return JSON.parse(trimmed);
|
|
129
|
+
} catch {
|
|
130
|
+
return rawInput;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function normalizeCursorToolInput(toolName: string, rawInput: unknown): unknown {
|
|
135
|
+
const parsed = parseCursorToolInput(rawInput);
|
|
136
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
137
|
+
return parsed;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const input = parsed as AnyRecord;
|
|
141
|
+
const normalized: AnyRecord = { ...input };
|
|
142
|
+
|
|
143
|
+
const filePath = input.file_path
|
|
144
|
+
?? input.filePath
|
|
145
|
+
?? input.path
|
|
146
|
+
?? input.file
|
|
147
|
+
?? input.filename;
|
|
148
|
+
if (typeof filePath === 'string' && filePath.trim()) {
|
|
149
|
+
normalized.file_path = filePath;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (toolName === 'Write') {
|
|
153
|
+
const content = input.content
|
|
154
|
+
?? input.text
|
|
155
|
+
?? input.value
|
|
156
|
+
?? input.contents
|
|
157
|
+
?? input.fileContent
|
|
158
|
+
?? input.new_string
|
|
159
|
+
?? input.newString;
|
|
160
|
+
if (typeof content === 'string') {
|
|
161
|
+
normalized.content = content;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (toolName === 'Edit') {
|
|
166
|
+
const oldString = input.old_string
|
|
167
|
+
?? input.oldString
|
|
168
|
+
?? input.old
|
|
169
|
+
?? '';
|
|
170
|
+
const newString = input.new_string
|
|
171
|
+
?? input.newString
|
|
172
|
+
?? input.new
|
|
173
|
+
?? input.content
|
|
174
|
+
?? '';
|
|
175
|
+
|
|
176
|
+
if (typeof oldString === 'string') {
|
|
177
|
+
normalized.old_string = oldString;
|
|
178
|
+
}
|
|
179
|
+
if (typeof newString === 'string') {
|
|
180
|
+
normalized.new_string = newString;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (toolName === 'ApplyPatch') {
|
|
185
|
+
const patch = input.patch ?? input.diff ?? input.content;
|
|
186
|
+
if (typeof patch === 'string' && !normalized.patch) {
|
|
187
|
+
normalized.patch = patch;
|
|
188
|
+
}
|
|
41
189
|
}
|
|
42
190
|
|
|
43
191
|
return normalized;
|
|
@@ -53,7 +201,7 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
53
201
|
const { default: Database } = await import('better-sqlite3');
|
|
54
202
|
|
|
55
203
|
const cwdId = crypto.createHash('md5').update(projectPath || process.cwd()).digest('hex');
|
|
56
|
-
const safeSessionId =
|
|
204
|
+
const safeSessionId = sanitizeLeafDirectoryName(sessionId, 'cursor session id');
|
|
57
205
|
const baseChatsPath = path.join(os.homedir(), '.cursor', 'chats', cwdId);
|
|
58
206
|
const storeDbPath = path.join(baseChatsPath, safeSessionId, 'store.db');
|
|
59
207
|
const resolvedBaseChatsPath = path.resolve(baseChatsPath);
|
|
@@ -225,13 +373,14 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
225
373
|
try {
|
|
226
374
|
const blobs = await this.loadCursorBlobs(sessionId, projectPath);
|
|
227
375
|
const allNormalized = this.normalizeCursorBlobs(blobs, sessionId);
|
|
228
|
-
const
|
|
376
|
+
const renderableMessages = allNormalized.filter((msg) => msg.kind !== 'tool_result');
|
|
377
|
+
const total = renderableMessages.length;
|
|
229
378
|
|
|
230
379
|
if (limit !== null) {
|
|
231
380
|
const start = offset;
|
|
232
381
|
const page = limit === 0
|
|
233
382
|
? []
|
|
234
|
-
:
|
|
383
|
+
: renderableMessages.slice(start, start + limit);
|
|
235
384
|
const hasMore = limit === 0
|
|
236
385
|
? start < total
|
|
237
386
|
: start + limit < total;
|
|
@@ -245,7 +394,7 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
245
394
|
}
|
|
246
395
|
|
|
247
396
|
return {
|
|
248
|
-
messages:
|
|
397
|
+
messages: renderableMessages,
|
|
249
398
|
total,
|
|
250
399
|
hasMore: false,
|
|
251
400
|
offset: 0,
|
|
@@ -283,11 +432,24 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
283
432
|
let text = '';
|
|
284
433
|
if (Array.isArray(content.message.content)) {
|
|
285
434
|
text = content.message.content
|
|
286
|
-
.map((part: string | AnyRecord) =>
|
|
435
|
+
.map((part: string | AnyRecord) => {
|
|
436
|
+
if (typeof part === 'string') {
|
|
437
|
+
if (isInternalCursorText(part)) {
|
|
438
|
+
return '';
|
|
439
|
+
}
|
|
440
|
+
return unwrapUserQueryText(part, role);
|
|
441
|
+
}
|
|
442
|
+
if (isInternalCursorPart(part)) {
|
|
443
|
+
return '';
|
|
444
|
+
}
|
|
445
|
+
return unwrapUserQueryText(part?.text || '', role);
|
|
446
|
+
})
|
|
287
447
|
.filter(Boolean)
|
|
288
448
|
.join('\n');
|
|
289
449
|
} else if (typeof content.message.content === 'string') {
|
|
290
|
-
|
|
450
|
+
if (!isInternalCursorText(content.message.content)) {
|
|
451
|
+
text = unwrapUserQueryText(content.message.content, role);
|
|
452
|
+
}
|
|
291
453
|
}
|
|
292
454
|
if (text?.trim()) {
|
|
293
455
|
messages.push(createNormalizedMessage({
|
|
@@ -316,7 +478,14 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
316
478
|
if (item?.type !== 'tool-result') {
|
|
317
479
|
continue;
|
|
318
480
|
}
|
|
319
|
-
const
|
|
481
|
+
const cursorOptions = content.providerOptions?.cursor as AnyRecord | undefined;
|
|
482
|
+
const highLevelToolCallResult = cursorOptions?.highLevelToolCallResult;
|
|
483
|
+
const toolCallId = normalizeToolId(item.toolCallId)
|
|
484
|
+
|| normalizeToolId(item.tool_call_id)
|
|
485
|
+
|| normalizeToolId(highLevelToolCallResult?.toolCallId)
|
|
486
|
+
|| normalizeToolId(highLevelToolCallResult?.tool_call_id)
|
|
487
|
+
|| normalizeToolId(content.id)
|
|
488
|
+
|| '';
|
|
320
489
|
messages.push(createNormalizedMessage({
|
|
321
490
|
id: `${baseId}_tr`,
|
|
322
491
|
sessionId,
|
|
@@ -324,8 +493,9 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
324
493
|
provider: PROVIDER,
|
|
325
494
|
kind: 'tool_result',
|
|
326
495
|
toolId: toolCallId,
|
|
327
|
-
content: item
|
|
328
|
-
isError:
|
|
496
|
+
content: extractCursorToolResultContent(item),
|
|
497
|
+
isError: Boolean(item.isError || item.is_error),
|
|
498
|
+
toolUseResult: highLevelToolCallResult,
|
|
329
499
|
}));
|
|
330
500
|
}
|
|
331
501
|
continue;
|
|
@@ -336,8 +506,15 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
336
506
|
if (Array.isArray(content.content)) {
|
|
337
507
|
for (let partIdx = 0; partIdx < content.content.length; partIdx++) {
|
|
338
508
|
const part = content.content[partIdx];
|
|
509
|
+
if (isInternalCursorPart(part)) {
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
339
512
|
|
|
340
513
|
if (part?.type === 'text' && part?.text) {
|
|
514
|
+
const normalizedPartText = unwrapUserQueryText(part.text, role);
|
|
515
|
+
if (!normalizedPartText) {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
341
518
|
messages.push(createNormalizedMessage({
|
|
342
519
|
id: `${baseId}_${partIdx}`,
|
|
343
520
|
sessionId,
|
|
@@ -345,7 +522,7 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
345
522
|
provider: PROVIDER,
|
|
346
523
|
kind: 'text',
|
|
347
524
|
role,
|
|
348
|
-
content:
|
|
525
|
+
content: normalizedPartText,
|
|
349
526
|
sequence: blob.sequence,
|
|
350
527
|
rowid: blob.rowid,
|
|
351
528
|
}));
|
|
@@ -361,7 +538,11 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
361
538
|
} else if (part?.type === 'tool-call' || part?.type === 'tool_use') {
|
|
362
539
|
const rawToolName = part.toolName || part.name || 'Unknown Tool';
|
|
363
540
|
const toolName = rawToolName === 'ApplyPatch' ? 'Edit' : rawToolName;
|
|
364
|
-
const toolId = part.toolCallId
|
|
541
|
+
const toolId = normalizeToolId(part.toolCallId)
|
|
542
|
+
|| normalizeToolId(part.tool_call_id)
|
|
543
|
+
|| normalizeToolId(part.id)
|
|
544
|
+
|| `tool_${i}_${partIdx}`;
|
|
545
|
+
const normalizedToolInput = normalizeCursorToolInput(rawToolName, part.args ?? part.input);
|
|
365
546
|
const message = createNormalizedMessage({
|
|
366
547
|
id: `${baseId}_${partIdx}`,
|
|
367
548
|
sessionId,
|
|
@@ -369,14 +550,22 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
369
550
|
provider: PROVIDER,
|
|
370
551
|
kind: 'tool_use',
|
|
371
552
|
toolName,
|
|
372
|
-
toolInput:
|
|
553
|
+
toolInput: normalizedToolInput,
|
|
373
554
|
toolId,
|
|
374
555
|
});
|
|
375
556
|
messages.push(message);
|
|
376
557
|
toolUseMap.set(toolId, message);
|
|
377
558
|
}
|
|
378
559
|
}
|
|
379
|
-
} else if (
|
|
560
|
+
} else if (
|
|
561
|
+
typeof content.content === 'string'
|
|
562
|
+
&& content.content.trim()
|
|
563
|
+
&& !isInternalCursorText(content.content)
|
|
564
|
+
) {
|
|
565
|
+
const normalizedText = unwrapUserQueryText(content.content, role);
|
|
566
|
+
if (!normalizedText) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
380
569
|
messages.push(createNormalizedMessage({
|
|
381
570
|
id: baseId,
|
|
382
571
|
sessionId,
|
|
@@ -384,7 +573,7 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
384
573
|
provider: PROVIDER,
|
|
385
574
|
kind: 'text',
|
|
386
575
|
role,
|
|
387
|
-
content:
|
|
576
|
+
content: normalizedText,
|
|
388
577
|
sequence: blob.sequence,
|
|
389
578
|
rowid: blob.rowid,
|
|
390
579
|
}));
|
|
@@ -401,6 +590,7 @@ export class CursorSessionsProvider implements IProviderSessions {
|
|
|
401
590
|
toolUse.toolResult = {
|
|
402
591
|
content: msg.content,
|
|
403
592
|
isError: msg.isError,
|
|
593
|
+
toolUseResult: msg.toolUseResult,
|
|
404
594
|
};
|
|
405
595
|
}
|
|
406
596
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { SkillsProvider } from '@/modules/providers/shared/skills/skills.provider.js';
|
|
5
|
+
import type { ProviderSkillSource } from '@/shared/types.js';
|
|
6
|
+
|
|
7
|
+
export class CursorSkillsProvider extends SkillsProvider {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('cursor');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected async getSkillSources(workspacePath: string): Promise<ProviderSkillSource[]> {
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
scope: 'project',
|
|
16
|
+
rootDir: path.join(workspacePath, '.agents', 'skills'),
|
|
17
|
+
commandPrefix: '/',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
scope: 'project',
|
|
21
|
+
rootDir: path.join(workspacePath, '.cursor', 'skills'),
|
|
22
|
+
commandPrefix: '/',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
scope: 'user',
|
|
26
|
+
rootDir: path.join(os.homedir(), '.cursor', 'skills'),
|
|
27
|
+
commandPrefix: '/',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,13 +1,23 @@
|
|
|
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
|
+
import { CursorProviderModels } from '@/modules/providers/list/cursor/cursor-models.provider.js';
|
|
3
4
|
import { CursorMcpProvider } from '@/modules/providers/list/cursor/cursor-mcp.provider.js';
|
|
4
5
|
import { CursorSessionSynchronizer } from '@/modules/providers/list/cursor/cursor-session-synchronizer.provider.js';
|
|
5
6
|
import { CursorSessionsProvider } from '@/modules/providers/list/cursor/cursor-sessions.provider.js';
|
|
6
|
-
import
|
|
7
|
+
import { CursorSkillsProvider } from '@/modules/providers/list/cursor/cursor-skills.provider.js';
|
|
8
|
+
import type {
|
|
9
|
+
IProviderAuth,
|
|
10
|
+
IProviderModels,
|
|
11
|
+
IProviderSessionSynchronizer,
|
|
12
|
+
IProviderSkills,
|
|
13
|
+
IProviderSessions,
|
|
14
|
+
} from '@/shared/interfaces.js';
|
|
7
15
|
|
|
8
16
|
export class CursorProvider extends AbstractProvider {
|
|
17
|
+
readonly models: IProviderModels = new CursorProviderModels();
|
|
9
18
|
readonly mcp = new CursorMcpProvider();
|
|
10
19
|
readonly auth: IProviderAuth = new CursorProviderAuth();
|
|
20
|
+
readonly skills: IProviderSkills = new CursorSkillsProvider();
|
|
11
21
|
readonly sessions: IProviderSessions = new CursorSessionsProvider();
|
|
12
22
|
readonly sessionSynchronizer: IProviderSessionSynchronizer = new CursorSessionSynchronizer();
|
|
13
23
|
|
|
@@ -15,7 +15,24 @@ type GeminiCredentialsStatus = {
|
|
|
15
15
|
error?: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
type GeminiAuthType =
|
|
19
|
+
| 'oauth-personal'
|
|
20
|
+
| 'gemini-api-key'
|
|
21
|
+
| 'vertex-ai'
|
|
22
|
+
| 'compute-default-credentials'
|
|
23
|
+
| 'gateway'
|
|
24
|
+
| 'cloud-shell'
|
|
25
|
+
| null;
|
|
26
|
+
|
|
18
27
|
export class GeminiProviderAuth implements IProviderAuth {
|
|
28
|
+
/**
|
|
29
|
+
* Gemini CLI can override its home root via GEMINI_CLI_HOME.
|
|
30
|
+
* Use the same resolution so status checks match runtime behavior.
|
|
31
|
+
*/
|
|
32
|
+
private getGeminiCliHome(): string {
|
|
33
|
+
return process.env.GEMINI_CLI_HOME?.trim() || os.homedir();
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
/**
|
|
20
37
|
* Checks whether the Gemini CLI is available on this host.
|
|
21
38
|
*/
|
|
@@ -58,6 +75,88 @@ export class GeminiProviderAuth implements IProviderAuth {
|
|
|
58
75
|
};
|
|
59
76
|
}
|
|
60
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Parses dotenv-style key/value pairs.
|
|
80
|
+
*/
|
|
81
|
+
private parseEnvFile(content: string): Record<string, string> {
|
|
82
|
+
const parsed: Record<string, string> = {};
|
|
83
|
+
|
|
84
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
85
|
+
const line = rawLine.trim();
|
|
86
|
+
if (!line || line.startsWith('#')) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const normalizedLine = line.startsWith('export ')
|
|
91
|
+
? line.slice('export '.length).trim()
|
|
92
|
+
: line;
|
|
93
|
+
const separatorIndex = normalizedLine.indexOf('=');
|
|
94
|
+
if (separatorIndex <= 0) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const key = normalizedLine.slice(0, separatorIndex).trim();
|
|
99
|
+
if (!key) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let value = normalizedLine.slice(separatorIndex + 1).trim();
|
|
104
|
+
const quoted = (value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\''));
|
|
105
|
+
if (quoted) {
|
|
106
|
+
value = value.slice(1, -1);
|
|
107
|
+
} else {
|
|
108
|
+
value = value.replace(/\s+#.*$/, '').trim();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
parsed[key] = value;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return parsed;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Loads user-level auth env in Gemini's "first file found" order.
|
|
119
|
+
*/
|
|
120
|
+
private async loadUserLevelAuthEnv(): Promise<Record<string, string>> {
|
|
121
|
+
const geminiCliHome = this.getGeminiCliHome();
|
|
122
|
+
const envCandidates = [
|
|
123
|
+
path.join(geminiCliHome, '.gemini', '.env'),
|
|
124
|
+
path.join(geminiCliHome, '.env'),
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
for (const envPath of envCandidates) {
|
|
128
|
+
try {
|
|
129
|
+
const content = await readFile(envPath, 'utf8');
|
|
130
|
+
return this.parseEnvFile(content);
|
|
131
|
+
} catch {
|
|
132
|
+
// Continue to the next fallback.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Reads Gemini's selected auth type from settings.json when available.
|
|
141
|
+
*/
|
|
142
|
+
private async readSelectedAuthType(): Promise<GeminiAuthType> {
|
|
143
|
+
try {
|
|
144
|
+
const settingsPath = path.join(this.getGeminiCliHome(), '.gemini', 'settings.json');
|
|
145
|
+
const content = await readFile(settingsPath, 'utf8');
|
|
146
|
+
const settings = readObjectRecord(JSON.parse(content));
|
|
147
|
+
const security = readObjectRecord(settings?.security);
|
|
148
|
+
const auth = readObjectRecord(security?.auth);
|
|
149
|
+
const selectedType = readOptionalString(auth?.selectedType);
|
|
150
|
+
if (!selectedType) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return selectedType as GeminiAuthType;
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
61
160
|
/**
|
|
62
161
|
* Checks Gemini credentials from API key env vars or local OAuth credential files.
|
|
63
162
|
*/
|
|
@@ -66,8 +165,46 @@ export class GeminiProviderAuth implements IProviderAuth {
|
|
|
66
165
|
return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
|
|
67
166
|
}
|
|
68
167
|
|
|
168
|
+
const userEnv = await this.loadUserLevelAuthEnv();
|
|
169
|
+
if (readOptionalString(userEnv.GEMINI_API_KEY)) {
|
|
170
|
+
return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const selectedType = await this.readSelectedAuthType();
|
|
174
|
+
if (selectedType === 'vertex-ai') {
|
|
175
|
+
const hasGoogleApiKey = Boolean(
|
|
176
|
+
process.env.GOOGLE_API_KEY?.trim()
|
|
177
|
+
|| readOptionalString(userEnv.GOOGLE_API_KEY)
|
|
178
|
+
);
|
|
179
|
+
const hasProject = Boolean(
|
|
180
|
+
process.env.GOOGLE_CLOUD_PROJECT?.trim()
|
|
181
|
+
|| process.env.GOOGLE_CLOUD_PROJECT_ID?.trim()
|
|
182
|
+
|| readOptionalString(userEnv.GOOGLE_CLOUD_PROJECT)
|
|
183
|
+
|| readOptionalString(userEnv.GOOGLE_CLOUD_PROJECT_ID)
|
|
184
|
+
);
|
|
185
|
+
const hasLocation = Boolean(
|
|
186
|
+
process.env.GOOGLE_CLOUD_LOCATION?.trim()
|
|
187
|
+
|| readOptionalString(userEnv.GOOGLE_CLOUD_LOCATION)
|
|
188
|
+
);
|
|
189
|
+
const hasServiceAccount = Boolean(
|
|
190
|
+
process.env.GOOGLE_APPLICATION_CREDENTIALS?.trim()
|
|
191
|
+
|| readOptionalString(userEnv.GOOGLE_APPLICATION_CREDENTIALS)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
if (hasGoogleApiKey || hasServiceAccount || (hasProject && hasLocation)) {
|
|
195
|
+
return { authenticated: true, email: 'Vertex AI Auth', method: 'vertex_ai' };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
authenticated: false,
|
|
200
|
+
email: null,
|
|
201
|
+
method: 'vertex_ai',
|
|
202
|
+
error: 'Gemini is set to Vertex AI, but required env vars are missing',
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
69
206
|
try {
|
|
70
|
-
const credsPath = path.join(
|
|
207
|
+
const credsPath = path.join(this.getGeminiCliHome(), '.gemini', 'oauth_creds.json');
|
|
71
208
|
const content = await readFile(credsPath, 'utf8');
|
|
72
209
|
const creds = readObjectRecord(JSON.parse(content)) ?? {};
|
|
73
210
|
const accessToken = readOptionalString(creds.access_token);
|
|
@@ -106,6 +243,25 @@ export class GeminiProviderAuth implements IProviderAuth {
|
|
|
106
243
|
method: 'credentials_file',
|
|
107
244
|
};
|
|
108
245
|
} catch {
|
|
246
|
+
if (selectedType === 'gemini-api-key') {
|
|
247
|
+
return {
|
|
248
|
+
authenticated: false,
|
|
249
|
+
email: null,
|
|
250
|
+
method: 'api_key',
|
|
251
|
+
error: 'Gemini is set to "Use Gemini API key", but GEMINI_API_KEY is unavailable',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (selectedType === 'oauth-personal') {
|
|
256
|
+
return {
|
|
257
|
+
authenticated: false,
|
|
258
|
+
email: null,
|
|
259
|
+
method: 'credentials_file',
|
|
260
|
+
error: 'Gemini is set to Google sign-in, but no cached OAuth credentials were found',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// If no explicit auth type was selected, surface the generic "not configured" error.
|
|
109
265
|
return {
|
|
110
266
|
authenticated: false,
|
|
111
267
|
email: null,
|
|
@@ -140,7 +296,7 @@ export class GeminiProviderAuth implements IProviderAuth {
|
|
|
140
296
|
*/
|
|
141
297
|
private async getActiveAccountEmail(): Promise<string | null> {
|
|
142
298
|
try {
|
|
143
|
-
const accPath = path.join(
|
|
299
|
+
const accPath = path.join(this.getGeminiCliHome(), '.gemini', 'google_accounts.json');
|
|
144
300
|
const accContent = await readFile(accPath, 'utf8');
|
|
145
301
|
const accounts = readObjectRecord(JSON.parse(accContent));
|
|
146
302
|
return readOptionalString(accounts?.active) ?? null;
|