From 6af88846017cf1b681c4db12bc51fa9c3ac76a53 Mon Sep 17 00:00:00 2001 From: Ryder Sun Date: Sat, 7 Nov 2026 21:55:33 +0800 Subject: [PATCH] Honor Windows casing for Gemini configuration directories Keep Gemini native configuration selection consistent with Windows filesystem semantics. Checks: 131 native configuration and readiness tests. --- desktop/src/agents/native-config-directory.cjs | 7 +++++-- .../test/agents/native-config-directory.test.cjs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/desktop/src/agents/native-config-directory.cjs b/desktop/src/agents/native-config-directory.cjs index 88e1f16..aa79007 100644 --- a/desktop/src/agents/native-config-directory.cjs +++ b/desktop/src/agents/native-config-directory.cjs @@ -228,7 +228,10 @@ function nativeConfigEnvironment(kind, preferences, env = {}, platform = process if (!spec || directory === undefined || directory === '') return env if (typeof directory !== 'string' || directory.length > 4096 || directory.includes('\u0000') || !path.isAbsolute(directory)) throw new Error('LOCAL_NATIVE_CONFIG_INVALID') - if (kind === 'gemini' && path.basename(path.resolve(directory)) !== '.gemini') throw new Error('LOCAL_NATIVE_CONFIG_INVALID') + if (kind === 'gemini') { + const name = path.basename(path.resolve(directory)) + if ((platform === 'win32' ? name.toLowerCase() : name) !== '.gemini') throw new Error('LOCAL_NATIVE_CONFIG_INVALID') + } if (kind === 'qwen' || kind === 'opencodereview') { const resolved = path.resolve(directory) const name = path.basename(resolved) @@ -252,8 +255,8 @@ function validateNativeConfigDirectory(kind, directory) { const spec = Object.hasOwn(NATIVE_CONFIG_DIRECTORIES, kind) ? NATIVE_CONFIG_DIRECTORIES[kind] : null if (!spec || typeof directory !== 'string' || directory.length > 4096 || directory.includes('\u0000') || !path.isAbsolute(directory)) throw new Error('LOCAL_NATIVE_CONFIG_INVALID') - if (kind === 'gemini' && path.basename(path.resolve(directory)) !== '.gemini') throw new Error('LOCAL_NATIVE_CONFIG_INVALID') const caseName = value => process.platform === 'win32' ? value.toLowerCase() : value + if (kind === 'gemini' && caseName(path.basename(path.resolve(directory))) !== '.gemini') throw new Error('LOCAL_NATIVE_CONFIG_INVALID') if (kind === 'qwen' && caseName(path.basename(path.resolve(directory))) !== '.qwen') throw new Error('LOCAL_NATIVE_CONFIG_INVALID') if (kind === 'opencodereview' && caseName(path.basename(path.resolve(directory))) !== '.opencodereview') throw new Error('LOCAL_NATIVE_CONFIG_INVALID') if (kind === 'zcode' && (caseName(path.basename(path.resolve(directory))) !== 'cli' diff --git a/desktop/test/agents/native-config-directory.test.cjs b/desktop/test/agents/native-config-directory.test.cjs index 20f7014..5d787fa 100644 --- a/desktop/test/agents/native-config-directory.test.cjs +++ b/desktop/test/agents/native-config-directory.test.cjs @@ -5,6 +5,22 @@ const os = require('node:os') const path = require('node:path') const { nativeAuthIdentityFiles, nativeConfigEnvironment, nativeIdentityDirectory, nativeProjectConfigPaths, validateNativeConfigDirectory } = require('../../src/agents/native-config-directory.cjs') +test('Gemini native configuration respects Windows directory case rules', t => { + const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-gemini-case-')) + t.after(() => fs.rmSync(home, { recursive: true, force: true })) + const directory = path.join(home, '.GEMINI') + fs.mkdirSync(directory) + fs.writeFileSync(path.join(directory, 'settings.json'), '{}') + const preferences = { gemini: { nativeConfigDirectory: directory } } + assert.equal(nativeConfigEnvironment('gemini', preferences, {}, 'win32').GEMINI_CLI_HOME, home) + if (process.platform === 'win32') { + assert.equal(validateNativeConfigDirectory('gemini', directory), directory) + } else { + assert.throws(() => validateNativeConfigDirectory('gemini', directory), { message: 'LOCAL_NATIVE_CONFIG_INVALID' }) + assert.throws(() => nativeConfigEnvironment('gemini', preferences, {}, process.platform), { message: 'LOCAL_NATIVE_CONFIG_INVALID' }) + } +}) + test('OpenClaw selects its native state directory without changing other Agents', t => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-openclaw-config-')) t.after(() => fs.rmSync(root, { recursive: true, force: true }))