diff --git a/src/tui/shared/state-paths.ts b/src/tui/shared/state-paths.ts new file mode 100644 index 0000000..e7e0e6c --- /dev/null +++ b/src/tui/shared/state-paths.ts @@ -0,0 +1,19 @@ +import * as os from 'os'; +import * as path from 'path'; + +const APP_DIR = 'codesys-mcp'; +const FILE_NAME = 'tui-state.json'; + +export function stateFilePath(): string { + if (process.platform === 'win32') { + const localAppData = process.env.LOCALAPPDATA; + if (!localAppData) { + throw new Error('LOCALAPPDATA is not set; cannot resolve TUI state file path'); + } + return path.win32.join(localAppData, APP_DIR, FILE_NAME); + } + const xdg = process.env.XDG_STATE_HOME; + const home = process.env.HOME ?? os.homedir(); + const base = xdg ?? path.posix.join(home, '.local', 'state'); + return path.posix.join(base, APP_DIR, FILE_NAME); +} diff --git a/tests/tui/state-paths.test.ts b/tests/tui/state-paths.test.ts new file mode 100644 index 0000000..4df3849 --- /dev/null +++ b/tests/tui/state-paths.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import * as path from 'path'; +import { stateFilePath } from '../../src/tui/shared/state-paths.ts'; + +const ORIG_PLATFORM = Object.getOwnPropertyDescriptor(process, 'platform')!; +const ORIG_ENV = { ...process.env }; + +function setPlatform(p: NodeJS.Platform) { + Object.defineProperty(process, 'platform', { value: p, configurable: true }); +} + +beforeEach(() => { + process.env = { ...ORIG_ENV }; +}); + +afterEach(() => { + Object.defineProperty(process, 'platform', ORIG_PLATFORM); + process.env = { ...ORIG_ENV }; +}); + +describe('stateFilePath', () => { + it('uses %LOCALAPPDATA%/codesys-mcp/tui-state.json on Windows', () => { + setPlatform('win32'); + process.env.LOCALAPPDATA = 'C:\\\\Users\\\\u\\\\AppData\\\\Local'; + const p = stateFilePath(); + expect(p).toBe( + path.join('C:\\\\Users\\\\u\\\\AppData\\\\Local', 'codesys-mcp', 'tui-state.json') + ); + }); + + it('uses $XDG_STATE_HOME/codesys-mcp/tui-state.json when set', () => { + setPlatform('linux'); + process.env.XDG_STATE_HOME = '/tmp/xdg-state'; + const p = stateFilePath(); + expect(p).toBe('/tmp/xdg-state/codesys-mcp/tui-state.json'); + }); + + it('falls back to ~/.local/state on Linux without XDG_STATE_HOME', () => { + setPlatform('linux'); + delete process.env.XDG_STATE_HOME; + process.env.HOME = '/home/u'; + const p = stateFilePath(); + expect(p).toBe('/home/u/.local/state/codesys-mcp/tui-state.json'); + }); + + it('throws on Windows when LOCALAPPDATA is unset', () => { + setPlatform('win32'); + delete process.env.LOCALAPPDATA; + expect(() => stateFilePath()).toThrow(/LOCALAPPDATA/); + }); +});