mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 08:47:44 +00:00
Refac: Centralize storage file management (#4078)
Co-authored-by: Taylor Mullen <ntaylormullen@google.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
55
packages/core/src/config/storage.test.ts
Normal file
55
packages/core/src/config/storage.test.ts
Normal file
@@ -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);
|
||||
});
|
||||
});
|
||||
114
packages/core/src/config/storage.ts
Normal file
114
packages/core/src/config/storage.ts
Normal file
@@ -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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user