@cloudcli-ai/cloudcli 1.36.0 → 1.36.2
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 +17 -18
- package/README.ja.md +16 -17
- package/README.ko.md +17 -18
- package/README.md +19 -20
- package/README.ru.md +17 -18
- package/README.tr.md +17 -18
- package/README.zh-CN.md +17 -18
- package/README.zh-TW.md +17 -18
- package/dist/api-docs.html +2 -3
- package/dist/assets/{index-S6cIy7hj.js → index-CDGzV0LN.js} +264 -259
- package/dist/assets/index-DLmhQ15L.css +32 -0
- package/dist/assets/{vendor-xterm-CJZjLICi.js → vendor-xterm-CS4rQ5Mr.js} +12 -12
- package/dist/index.html +3 -3
- package/dist-server/server/claude-sdk.js +37 -90
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cli.js +1 -4
- package/dist-server/server/cli.js.map +1 -1
- package/dist-server/server/cursor-cli.js +12 -11
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/index.js +10 -141
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/assets/assets.routes.js +89 -0
- package/dist-server/server/modules/assets/assets.routes.js.map +1 -0
- package/dist-server/server/modules/assets/index.js +4 -0
- package/dist-server/server/modules/assets/index.js.map +1 -0
- package/dist-server/server/modules/assets/services/image-assets.service.js +57 -0
- package/dist-server/server/modules/assets/services/image-assets.service.js.map +1 -0
- package/dist-server/server/modules/assets/tests/image-assets.service.test.js +36 -0
- package/dist-server/server/modules/assets/tests/image-assets.service.test.js.map +1 -0
- package/dist-server/server/modules/browser-use/browser-use.service.js +6 -3
- package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -2
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +0 -1
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-clone.service.js +2 -1
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +28 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +54 -1
- 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 +36 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +3 -17
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +7 -1
- 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 +49 -13
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js +0 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +8 -14
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +24 -5
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +10 -21
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.registry.js +0 -2
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +0 -1
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +7 -14
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +3 -77
- 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 +0 -1
- 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 +0 -13
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +96 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +3 -29
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +7 -0
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +117 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-image-history.test.js +147 -0
- package/dist-server/server/modules/providers/tests/provider-image-history.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +8 -8
- package/dist-server/server/modules/providers/tests/skills.test.js +3 -26
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +14 -0
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +36 -2
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +3 -12
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js +33 -0
- package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +37 -4
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
- package/dist-server/server/openai-codex.js +8 -2
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +43 -6
- package/dist-server/server/opencode-cli.js.map +1 -1
- package/dist-server/server/opencode-cli.test.js +103 -3
- package/dist-server/server/opencode-cli.test.js.map +1 -1
- package/dist-server/server/routes/agent.js +9 -19
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/commands.js +1 -2
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/git.js +179 -53
- package/dist-server/server/routes/git.js.map +1 -1
- package/dist-server/server/routes/git.test.js +80 -0
- package/dist-server/server/routes/git.test.js.map +1 -0
- package/dist-server/server/routes/taskmaster.js +3 -1
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/dist-server/server/routes/user.js +2 -1
- package/dist-server/server/routes/user.js.map +1 -1
- package/dist-server/server/shared/image-attachments.js +270 -0
- package/dist-server/server/shared/image-attachments.js.map +1 -0
- package/dist-server/server/shared/tests/image-attachments.test.js +220 -0
- package/dist-server/server/shared/tests/image-attachments.test.js.map +1 -0
- package/dist-server/server/shared/utils.js +43 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/dist-server/server/utils/gitConfig.js +2 -1
- package/dist-server/server/utils/gitConfig.js.map +1 -1
- package/dist-server/server/utils/plugin-process-manager.js +2 -1
- package/dist-server/server/utils/plugin-process-manager.js.map +1 -1
- package/electron/launcher/launcher.js +1 -1
- package/package.json +4 -4
- package/public/api-docs.html +2 -3
- package/server/claude-sdk.js +37 -105
- package/server/cli.js +1 -4
- package/server/cursor-cli.js +13 -12
- package/server/index.js +11 -159
- package/server/modules/assets/assets.routes.ts +103 -0
- package/server/modules/assets/index.ts +3 -0
- package/server/modules/assets/services/image-assets.service.ts +82 -0
- package/server/modules/assets/tests/image-assets.service.test.ts +46 -0
- package/server/modules/browser-use/browser-use.service.ts +7 -3
- package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -2
- package/server/modules/notifications/services/notification-orchestrator.service.js +0 -1
- package/server/modules/projects/services/project-clone.service.ts +3 -1
- package/server/modules/providers/README.md +1 -7
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +30 -0
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +60 -1
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +41 -1
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +3 -17
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +7 -1
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +56 -15
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +0 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +15 -19
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +25 -4
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +10 -21
- package/server/modules/providers/provider.registry.ts +0 -2
- package/server/modules/providers/provider.routes.ts +0 -1
- package/server/modules/providers/services/provider-capabilities.service.ts +7 -14
- package/server/modules/providers/services/provider-models.service.ts +1 -1
- package/server/modules/providers/services/session-conversations-search.service.ts +3 -93
- package/server/modules/providers/services/session-synchronizer.service.ts +0 -1
- package/server/modules/providers/services/sessions-watcher.service.ts +0 -14
- package/server/modules/providers/tests/codex-sessions.test.ts +111 -0
- package/server/modules/providers/tests/mcp.test.ts +3 -34
- package/server/modules/providers/tests/opencode-models.test.ts +7 -0
- package/server/modules/providers/tests/opencode-sessions.test.ts +139 -0
- package/server/modules/providers/tests/provider-image-history.test.ts +180 -0
- package/server/modules/providers/tests/provider-models.service.test.ts +8 -8
- package/server/modules/providers/tests/skills.test.ts +3 -42
- package/server/modules/websocket/services/chat-run-registry.service.ts +16 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +41 -2
- package/server/modules/websocket/services/shell-websocket.service.ts +1 -11
- package/server/modules/websocket/tests/chat-image-filter.test.ts +44 -0
- package/server/modules/websocket/tests/chat-run-registry.test.ts +42 -4
- package/server/openai-codex.js +9 -1
- package/server/opencode-cli.js +44 -6
- package/server/opencode-cli.test.js +112 -3
- package/server/routes/agent.js +9 -19
- package/server/routes/commands.js +1 -2
- package/server/routes/git.js +201 -60
- package/server/routes/git.test.js +106 -0
- package/server/routes/taskmaster.js +3 -1
- package/server/routes/user.js +2 -1
- package/server/shared/image-attachments.ts +341 -0
- package/server/shared/tests/image-attachments.test.ts +298 -0
- package/server/shared/types.ts +1 -1
- package/server/shared/utils.ts +45 -0
- package/server/utils/gitConfig.js +2 -1
- package/server/utils/plugin-process-manager.js +3 -1
- package/dist/assets/index-uLUE6Qt1.css +0 -32
- package/dist/icons/gemini-ai-icon.svg +0 -1
- package/dist-server/server/gemini-cli.js +0 -551
- package/dist-server/server/gemini-cli.js.map +0 -1
- package/dist-server/server/gemini-response-handler.js +0 -106
- package/dist-server/server/gemini-response-handler.js.map +0 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +0 -254
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +0 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +0 -82
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +0 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +0 -24
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +0 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +0 -312
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +0 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +0 -474
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +0 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +0 -40
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +0 -1
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +0 -19
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +0 -1
- package/dist-server/server/routes/gemini.js +0 -21
- package/dist-server/server/routes/gemini.js.map +0 -1
- package/dist-server/server/sessionManager.js +0 -194
- package/dist-server/server/sessionManager.js.map +0 -1
- package/server/gemini-cli.js +0 -638
- package/server/gemini-response-handler.js +0 -117
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +0 -307
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +0 -110
- package/server/modules/providers/list/gemini/gemini-models.provider.ts +0 -39
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +0 -405
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +0 -540
- package/server/modules/providers/list/gemini/gemini-skills.provider.ts +0 -44
- package/server/modules/providers/list/gemini/gemini.provider.ts +0 -27
- package/server/routes/gemini.js +0 -25
- package/server/sessionManager.js +0 -226
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// Gemini Response Handler - JSON Stream processing
|
|
2
|
-
import { sessionsService } from './modules/providers/services/sessions.service.js';
|
|
3
|
-
import { createNormalizedMessage } from './shared/utils.js';
|
|
4
|
-
|
|
5
|
-
function buildGeminiTokenBudget(tokens) {
|
|
6
|
-
if (!tokens || typeof tokens !== 'object') {
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const parsedInputTokens = Number(tokens.input);
|
|
11
|
-
const parsedOutputTokens = Number(tokens.output);
|
|
12
|
-
const inputTokens = Number.isFinite(parsedInputTokens) ? parsedInputTokens : 0;
|
|
13
|
-
const outputTokens = Number.isFinite(parsedOutputTokens) ? parsedOutputTokens : 0;
|
|
14
|
-
const parsedUsed = Number(tokens.total);
|
|
15
|
-
const used = Number.isFinite(parsedUsed) ? parsedUsed : inputTokens + outputTokens;
|
|
16
|
-
if (!Number.isFinite(used) || used <= 0) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
used,
|
|
22
|
-
inputTokens,
|
|
23
|
-
outputTokens,
|
|
24
|
-
breakdown: {
|
|
25
|
-
input: inputTokens,
|
|
26
|
-
output: outputTokens,
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
class GeminiResponseHandler {
|
|
32
|
-
constructor(ws, options = {}) {
|
|
33
|
-
this.ws = ws;
|
|
34
|
-
this.buffer = '';
|
|
35
|
-
this.onContentFragment = options.onContentFragment || null;
|
|
36
|
-
this.onInit = options.onInit || null;
|
|
37
|
-
this.onToolUse = options.onToolUse || null;
|
|
38
|
-
this.onToolResult = options.onToolResult || null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Process incoming raw data from Gemini stream-json
|
|
42
|
-
processData(data) {
|
|
43
|
-
this.buffer += data;
|
|
44
|
-
|
|
45
|
-
// Split by newline
|
|
46
|
-
const lines = this.buffer.split('\n');
|
|
47
|
-
|
|
48
|
-
// Keep the last incomplete line in the buffer
|
|
49
|
-
this.buffer = lines.pop() || '';
|
|
50
|
-
|
|
51
|
-
for (const line of lines) {
|
|
52
|
-
if (!line.trim()) continue;
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const event = JSON.parse(line);
|
|
56
|
-
this.handleEvent(event);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
// Not a JSON line, probably debug output or CLI warnings
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
handleEvent(event) {
|
|
64
|
-
const sid = typeof this.ws.getSessionId === 'function' ? this.ws.getSessionId() : null;
|
|
65
|
-
|
|
66
|
-
if (event.type === 'init') {
|
|
67
|
-
if (this.onInit) {
|
|
68
|
-
this.onInit(event);
|
|
69
|
-
}
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Invoke per-type callbacks for session tracking
|
|
74
|
-
if (event.type === 'message' && event.role === 'assistant') {
|
|
75
|
-
const content = event.content || '';
|
|
76
|
-
if (this.onContentFragment && content) {
|
|
77
|
-
this.onContentFragment(content);
|
|
78
|
-
}
|
|
79
|
-
} else if (event.type === 'tool_use' && this.onToolUse) {
|
|
80
|
-
this.onToolUse(event);
|
|
81
|
-
} else if (event.type === 'tool_result' && this.onToolResult) {
|
|
82
|
-
this.onToolResult(event);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Normalize via adapter and send all resulting messages
|
|
86
|
-
const normalized = sessionsService.normalizeMessage('gemini', event, sid);
|
|
87
|
-
for (const msg of normalized) {
|
|
88
|
-
this.ws.send(msg);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const tokenBudget = buildGeminiTokenBudget(event.tokens);
|
|
92
|
-
if (tokenBudget) {
|
|
93
|
-
this.ws.send(createNormalizedMessage({
|
|
94
|
-
kind: 'status',
|
|
95
|
-
text: 'token_budget',
|
|
96
|
-
tokenBudget,
|
|
97
|
-
sessionId: sid,
|
|
98
|
-
provider: 'gemini',
|
|
99
|
-
}));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
forceFlush() {
|
|
104
|
-
if (this.buffer.trim()) {
|
|
105
|
-
try {
|
|
106
|
-
const event = JSON.parse(this.buffer);
|
|
107
|
-
this.handleEvent(event);
|
|
108
|
-
} catch (err) { }
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
destroy() {
|
|
113
|
-
this.buffer = '';
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export default GeminiResponseHandler;
|
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
|
|
5
|
-
import spawn from 'cross-spawn';
|
|
6
|
-
|
|
7
|
-
import type { IProviderAuth } from '@/shared/interfaces.js';
|
|
8
|
-
import type { ProviderAuthStatus } from '@/shared/types.js';
|
|
9
|
-
import { readObjectRecord, readOptionalString } from '@/shared/utils.js';
|
|
10
|
-
|
|
11
|
-
type GeminiCredentialsStatus = {
|
|
12
|
-
authenticated: boolean;
|
|
13
|
-
email: string | null;
|
|
14
|
-
method: string | null;
|
|
15
|
-
error?: string;
|
|
16
|
-
};
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Checks whether the Gemini CLI is available on this host.
|
|
38
|
-
*/
|
|
39
|
-
private checkInstalled(): boolean {
|
|
40
|
-
const cliPath = process.env.GEMINI_PATH || 'gemini';
|
|
41
|
-
try {
|
|
42
|
-
spawn.sync(cliPath, ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
43
|
-
return true;
|
|
44
|
-
} catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Returns Gemini CLI installation and credential status.
|
|
51
|
-
*/
|
|
52
|
-
async getStatus(): Promise<ProviderAuthStatus> {
|
|
53
|
-
const installed = this.checkInstalled();
|
|
54
|
-
|
|
55
|
-
if (!installed) {
|
|
56
|
-
return {
|
|
57
|
-
installed,
|
|
58
|
-
provider: 'gemini',
|
|
59
|
-
authenticated: false,
|
|
60
|
-
email: null,
|
|
61
|
-
method: null,
|
|
62
|
-
error: 'Gemini CLI is not installed',
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const credentials = await this.checkCredentials();
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
installed,
|
|
70
|
-
provider: 'gemini',
|
|
71
|
-
authenticated: credentials.authenticated,
|
|
72
|
-
email: credentials.email,
|
|
73
|
-
method: credentials.method,
|
|
74
|
-
error: credentials.authenticated ? undefined : credentials.error || 'Not authenticated',
|
|
75
|
-
};
|
|
76
|
-
}
|
|
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
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Checks Gemini credentials from API key env vars or local OAuth credential files.
|
|
162
|
-
*/
|
|
163
|
-
private async checkCredentials(): Promise<GeminiCredentialsStatus> {
|
|
164
|
-
if (process.env.GEMINI_API_KEY?.trim()) {
|
|
165
|
-
return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
|
|
166
|
-
}
|
|
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
|
-
|
|
206
|
-
try {
|
|
207
|
-
const credsPath = path.join(this.getGeminiCliHome(), '.gemini', 'oauth_creds.json');
|
|
208
|
-
const content = await readFile(credsPath, 'utf8');
|
|
209
|
-
const creds = readObjectRecord(JSON.parse(content)) ?? {};
|
|
210
|
-
const accessToken = readOptionalString(creds.access_token);
|
|
211
|
-
|
|
212
|
-
if (!accessToken) {
|
|
213
|
-
return {
|
|
214
|
-
authenticated: false,
|
|
215
|
-
email: null,
|
|
216
|
-
method: null,
|
|
217
|
-
error: 'No valid tokens found in oauth_creds',
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const refreshToken = readOptionalString(creds.refresh_token);
|
|
222
|
-
const tokenInfo = await this.getTokenInfoEmail(accessToken);
|
|
223
|
-
if (tokenInfo.valid) {
|
|
224
|
-
return {
|
|
225
|
-
authenticated: true,
|
|
226
|
-
email: tokenInfo.email || 'OAuth Session',
|
|
227
|
-
method: 'credentials_file',
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (!refreshToken) {
|
|
232
|
-
return {
|
|
233
|
-
authenticated: false,
|
|
234
|
-
email: null,
|
|
235
|
-
method: 'credentials_file',
|
|
236
|
-
error: 'Access token invalid and no refresh token found',
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
authenticated: true,
|
|
242
|
-
email: await this.getActiveAccountEmail() || 'OAuth Session',
|
|
243
|
-
method: 'credentials_file',
|
|
244
|
-
};
|
|
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.
|
|
265
|
-
return {
|
|
266
|
-
authenticated: false,
|
|
267
|
-
email: null,
|
|
268
|
-
method: null,
|
|
269
|
-
error: 'Gemini CLI not configured',
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Validates a Gemini OAuth access token and returns an email when Google reports one.
|
|
276
|
-
*/
|
|
277
|
-
private async getTokenInfoEmail(accessToken: string): Promise<{ valid: boolean; email: string | null }> {
|
|
278
|
-
try {
|
|
279
|
-
const tokenRes = await fetch(`https://oauth2.googleapis.com/tokeninfo?access_token=${accessToken}`);
|
|
280
|
-
if (!tokenRes.ok) {
|
|
281
|
-
return { valid: false, email: null };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const tokenInfo = readObjectRecord(await tokenRes.json());
|
|
285
|
-
return {
|
|
286
|
-
valid: true,
|
|
287
|
-
email: readOptionalString(tokenInfo?.email) ?? null,
|
|
288
|
-
};
|
|
289
|
-
} catch {
|
|
290
|
-
return { valid: false, email: null };
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Reads Gemini's active local Google account as an offline fallback for display.
|
|
296
|
-
*/
|
|
297
|
-
private async getActiveAccountEmail(): Promise<string | null> {
|
|
298
|
-
try {
|
|
299
|
-
const accPath = path.join(this.getGeminiCliHome(), '.gemini', 'google_accounts.json');
|
|
300
|
-
const accContent = await readFile(accPath, 'utf8');
|
|
301
|
-
const accounts = readObjectRecord(JSON.parse(accContent));
|
|
302
|
-
return readOptionalString(accounts?.active) ?? null;
|
|
303
|
-
} catch {
|
|
304
|
-
return null;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { McpProvider } from '@/modules/providers/shared/mcp/mcp.provider.js';
|
|
5
|
-
import type { McpScope, ProviderMcpServer, UpsertProviderMcpServerInput } from '@/shared/types.js';
|
|
6
|
-
import {
|
|
7
|
-
AppError,
|
|
8
|
-
readJsonConfig,
|
|
9
|
-
readObjectRecord,
|
|
10
|
-
readOptionalString,
|
|
11
|
-
readStringArray,
|
|
12
|
-
readStringRecord,
|
|
13
|
-
writeJsonConfig,
|
|
14
|
-
} from '@/shared/utils.js';
|
|
15
|
-
|
|
16
|
-
export class GeminiMcpProvider extends McpProvider {
|
|
17
|
-
constructor() {
|
|
18
|
-
super('gemini', ['user', 'project'], ['stdio', 'http', 'sse']);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
protected async readScopedServers(scope: McpScope, workspacePath: string): Promise<Record<string, unknown>> {
|
|
22
|
-
const filePath = scope === 'user'
|
|
23
|
-
? path.join(os.homedir(), '.gemini', 'settings.json')
|
|
24
|
-
: path.join(workspacePath, '.gemini', 'settings.json');
|
|
25
|
-
const config = await readJsonConfig(filePath);
|
|
26
|
-
return readObjectRecord(config.mcpServers) ?? {};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
protected async writeScopedServers(
|
|
30
|
-
scope: McpScope,
|
|
31
|
-
workspacePath: string,
|
|
32
|
-
servers: Record<string, unknown>,
|
|
33
|
-
): Promise<void> {
|
|
34
|
-
const filePath = scope === 'user'
|
|
35
|
-
? path.join(os.homedir(), '.gemini', 'settings.json')
|
|
36
|
-
: path.join(workspacePath, '.gemini', 'settings.json');
|
|
37
|
-
const config = await readJsonConfig(filePath);
|
|
38
|
-
config.mcpServers = servers;
|
|
39
|
-
await writeJsonConfig(filePath, config);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
protected buildServerConfig(input: UpsertProviderMcpServerInput): Record<string, unknown> {
|
|
43
|
-
if (input.transport === 'stdio') {
|
|
44
|
-
if (!input.command?.trim()) {
|
|
45
|
-
throw new AppError('command is required for stdio MCP servers.', {
|
|
46
|
-
code: 'MCP_COMMAND_REQUIRED',
|
|
47
|
-
statusCode: 400,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
command: input.command,
|
|
53
|
-
args: input.args ?? [],
|
|
54
|
-
env: input.env ?? {},
|
|
55
|
-
cwd: input.cwd,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!input.url?.trim()) {
|
|
60
|
-
throw new AppError('url is required for http/sse MCP servers.', {
|
|
61
|
-
code: 'MCP_URL_REQUIRED',
|
|
62
|
-
statusCode: 400,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
type: input.transport,
|
|
68
|
-
url: input.url,
|
|
69
|
-
headers: input.headers ?? {},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
protected normalizeServerConfig(
|
|
74
|
-
scope: McpScope,
|
|
75
|
-
name: string,
|
|
76
|
-
rawConfig: unknown,
|
|
77
|
-
): ProviderMcpServer | null {
|
|
78
|
-
if (!rawConfig || typeof rawConfig !== 'object') {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const config = rawConfig as Record<string, unknown>;
|
|
83
|
-
if (typeof config.command === 'string') {
|
|
84
|
-
return {
|
|
85
|
-
provider: 'gemini',
|
|
86
|
-
name,
|
|
87
|
-
scope,
|
|
88
|
-
transport: 'stdio',
|
|
89
|
-
command: config.command,
|
|
90
|
-
args: readStringArray(config.args),
|
|
91
|
-
env: readStringRecord(config.env),
|
|
92
|
-
cwd: readOptionalString(config.cwd),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (typeof config.url === 'string') {
|
|
97
|
-
const transport = readOptionalString(config.type) === 'sse' ? 'sse' : 'http';
|
|
98
|
-
return {
|
|
99
|
-
provider: 'gemini',
|
|
100
|
-
name,
|
|
101
|
-
scope,
|
|
102
|
-
transport,
|
|
103
|
-
url: config.url,
|
|
104
|
-
headers: readStringRecord(config.headers),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { IProviderModels } from '@/shared/interfaces.js';
|
|
2
|
-
import type {
|
|
3
|
-
ProviderChangeActiveModelInput,
|
|
4
|
-
ProviderCurrentActiveModel,
|
|
5
|
-
ProviderModelsDefinition,
|
|
6
|
-
ProviderSessionActiveModelChange,
|
|
7
|
-
} from '@/shared/types.js';
|
|
8
|
-
import {
|
|
9
|
-
buildDefaultProviderCurrentActiveModel,
|
|
10
|
-
writeProviderSessionActiveModelChange,
|
|
11
|
-
} from '@/shared/utils.js';
|
|
12
|
-
|
|
13
|
-
export const GEMINI_FALLBACK_MODELS: ProviderModelsDefinition = {
|
|
14
|
-
OPTIONS: [
|
|
15
|
-
{ value: 'gemini-3-flash-preview', label: 'Gemini 3 Flash Preview' },
|
|
16
|
-
{ value: 'gemini-3.1-flash-lite-preview', label: 'Gemini 3.1 Flash Lite Preview' },
|
|
17
|
-
{ value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash' },
|
|
18
|
-
{ value: 'gemini-2.5-flash-lite', label: 'Gemini 2.5 Flash Lite' },
|
|
19
|
-
{ value: 'gemma-4-31b-it', label: 'Gemma 4 31B IT' },
|
|
20
|
-
{ value: 'gemma-4-26b-a4b-it', label: 'Gemma 4 26B A4B IT' },
|
|
21
|
-
],
|
|
22
|
-
DEFAULT: 'gemini-3-flash-preview',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export class GeminiProviderModels implements IProviderModels {
|
|
26
|
-
async getSupportedModels(): Promise<ProviderModelsDefinition> {
|
|
27
|
-
return GEMINI_FALLBACK_MODELS;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async getCurrentActiveModel(): Promise<ProviderCurrentActiveModel> {
|
|
31
|
-
return buildDefaultProviderCurrentActiveModel(GEMINI_FALLBACK_MODELS);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async changeActiveModel(
|
|
35
|
-
input: ProviderChangeActiveModelInput,
|
|
36
|
-
): Promise<ProviderSessionActiveModelChange> {
|
|
37
|
-
return writeProviderSessionActiveModelChange('gemini', input);
|
|
38
|
-
}
|
|
39
|
-
}
|