summaryrefslogtreecommitdiff
path: root/packages/core/src/config
diff options
context:
space:
mode:
authorYuki Okita <[email protected]>2025-08-20 10:55:47 +0900
committerGitHub <[email protected]>2025-08-20 01:55:47 +0000
commit21c6480b65528a98ac0e1e3855f3c78c1f9b7cbe (patch)
tree5555ec429209e87e0c21483c9e5fddd53ac01dbc /packages/core/src/config
parent1049d388451120587a8643a401fd71430a8cd5fe (diff)
Refac: Centralize storage file management (#4078)
Co-authored-by: Taylor Mullen <[email protected]>
Diffstat (limited to 'packages/core/src/config')
-rw-r--r--packages/core/src/config/config.ts20
-rw-r--r--packages/core/src/config/storage.test.ts55
-rw-r--r--packages/core/src/config/storage.ts114
3 files changed, 174 insertions, 15 deletions
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index 6a8e6d4b..39e885e2 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -22,16 +22,11 @@ import { ShellTool } from '../tools/shell.js';
import { WriteFileTool } from '../tools/write-file.js';
import { WebFetchTool } from '../tools/web-fetch.js';
import { ReadManyFilesTool } from '../tools/read-many-files.js';
-import {
- MemoryTool,
- setGeminiMdFilename,
- GEMINI_CONFIG_DIR as GEMINI_DIR,
-} from '../tools/memoryTool.js';
+import { MemoryTool, setGeminiMdFilename } from '../tools/memoryTool.js';
import { WebSearchTool } from '../tools/web-search.js';
import { GeminiClient } from '../core/client.js';
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
import { GitService } from '../services/gitService.js';
-import { getProjectTempDir } from '../utils/paths.js';
import {
initializeTelemetry,
DEFAULT_TELEMETRY_TARGET,
@@ -57,6 +52,7 @@ import { IdeConnectionEvent, IdeConnectionType } from '../telemetry/types.js';
// Re-export OAuth config type
export type { MCPOAuthConfig };
import { WorkspaceContext } from '../utils/workspaceContext.js';
+import { Storage } from './storage.js';
export enum ApprovalMode {
DEFAULT = 'default',
@@ -272,6 +268,7 @@ export class Config {
private readonly shouldUseNodePtyShell: boolean;
private readonly skipNextSpeakerCheck: boolean;
private initialized: boolean = false;
+ readonly storage: Storage;
constructor(params: ConfigParameters) {
this.sessionId = params.sessionId;
@@ -340,6 +337,7 @@ export class Config {
this.trustedFolder = params.trustedFolder;
this.shouldUseNodePtyShell = params.shouldUseNodePtyShell ?? false;
this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? false;
+ this.storage = new Storage(this.targetDir);
if (params.contextFileName) {
setGeminiMdFilename(params.contextFileName);
@@ -591,14 +589,6 @@ export class Config {
return this.geminiClient;
}
- getGeminiDir(): string {
- return path.join(this.targetDir, GEMINI_DIR);
- }
-
- getProjectTempDir(): string {
- return getProjectTempDir(this.getProjectRoot());
- }
-
getEnableRecursiveFileSearch(): boolean {
return this.fileFiltering.enableRecursiveFileSearch;
}
@@ -744,7 +734,7 @@ export class Config {
async getGitService(): Promise<GitService> {
if (!this.gitService) {
- this.gitService = new GitService(this.targetDir);
+ this.gitService = new GitService(this.targetDir, this.storage);
await this.gitService.initialize();
}
return this.gitService;
diff --git a/packages/core/src/config/storage.test.ts b/packages/core/src/config/storage.test.ts
new file mode 100644
index 00000000..4dab76f1
--- /dev/null
+++ b/packages/core/src/config/storage.test.ts
@@ -0,0 +1,55 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { describe, it, expect, vi } from 'vitest';
+import * as os from 'os';
+import * as path from 'node:path';
+
+vi.mock('fs', async (importOriginal) => {
+ const actual = await importOriginal<typeof import('fs')>();
+ return {
+ ...actual,
+ mkdirSync: vi.fn(),
+ };
+});
+
+import { Storage } from './storage.js';
+
+describe('Storage – getGlobalSettingsPath', () => {
+ it('returns path to ~/.gemini/settings.json', () => {
+ const expected = path.join(os.homedir(), '.gemini', 'settings.json');
+ expect(Storage.getGlobalSettingsPath()).toBe(expected);
+ });
+});
+
+describe('Storage – additional helpers', () => {
+ const projectRoot = '/tmp/project';
+ const storage = new Storage(projectRoot);
+
+ it('getWorkspaceSettingsPath returns project/.gemini/settings.json', () => {
+ const expected = path.join(projectRoot, '.gemini', 'settings.json');
+ expect(storage.getWorkspaceSettingsPath()).toBe(expected);
+ });
+
+ it('getUserCommandsDir returns ~/.gemini/commands', () => {
+ const expected = path.join(os.homedir(), '.gemini', 'commands');
+ expect(Storage.getUserCommandsDir()).toBe(expected);
+ });
+
+ it('getProjectCommandsDir returns project/.gemini/commands', () => {
+ const expected = path.join(projectRoot, '.gemini', 'commands');
+ expect(storage.getProjectCommandsDir()).toBe(expected);
+ });
+
+ it('getMcpOAuthTokensPath returns ~/.gemini/mcp-oauth-tokens.json', () => {
+ const expected = path.join(
+ os.homedir(),
+ '.gemini',
+ 'mcp-oauth-tokens.json',
+ );
+ expect(Storage.getMcpOAuthTokensPath()).toBe(expected);
+ });
+});
diff --git a/packages/core/src/config/storage.ts b/packages/core/src/config/storage.ts
new file mode 100644
index 00000000..1459c8c7
--- /dev/null
+++ b/packages/core/src/config/storage.ts
@@ -0,0 +1,114 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as path from 'node:path';
+import * as os from 'os';
+import * as crypto from 'crypto';
+import * as fs from 'fs';
+
+export const GEMINI_DIR = '.gemini';
+export const GOOGLE_ACCOUNTS_FILENAME = 'google_accounts.json';
+const TMP_DIR_NAME = 'tmp';
+
+export class Storage {
+ private readonly targetDir: string;
+
+ constructor(targetDir: string) {
+ this.targetDir = targetDir;
+ }
+
+ static getGlobalGeminiDir(): string {
+ const homeDir = os.homedir();
+ if (!homeDir) {
+ return path.join(os.tmpdir(), '.gemini');
+ }
+ return path.join(homeDir, GEMINI_DIR);
+ }
+
+ static getMcpOAuthTokensPath(): string {
+ return path.join(Storage.getGlobalGeminiDir(), 'mcp-oauth-tokens.json');
+ }
+
+ static getGlobalSettingsPath(): string {
+ return path.join(Storage.getGlobalGeminiDir(), 'settings.json');
+ }
+
+ static getInstallationIdPath(): string {
+ return path.join(Storage.getGlobalGeminiDir(), 'installation_id');
+ }
+
+ static getGoogleAccountsPath(): string {
+ return path.join(Storage.getGlobalGeminiDir(), GOOGLE_ACCOUNTS_FILENAME);
+ }
+
+ static getUserCommandsDir(): string {
+ return path.join(Storage.getGlobalGeminiDir(), 'commands');
+ }
+
+ static getGlobalMemoryFilePath(): string {
+ return path.join(Storage.getGlobalGeminiDir(), 'memory.md');
+ }
+
+ static getGlobalTempDir(): string {
+ return path.join(Storage.getGlobalGeminiDir(), TMP_DIR_NAME);
+ }
+
+ getGeminiDir(): string {
+ return path.join(this.targetDir, GEMINI_DIR);
+ }
+
+ getProjectTempDir(): string {
+ const hash = this.getFilePathHash(this.getProjectRoot());
+ const tempDir = Storage.getGlobalTempDir();
+ return path.join(tempDir, hash);
+ }
+
+ ensureProjectTempDirExists(): void {
+ fs.mkdirSync(this.getProjectTempDir(), { recursive: true });
+ }
+
+ static getOAuthCredsPath(): string {
+ return path.join(Storage.getGlobalGeminiDir(), 'oauth_creds.json');
+ }
+
+ getProjectRoot(): string {
+ return this.targetDir;
+ }
+
+ private getFilePathHash(filePath: string): string {
+ return crypto.createHash('sha256').update(filePath).digest('hex');
+ }
+
+ getHistoryDir(): string {
+ const hash = this.getFilePathHash(this.getProjectRoot());
+ const historyDir = path.join(Storage.getGlobalGeminiDir(), 'history');
+ return path.join(historyDir, hash);
+ }
+
+ getWorkspaceSettingsPath(): string {
+ return path.join(this.getGeminiDir(), 'settings.json');
+ }
+
+ getProjectCommandsDir(): string {
+ return path.join(this.getGeminiDir(), 'commands');
+ }
+
+ getProjectTempCheckpointsDir(): string {
+ return path.join(this.getProjectTempDir(), 'checkpoints');
+ }
+
+ getExtensionsDir(): string {
+ return path.join(this.getGeminiDir(), 'extensions');
+ }
+
+ getExtensionsConfigPath(): string {
+ return path.join(this.getExtensionsDir(), 'gemini-extension.json');
+ }
+
+ getHistoryFilePath(): string {
+ return path.join(this.getProjectTempDir(), 'shell_history');
+ }
+}