@google/gemini-cli-core 0.3.0-preview.2 → 0.4.0-nightly.20250904.e133acd2
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/google-gemini-cli-core-0.2.2.tgz +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/src/code_assist/codeAssist.d.ts +2 -0
- package/dist/src/code_assist/codeAssist.js +12 -0
- package/dist/src/code_assist/codeAssist.js.map +1 -1
- package/dist/src/code_assist/converter.d.ts +2 -1
- package/dist/src/code_assist/converter.js +1 -1
- package/dist/src/code_assist/converter.js.map +1 -1
- package/dist/src/code_assist/oauth2.js +64 -27
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +252 -0
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/config/config.d.ts +12 -3
- package/dist/src/config/config.js +36 -7
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +26 -40
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/models.d.ts +1 -0
- package/dist/src/config/models.js +2 -0
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/core/client.d.ts +6 -2
- package/dist/src/core/client.js +22 -11
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +62 -7
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +2 -2
- package/dist/src/core/coreToolScheduler.test.js +18 -0
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +16 -0
- package/dist/src/core/geminiChat.js +133 -4
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +184 -0
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.js +9 -9
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +2 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/turn.d.ts +14 -4
- package/dist/src/core/turn.js +32 -10
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +217 -8
- package/dist/src/core/turn.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +6 -1
- package/dist/src/ide/ide-client.js +20 -2
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ideContext.d.ts +12 -0
- package/dist/src/ide/ideContext.js +1 -0
- package/dist/src/ide/ideContext.js.map +1 -1
- package/dist/src/ide/process-utils.js +15 -9
- package/dist/src/ide/process-utils.js.map +1 -1
- package/dist/src/ide/process-utils.test.js +90 -4
- package/dist/src/ide/process-utils.test.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/token-storage/base-token-storage.d.ts +1 -1
- package/dist/src/mcp/token-storage/base-token-storage.js +1 -1
- package/dist/src/mcp/token-storage/base-token-storage.js.map +1 -1
- package/dist/src/mcp/token-storage/base-token-storage.test.js +1 -1
- package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -1
- package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
- package/dist/src/mcp/token-storage/file-token-storage.js +144 -0
- package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js +235 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.d.ts +23 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.js +78 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +193 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +31 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.js +190 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.js +254 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/types.d.ts +4 -0
- package/dist/src/mcp/token-storage/types.js +5 -1
- package/dist/src/mcp/token-storage/types.js.map +1 -1
- package/dist/src/services/chatRecordingService.d.ts +5 -12
- package/dist/src/services/chatRecordingService.js +25 -16
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/chatRecordingService.test.js +54 -17
- package/dist/src/services/chatRecordingService.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +10 -16
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +27 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +3 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +4 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/loggers.js +1 -1
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +1 -2
- package/dist/src/telemetry/metrics.js +1 -7
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +5 -25
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +1 -3
- package/dist/src/telemetry/types.js +1 -3
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/test-utils/index.d.ts +6 -0
- package/dist/src/test-utils/index.js +7 -0
- package/dist/src/test-utils/index.js.map +1 -0
- package/dist/src/test-utils/mock-tool.d.ts +41 -0
- package/dist/src/test-utils/mock-tool.js +51 -0
- package/dist/src/test-utils/mock-tool.js.map +1 -0
- package/dist/src/tools/diffOptions.js +21 -13
- package/dist/src/tools/diffOptions.js.map +1 -1
- package/dist/src/tools/diffOptions.test.js +40 -4
- package/dist/src/tools/diffOptions.test.js.map +1 -1
- package/dist/src/tools/edit.d.ts +2 -2
- package/dist/src/tools/edit.js +20 -22
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +26 -5
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.d.ts +5 -3
- package/dist/src/tools/mcp-client-manager.js +30 -5
- package/dist/src/tools/mcp-client-manager.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.test.js +20 -1
- package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +5 -5
- package/dist/src/tools/mcp-client.js +13 -22
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +3 -3
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts +3 -2
- package/dist/src/tools/mcp-tool.js +9 -9
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +28 -7
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.js +5 -33
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/read-file.js +1 -1
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-many-files.js +1 -1
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/shell.js +1 -1
- package/dist/src/tools/shell.test.js +2 -2
- package/dist/src/tools/smart-edit.d.ts +73 -0
- package/dist/src/tools/smart-edit.js +607 -0
- package/dist/src/tools/smart-edit.js.map +1 -0
- package/dist/src/tools/smart-edit.test.d.ts +6 -0
- package/dist/src/tools/smart-edit.test.js +397 -0
- package/dist/src/tools/smart-edit.test.js.map +1 -0
- package/dist/src/tools/tool-registry.d.ts +2 -1
- package/dist/src/tools/tool-registry.js +5 -5
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tools.d.ts +5 -1
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/web-fetch.js +4 -3
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-search.d.ts +1 -1
- package/dist/src/tools/web-search.js +3 -1
- package/dist/src/tools/web-search.js.map +1 -1
- package/dist/src/tools/write-file.js +9 -13
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/utils/fileUtils.d.ts +19 -3
- package/dist/src/utils/fileUtils.js +139 -28
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +293 -1
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.d.ts +1 -0
- package/dist/src/utils/gitIgnoreParser.js +101 -10
- package/dist/src/utils/gitIgnoreParser.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.test.js +66 -0
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
- package/dist/src/utils/ide-trust.d.ts +10 -0
- package/dist/src/utils/ide-trust.js +14 -0
- package/dist/src/utils/ide-trust.js.map +1 -0
- package/dist/src/utils/llm-edit-fixer.d.ts +25 -0
- package/dist/src/utils/llm-edit-fixer.js +112 -0
- package/dist/src/utils/llm-edit-fixer.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.d.ts +5 -4
- package/dist/src/utils/memoryDiscovery.js +10 -9
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +38 -19
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +33 -0
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/google-gemini-cli-core-0.3.0-preview.1.tgz +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import * as crypto from 'node:crypto';
|
|
7
|
+
import { BaseTokenStorage } from './base-token-storage.js';
|
|
8
|
+
const KEYCHAIN_TEST_PREFIX = '__keychain_test__';
|
|
9
|
+
export class KeychainTokenStorage extends BaseTokenStorage {
|
|
10
|
+
keychainAvailable = null;
|
|
11
|
+
keytarModule = null;
|
|
12
|
+
keytarLoadAttempted = false;
|
|
13
|
+
async getKeytar() {
|
|
14
|
+
// If we've already tried loading (successfully or not), return the result
|
|
15
|
+
if (this.keytarLoadAttempted) {
|
|
16
|
+
return this.keytarModule;
|
|
17
|
+
}
|
|
18
|
+
this.keytarLoadAttempted = true;
|
|
19
|
+
try {
|
|
20
|
+
// Try to import keytar without any timeout - let the OS handle it
|
|
21
|
+
const moduleName = 'keytar';
|
|
22
|
+
const module = await import(moduleName);
|
|
23
|
+
this.keytarModule = module.default || module;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.error(error);
|
|
27
|
+
}
|
|
28
|
+
return this.keytarModule;
|
|
29
|
+
}
|
|
30
|
+
async getCredentials(serverName) {
|
|
31
|
+
if (!(await this.checkKeychainAvailability())) {
|
|
32
|
+
throw new Error('Keychain is not available');
|
|
33
|
+
}
|
|
34
|
+
const keytar = await this.getKeytar();
|
|
35
|
+
if (!keytar) {
|
|
36
|
+
throw new Error('Keytar module not available');
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const sanitizedName = this.sanitizeServerName(serverName);
|
|
40
|
+
const data = await keytar.getPassword(this.serviceName, sanitizedName);
|
|
41
|
+
if (!data) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const credentials = JSON.parse(data);
|
|
45
|
+
if (this.isTokenExpired(credentials)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return credentials;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof SyntaxError) {
|
|
52
|
+
throw new Error(`Failed to parse stored credentials for ${serverName}`);
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async setCredentials(credentials) {
|
|
58
|
+
if (!(await this.checkKeychainAvailability())) {
|
|
59
|
+
throw new Error('Keychain is not available');
|
|
60
|
+
}
|
|
61
|
+
const keytar = await this.getKeytar();
|
|
62
|
+
if (!keytar) {
|
|
63
|
+
throw new Error('Keytar module not available');
|
|
64
|
+
}
|
|
65
|
+
this.validateCredentials(credentials);
|
|
66
|
+
const sanitizedName = this.sanitizeServerName(credentials.serverName);
|
|
67
|
+
const updatedCredentials = {
|
|
68
|
+
...credentials,
|
|
69
|
+
updatedAt: Date.now(),
|
|
70
|
+
};
|
|
71
|
+
const data = JSON.stringify(updatedCredentials);
|
|
72
|
+
await keytar.setPassword(this.serviceName, sanitizedName, data);
|
|
73
|
+
}
|
|
74
|
+
async deleteCredentials(serverName) {
|
|
75
|
+
if (!(await this.checkKeychainAvailability())) {
|
|
76
|
+
throw new Error('Keychain is not available');
|
|
77
|
+
}
|
|
78
|
+
const keytar = await this.getKeytar();
|
|
79
|
+
if (!keytar) {
|
|
80
|
+
throw new Error('Keytar module not available');
|
|
81
|
+
}
|
|
82
|
+
const sanitizedName = this.sanitizeServerName(serverName);
|
|
83
|
+
const deleted = await keytar.deletePassword(this.serviceName, sanitizedName);
|
|
84
|
+
if (!deleted) {
|
|
85
|
+
throw new Error(`No credentials found for ${serverName}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async listServers() {
|
|
89
|
+
if (!(await this.checkKeychainAvailability())) {
|
|
90
|
+
throw new Error('Keychain is not available');
|
|
91
|
+
}
|
|
92
|
+
const keytar = await this.getKeytar();
|
|
93
|
+
if (!keytar) {
|
|
94
|
+
throw new Error('Keytar module not available');
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const credentials = await keytar.findCredentials(this.serviceName);
|
|
98
|
+
return credentials
|
|
99
|
+
.filter((cred) => !cred.account.startsWith(KEYCHAIN_TEST_PREFIX))
|
|
100
|
+
.map((cred) => cred.account);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.error('Failed to list servers from keychain:', error);
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async getAllCredentials() {
|
|
108
|
+
if (!(await this.checkKeychainAvailability())) {
|
|
109
|
+
throw new Error('Keychain is not available');
|
|
110
|
+
}
|
|
111
|
+
const keytar = await this.getKeytar();
|
|
112
|
+
if (!keytar) {
|
|
113
|
+
throw new Error('Keytar module not available');
|
|
114
|
+
}
|
|
115
|
+
const result = new Map();
|
|
116
|
+
try {
|
|
117
|
+
const credentials = (await keytar.findCredentials(this.serviceName)).filter((c) => !c.account.startsWith(KEYCHAIN_TEST_PREFIX));
|
|
118
|
+
for (const cred of credentials) {
|
|
119
|
+
try {
|
|
120
|
+
const data = JSON.parse(cred.password);
|
|
121
|
+
if (!this.isTokenExpired(data)) {
|
|
122
|
+
result.set(cred.account, data);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.error(`Failed to parse credentials for ${cred.account}:`, error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error('Failed to get all credentials from keychain:', error);
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
async clearAll() {
|
|
136
|
+
if (!(await this.checkKeychainAvailability())) {
|
|
137
|
+
throw new Error('Keychain is not available');
|
|
138
|
+
}
|
|
139
|
+
const servers = this.keytarModule
|
|
140
|
+
? await this.keytarModule
|
|
141
|
+
.findCredentials(this.serviceName)
|
|
142
|
+
.then((creds) => creds.map((c) => c.account))
|
|
143
|
+
.catch((error) => {
|
|
144
|
+
throw new Error(`Failed to list servers for clearing: ${error.message}`);
|
|
145
|
+
})
|
|
146
|
+
: [];
|
|
147
|
+
const errors = [];
|
|
148
|
+
for (const server of servers) {
|
|
149
|
+
try {
|
|
150
|
+
await this.deleteCredentials(server);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
errors.push(error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (errors.length > 0) {
|
|
157
|
+
throw new Error(`Failed to clear some credentials: ${errors.map((e) => e.message).join(', ')}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Checks whether or not a set-get-delete cycle with the keychain works.
|
|
161
|
+
// Returns false if any operation fails.
|
|
162
|
+
async checkKeychainAvailability() {
|
|
163
|
+
if (this.keychainAvailable !== null) {
|
|
164
|
+
return this.keychainAvailable;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const keytar = await this.getKeytar();
|
|
168
|
+
if (!keytar) {
|
|
169
|
+
this.keychainAvailable = false;
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const testAccount = `${KEYCHAIN_TEST_PREFIX}${crypto.randomBytes(8).toString('hex')}`;
|
|
173
|
+
const testPassword = 'test';
|
|
174
|
+
await keytar.setPassword(this.serviceName, testAccount, testPassword);
|
|
175
|
+
const retrieved = await keytar.getPassword(this.serviceName, testAccount);
|
|
176
|
+
const deleted = await keytar.deletePassword(this.serviceName, testAccount);
|
|
177
|
+
const success = deleted && retrieved === testPassword;
|
|
178
|
+
this.keychainAvailable = success;
|
|
179
|
+
return success;
|
|
180
|
+
}
|
|
181
|
+
catch (_error) {
|
|
182
|
+
this.keychainAvailable = false;
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async isAvailable() {
|
|
187
|
+
return this.checkKeychainAvailability();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=keychain-token-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain-token-storage.js","sourceRoot":"","sources":["../../../../src/mcp/token-storage/keychain-token-storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAgB3D,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAEjD,MAAM,OAAO,oBAAqB,SAAQ,gBAAgB;IAChD,iBAAiB,GAAmB,IAAI,CAAC;IACzC,YAAY,GAAkB,IAAI,CAAC;IACnC,mBAAmB,GAAG,KAAK,CAAC;IAEpC,KAAK,CAAC,SAAS;QACb,0EAA0E;QAC1E,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,YAAY,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAEhC,IAAI,CAAC;YACH,kEAAkE;YAClE,MAAM,UAAU,GAAG,QAAQ,CAAC;YAC5B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACxC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,UAAkB;QACrC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,yBAAyB,EAAE,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;YAEvE,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;YAEzD,IAAI,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,WAAW,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CAAC,0CAA0C,UAAU,EAAE,CAAC,CAAC;YAC1E,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,WAA6B;QAChD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,yBAAyB,EAAE,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAEtC,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QACtE,MAAM,kBAAkB,GAAqB;YAC3C,GAAG,WAAW;YACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAChD,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,UAAkB;QACxC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,yBAAyB,EAAE,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,CACzC,IAAI,CAAC,WAAW,EAChB,aAAa,CACd,CAAC;QAEF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,yBAAyB,EAAE,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnE,OAAO,WAAW;iBACf,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC;iBAChE,GAAG,CAAC,CAAC,IAAyB,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;YAC9D,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,yBAAyB,EAAE,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,CAClB,MAAM,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAC/C,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAE7D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAqB,CAAC;oBAC3D,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC/B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;oBACjC,CAAC;gBACH,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CACX,mCAAmC,IAAI,CAAC,OAAO,GAAG,EAClD,KAAK,CACN,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,yBAAyB,EAAE,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY;YAC/B,CAAC,CAAC,MAAM,IAAI,CAAC,YAAY;iBACpB,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;iBACjC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;iBAC5C,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;gBACtB,MAAM,IAAI,KAAK,CACb,wCAAwC,KAAK,CAAC,OAAO,EAAE,CACxD,CAAC;YACJ,CAAC,CAAC;YACN,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,MAAM,GAAY,EAAE,CAAC;QAE3B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,KAAc,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,qCAAqC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC/E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,wCAAwC;IACxC,KAAK,CAAC,yBAAyB;QAC7B,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,iBAAiB,CAAC;QAChC,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;gBAC/B,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,WAAW,GAAG,GAAG,oBAAoB,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACtF,MAAM,YAAY,GAAG,MAAM,CAAC;YAE5B,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;YACtE,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAC1E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,CACzC,IAAI,CAAC,WAAW,EAChB,WAAW,CACZ,CAAC;YAEF,MAAM,OAAO,GAAG,OAAO,IAAI,SAAS,KAAK,YAAY,CAAC;YACtD,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC;YACjC,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;YAC/B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,IAAI,CAAC,yBAAyB,EAAE,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
7
|
+
// Hoist the mock to be available in the vi.mock factory
|
|
8
|
+
const mockKeytar = vi.hoisted(() => ({
|
|
9
|
+
getPassword: vi.fn(),
|
|
10
|
+
setPassword: vi.fn(),
|
|
11
|
+
deletePassword: vi.fn(),
|
|
12
|
+
findCredentials: vi.fn(),
|
|
13
|
+
}));
|
|
14
|
+
const mockServiceName = 'service-name';
|
|
15
|
+
const mockCryptoRandomBytesString = 'random-string';
|
|
16
|
+
// Mock the dynamic import of 'keytar'
|
|
17
|
+
vi.mock('keytar', () => ({
|
|
18
|
+
default: mockKeytar,
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('node:crypto', () => ({
|
|
21
|
+
randomBytes: vi.fn(() => ({
|
|
22
|
+
toString: vi.fn(() => mockCryptoRandomBytesString),
|
|
23
|
+
})),
|
|
24
|
+
}));
|
|
25
|
+
describe('KeychainTokenStorage', () => {
|
|
26
|
+
let storage;
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
vi.resetAllMocks();
|
|
29
|
+
// Reset the internal state of the keychain-token-storage module
|
|
30
|
+
vi.resetModules();
|
|
31
|
+
const { KeychainTokenStorage } = await import('./keychain-token-storage.js');
|
|
32
|
+
storage = new KeychainTokenStorage(mockServiceName);
|
|
33
|
+
});
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
vi.restoreAllMocks();
|
|
36
|
+
vi.useRealTimers();
|
|
37
|
+
});
|
|
38
|
+
const validCredentials = {
|
|
39
|
+
serverName: 'test-server',
|
|
40
|
+
token: {
|
|
41
|
+
accessToken: 'access-token',
|
|
42
|
+
tokenType: 'Bearer',
|
|
43
|
+
expiresAt: Date.now() + 3600000,
|
|
44
|
+
},
|
|
45
|
+
updatedAt: Date.now(),
|
|
46
|
+
};
|
|
47
|
+
describe('checkKeychainAvailability', () => {
|
|
48
|
+
it('should return true if keytar is available and functional', async () => {
|
|
49
|
+
mockKeytar.setPassword.mockResolvedValue(undefined);
|
|
50
|
+
mockKeytar.getPassword.mockResolvedValue('test');
|
|
51
|
+
mockKeytar.deletePassword.mockResolvedValue(true);
|
|
52
|
+
const isAvailable = await storage.checkKeychainAvailability();
|
|
53
|
+
expect(isAvailable).toBe(true);
|
|
54
|
+
expect(mockKeytar.setPassword).toHaveBeenCalledWith(mockServiceName, `__keychain_test__${mockCryptoRandomBytesString}`, 'test');
|
|
55
|
+
expect(mockKeytar.getPassword).toHaveBeenCalledWith(mockServiceName, `__keychain_test__${mockCryptoRandomBytesString}`);
|
|
56
|
+
expect(mockKeytar.deletePassword).toHaveBeenCalledWith(mockServiceName, `__keychain_test__${mockCryptoRandomBytesString}`);
|
|
57
|
+
});
|
|
58
|
+
it('should return false if keytar fails to set password', async () => {
|
|
59
|
+
mockKeytar.setPassword.mockRejectedValue(new Error('write error'));
|
|
60
|
+
const isAvailable = await storage.checkKeychainAvailability();
|
|
61
|
+
expect(isAvailable).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
it('should return false if retrieved password does not match', async () => {
|
|
64
|
+
mockKeytar.setPassword.mockResolvedValue(undefined);
|
|
65
|
+
mockKeytar.getPassword.mockResolvedValue('wrong-password');
|
|
66
|
+
mockKeytar.deletePassword.mockResolvedValue(true);
|
|
67
|
+
const isAvailable = await storage.checkKeychainAvailability();
|
|
68
|
+
expect(isAvailable).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
it('should cache the availability result', async () => {
|
|
71
|
+
mockKeytar.setPassword.mockResolvedValue(undefined);
|
|
72
|
+
mockKeytar.getPassword.mockResolvedValue('test');
|
|
73
|
+
mockKeytar.deletePassword.mockResolvedValue(true);
|
|
74
|
+
await storage.checkKeychainAvailability();
|
|
75
|
+
await storage.checkKeychainAvailability();
|
|
76
|
+
expect(mockKeytar.setPassword).toHaveBeenCalledTimes(1);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('with keychain unavailable', () => {
|
|
80
|
+
beforeEach(async () => {
|
|
81
|
+
// Force keychain to be unavailable
|
|
82
|
+
mockKeytar.setPassword.mockRejectedValue(new Error('keychain error'));
|
|
83
|
+
await storage.checkKeychainAvailability();
|
|
84
|
+
});
|
|
85
|
+
it('getCredentials should throw', async () => {
|
|
86
|
+
await expect(storage.getCredentials('server')).rejects.toThrow('Keychain is not available');
|
|
87
|
+
});
|
|
88
|
+
it('setCredentials should throw', async () => {
|
|
89
|
+
await expect(storage.setCredentials(validCredentials)).rejects.toThrow('Keychain is not available');
|
|
90
|
+
});
|
|
91
|
+
it('deleteCredentials should throw', async () => {
|
|
92
|
+
await expect(storage.deleteCredentials('server')).rejects.toThrow('Keychain is not available');
|
|
93
|
+
});
|
|
94
|
+
it('listServers should throw', async () => {
|
|
95
|
+
await expect(storage.listServers()).rejects.toThrow('Keychain is not available');
|
|
96
|
+
});
|
|
97
|
+
it('getAllCredentials should throw', async () => {
|
|
98
|
+
await expect(storage.getAllCredentials()).rejects.toThrow('Keychain is not available');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe('with keychain available', () => {
|
|
102
|
+
beforeEach(async () => {
|
|
103
|
+
mockKeytar.setPassword.mockResolvedValue(undefined);
|
|
104
|
+
mockKeytar.getPassword.mockResolvedValue('test');
|
|
105
|
+
mockKeytar.deletePassword.mockResolvedValue(true);
|
|
106
|
+
await storage.checkKeychainAvailability();
|
|
107
|
+
// Reset mocks after availability check
|
|
108
|
+
vi.resetAllMocks();
|
|
109
|
+
});
|
|
110
|
+
describe('getCredentials', () => {
|
|
111
|
+
it('should return null if no credentials are found', async () => {
|
|
112
|
+
mockKeytar.getPassword.mockResolvedValue(null);
|
|
113
|
+
const result = await storage.getCredentials('test-server');
|
|
114
|
+
expect(result).toBeNull();
|
|
115
|
+
expect(mockKeytar.getPassword).toHaveBeenCalledWith(mockServiceName, 'test-server');
|
|
116
|
+
});
|
|
117
|
+
it('should return credentials if found and not expired', async () => {
|
|
118
|
+
mockKeytar.getPassword.mockResolvedValue(JSON.stringify(validCredentials));
|
|
119
|
+
const result = await storage.getCredentials('test-server');
|
|
120
|
+
expect(result).toEqual(validCredentials);
|
|
121
|
+
});
|
|
122
|
+
it('should return null if credentials have expired', async () => {
|
|
123
|
+
const expiredCreds = {
|
|
124
|
+
...validCredentials,
|
|
125
|
+
token: { ...validCredentials.token, expiresAt: Date.now() - 1000 },
|
|
126
|
+
};
|
|
127
|
+
mockKeytar.getPassword.mockResolvedValue(JSON.stringify(expiredCreds));
|
|
128
|
+
const result = await storage.getCredentials('test-server');
|
|
129
|
+
expect(result).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
it('should throw if stored data is corrupted JSON', async () => {
|
|
132
|
+
mockKeytar.getPassword.mockResolvedValue('not-json');
|
|
133
|
+
await expect(storage.getCredentials('test-server')).rejects.toThrow('Failed to parse stored credentials for test-server');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('setCredentials', () => {
|
|
137
|
+
it('should save credentials to keychain', async () => {
|
|
138
|
+
vi.useFakeTimers();
|
|
139
|
+
mockKeytar.setPassword.mockResolvedValue(undefined);
|
|
140
|
+
await storage.setCredentials(validCredentials);
|
|
141
|
+
expect(mockKeytar.setPassword).toHaveBeenCalledWith(mockServiceName, 'test-server', JSON.stringify({ ...validCredentials, updatedAt: Date.now() }));
|
|
142
|
+
});
|
|
143
|
+
it('should throw if saving to keychain fails', async () => {
|
|
144
|
+
mockKeytar.setPassword.mockRejectedValue(new Error('keychain write error'));
|
|
145
|
+
await expect(storage.setCredentials(validCredentials)).rejects.toThrow('keychain write error');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('deleteCredentials', () => {
|
|
149
|
+
it('should delete credentials from keychain', async () => {
|
|
150
|
+
mockKeytar.deletePassword.mockResolvedValue(true);
|
|
151
|
+
await storage.deleteCredentials('test-server');
|
|
152
|
+
expect(mockKeytar.deletePassword).toHaveBeenCalledWith(mockServiceName, 'test-server');
|
|
153
|
+
});
|
|
154
|
+
it('should throw if no credentials were found to delete', async () => {
|
|
155
|
+
mockKeytar.deletePassword.mockResolvedValue(false);
|
|
156
|
+
await expect(storage.deleteCredentials('test-server')).rejects.toThrow('No credentials found for test-server');
|
|
157
|
+
});
|
|
158
|
+
it('should throw if deleting from keychain fails', async () => {
|
|
159
|
+
mockKeytar.deletePassword.mockRejectedValue(new Error('keychain delete error'));
|
|
160
|
+
await expect(storage.deleteCredentials('test-server')).rejects.toThrow('keychain delete error');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe('listServers', () => {
|
|
164
|
+
it('should return a list of server names', async () => {
|
|
165
|
+
mockKeytar.findCredentials.mockResolvedValue([
|
|
166
|
+
{ account: 'server1', password: '' },
|
|
167
|
+
{ account: 'server2', password: '' },
|
|
168
|
+
]);
|
|
169
|
+
const result = await storage.listServers();
|
|
170
|
+
expect(result).toEqual(['server1', 'server2']);
|
|
171
|
+
});
|
|
172
|
+
it('should not include internal test keys in the server list', async () => {
|
|
173
|
+
mockKeytar.findCredentials.mockResolvedValue([
|
|
174
|
+
{ account: 'server1', password: '' },
|
|
175
|
+
{
|
|
176
|
+
account: `__keychain_test__${mockCryptoRandomBytesString}`,
|
|
177
|
+
password: '',
|
|
178
|
+
},
|
|
179
|
+
{ account: 'server2', password: '' },
|
|
180
|
+
]);
|
|
181
|
+
const result = await storage.listServers();
|
|
182
|
+
expect(result).toEqual(['server1', 'server2']);
|
|
183
|
+
});
|
|
184
|
+
it('should return an empty array on error', async () => {
|
|
185
|
+
mockKeytar.findCredentials.mockRejectedValue(new Error('find error'));
|
|
186
|
+
const result = await storage.listServers();
|
|
187
|
+
expect(result).toEqual([]);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
describe('getAllCredentials', () => {
|
|
191
|
+
it('should return a map of all valid credentials', async () => {
|
|
192
|
+
const creds2 = {
|
|
193
|
+
...validCredentials,
|
|
194
|
+
serverName: 'server2',
|
|
195
|
+
};
|
|
196
|
+
const expiredCreds = {
|
|
197
|
+
...validCredentials,
|
|
198
|
+
serverName: 'expired-server',
|
|
199
|
+
token: { ...validCredentials.token, expiresAt: Date.now() - 1000 },
|
|
200
|
+
};
|
|
201
|
+
const structurallyInvalidCreds = {
|
|
202
|
+
serverName: 'invalid-server',
|
|
203
|
+
};
|
|
204
|
+
mockKeytar.findCredentials.mockResolvedValue([
|
|
205
|
+
{
|
|
206
|
+
account: 'test-server',
|
|
207
|
+
password: JSON.stringify(validCredentials),
|
|
208
|
+
},
|
|
209
|
+
{ account: 'server2', password: JSON.stringify(creds2) },
|
|
210
|
+
{
|
|
211
|
+
account: 'expired-server',
|
|
212
|
+
password: JSON.stringify(expiredCreds),
|
|
213
|
+
},
|
|
214
|
+
{ account: 'bad-server', password: 'not-json' },
|
|
215
|
+
{
|
|
216
|
+
account: 'invalid-server',
|
|
217
|
+
password: JSON.stringify(structurallyInvalidCreds),
|
|
218
|
+
},
|
|
219
|
+
]);
|
|
220
|
+
const result = await storage.getAllCredentials();
|
|
221
|
+
expect(result.size).toBe(2);
|
|
222
|
+
expect(result.get('test-server')).toEqual(validCredentials);
|
|
223
|
+
expect(result.get('server2')).toEqual(creds2);
|
|
224
|
+
expect(result.has('expired-server')).toBe(false);
|
|
225
|
+
expect(result.has('bad-server')).toBe(false);
|
|
226
|
+
expect(result.has('invalid-server')).toBe(false);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
describe('clearAll', () => {
|
|
230
|
+
it('should delete all credentials for the service', async () => {
|
|
231
|
+
mockKeytar.findCredentials.mockResolvedValue([
|
|
232
|
+
{ account: 'server1', password: '' },
|
|
233
|
+
{ account: 'server2', password: '' },
|
|
234
|
+
]);
|
|
235
|
+
mockKeytar.deletePassword.mockResolvedValue(true);
|
|
236
|
+
await storage.clearAll();
|
|
237
|
+
expect(mockKeytar.deletePassword).toHaveBeenCalledTimes(2);
|
|
238
|
+
expect(mockKeytar.deletePassword).toHaveBeenCalledWith(mockServiceName, 'server1');
|
|
239
|
+
expect(mockKeytar.deletePassword).toHaveBeenCalledWith(mockServiceName, 'server2');
|
|
240
|
+
});
|
|
241
|
+
it('should throw an aggregated error if deletions fail', async () => {
|
|
242
|
+
mockKeytar.findCredentials.mockResolvedValue([
|
|
243
|
+
{ account: 'server1', password: '' },
|
|
244
|
+
{ account: 'server2', password: '' },
|
|
245
|
+
]);
|
|
246
|
+
mockKeytar.deletePassword
|
|
247
|
+
.mockResolvedValueOnce(true)
|
|
248
|
+
.mockRejectedValueOnce(new Error('delete failed'));
|
|
249
|
+
await expect(storage.clearAll()).rejects.toThrow('Failed to clear some credentials: delete failed');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
//# sourceMappingURL=keychain-token-storage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain-token-storage.test.js","sourceRoot":"","sources":["../../../../src/mcp/token-storage/keychain-token-storage.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,5 +3,9 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
6
|
+
export var TokenStorageType;
|
|
7
|
+
(function (TokenStorageType) {
|
|
8
|
+
TokenStorageType["KEYCHAIN"] = "keychain";
|
|
9
|
+
TokenStorageType["ENCRYPTED_FILE"] = "encrypted_file";
|
|
10
|
+
})(TokenStorageType || (TokenStorageType = {}));
|
|
7
11
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/mcp/token-storage/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/mcp/token-storage/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkCH,MAAM,CAAN,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,yCAAqB,CAAA;IACrB,qDAAiC,CAAA;AACnC,CAAC,EAHW,gBAAgB,KAAhB,gBAAgB,QAG3B"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { type Config } from '../config/config.js';
|
|
7
7
|
import { type Status } from '../core/coreToolScheduler.js';
|
|
8
8
|
import { type ThoughtSummary } from '../core/turn.js';
|
|
9
|
-
import type { PartListUnion } from '@google/genai';
|
|
9
|
+
import type { PartListUnion, GenerateContentResponseUsageMetadata } from '@google/genai';
|
|
10
10
|
/**
|
|
11
11
|
* Token usage summary for a message or conversation.
|
|
12
12
|
*/
|
|
@@ -24,7 +24,7 @@ export interface TokensSummary {
|
|
|
24
24
|
export interface BaseMessageRecord {
|
|
25
25
|
id: string;
|
|
26
26
|
timestamp: string;
|
|
27
|
-
content:
|
|
27
|
+
content: PartListUnion;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Record of a tool call execution within a conversation.
|
|
@@ -108,8 +108,7 @@ export declare class ChatRecordingService {
|
|
|
108
108
|
*/
|
|
109
109
|
recordMessage(message: {
|
|
110
110
|
type: ConversationRecordExtra['type'];
|
|
111
|
-
content:
|
|
112
|
-
append?: boolean;
|
|
111
|
+
content: PartListUnion;
|
|
113
112
|
}): void;
|
|
114
113
|
/**
|
|
115
114
|
* Records a thought from the assistant's reasoning process.
|
|
@@ -118,16 +117,10 @@ export declare class ChatRecordingService {
|
|
|
118
117
|
/**
|
|
119
118
|
* Updates the tokens for the last message in the conversation (which should be by Gemini).
|
|
120
119
|
*/
|
|
121
|
-
recordMessageTokens(
|
|
122
|
-
input: number;
|
|
123
|
-
output: number;
|
|
124
|
-
cached: number;
|
|
125
|
-
thoughts?: number;
|
|
126
|
-
tool?: number;
|
|
127
|
-
total: number;
|
|
128
|
-
}): void;
|
|
120
|
+
recordMessageTokens(respUsageMetadata: GenerateContentResponseUsageMetadata): void;
|
|
129
121
|
/**
|
|
130
122
|
* Adds tool calls to the last message in the conversation (which should be by Gemini).
|
|
123
|
+
* This method enriches tool calls with metadata from the ToolRegistry.
|
|
131
124
|
*/
|
|
132
125
|
recordToolCalls(toolCalls: ToolCallRecord[]): void;
|
|
133
126
|
/**
|
|
@@ -97,15 +97,6 @@ export class ChatRecordingService {
|
|
|
97
97
|
return;
|
|
98
98
|
try {
|
|
99
99
|
this.updateConversation((conversation) => {
|
|
100
|
-
if (message.append) {
|
|
101
|
-
const lastMsg = this.getLastMessage(conversation);
|
|
102
|
-
if (lastMsg && lastMsg.type === message.type) {
|
|
103
|
-
lastMsg.content += message.content;
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// We're not appending, or we are appending but the last message's type is not the same as
|
|
108
|
-
// the specified type, so just create a new message.
|
|
109
100
|
const msg = this.newMessage(message.type, message.content);
|
|
110
101
|
if (msg.type === 'gemini') {
|
|
111
102
|
// If it's a new Gemini message then incorporate any queued thoughts.
|
|
@@ -142,19 +133,25 @@ export class ChatRecordingService {
|
|
|
142
133
|
});
|
|
143
134
|
}
|
|
144
135
|
catch (error) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
136
|
+
console.error('Error saving thought:', error);
|
|
137
|
+
throw error;
|
|
149
138
|
}
|
|
150
139
|
}
|
|
151
140
|
/**
|
|
152
141
|
* Updates the tokens for the last message in the conversation (which should be by Gemini).
|
|
153
142
|
*/
|
|
154
|
-
recordMessageTokens(
|
|
143
|
+
recordMessageTokens(respUsageMetadata) {
|
|
155
144
|
if (!this.conversationFile)
|
|
156
145
|
return;
|
|
157
146
|
try {
|
|
147
|
+
const tokens = {
|
|
148
|
+
input: respUsageMetadata.promptTokenCount ?? 0,
|
|
149
|
+
output: respUsageMetadata.candidatesTokenCount ?? 0,
|
|
150
|
+
cached: respUsageMetadata.cachedContentTokenCount ?? 0,
|
|
151
|
+
thoughts: respUsageMetadata.thoughtsTokenCount ?? 0,
|
|
152
|
+
tool: respUsageMetadata.toolUsePromptTokenCount ?? 0,
|
|
153
|
+
total: respUsageMetadata.totalTokenCount ?? 0,
|
|
154
|
+
};
|
|
158
155
|
this.updateConversation((conversation) => {
|
|
159
156
|
const lastMsg = this.getLastMessage(conversation);
|
|
160
157
|
// If the last message already has token info, it's because this new token info is for a
|
|
@@ -175,10 +172,22 @@ export class ChatRecordingService {
|
|
|
175
172
|
}
|
|
176
173
|
/**
|
|
177
174
|
* Adds tool calls to the last message in the conversation (which should be by Gemini).
|
|
175
|
+
* This method enriches tool calls with metadata from the ToolRegistry.
|
|
178
176
|
*/
|
|
179
177
|
recordToolCalls(toolCalls) {
|
|
180
178
|
if (!this.conversationFile)
|
|
181
179
|
return;
|
|
180
|
+
// Enrich tool calls with metadata from the ToolRegistry
|
|
181
|
+
const toolRegistry = this.config.getToolRegistry();
|
|
182
|
+
const enrichedToolCalls = toolCalls.map((toolCall) => {
|
|
183
|
+
const toolInstance = toolRegistry.getTool(toolCall.name);
|
|
184
|
+
return {
|
|
185
|
+
...toolCall,
|
|
186
|
+
displayName: toolInstance?.displayName || toolCall.name,
|
|
187
|
+
description: toolInstance?.description || '',
|
|
188
|
+
renderOutputAsMarkdown: toolInstance?.isOutputMarkdown || false,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
182
191
|
try {
|
|
183
192
|
this.updateConversation((conversation) => {
|
|
184
193
|
const lastMsg = this.getLastMessage(conversation);
|
|
@@ -199,7 +208,7 @@ export class ChatRecordingService {
|
|
|
199
208
|
// resulting message's type, and so it thinks that toolCalls may
|
|
200
209
|
// not be present. Confirming the type here satisfies it.
|
|
201
210
|
type: 'gemini',
|
|
202
|
-
toolCalls,
|
|
211
|
+
toolCalls: enrichedToolCalls,
|
|
203
212
|
thoughts: this.queuedThoughts,
|
|
204
213
|
model: this.config.getModel(),
|
|
205
214
|
};
|
|
@@ -234,7 +243,7 @@ export class ChatRecordingService {
|
|
|
234
243
|
}
|
|
235
244
|
});
|
|
236
245
|
// Add any new tools calls that aren't in the message yet.
|
|
237
|
-
for (const toolCall of
|
|
246
|
+
for (const toolCall of enrichedToolCalls) {
|
|
238
247
|
const existingToolCall = lastMsg.toolCalls.find((tc) => tc.id === toolCall.id);
|
|
239
248
|
if (!existingToolCall) {
|
|
240
249
|
lastMsg.toolCalls.push(toolCall);
|