// Where s3d keeps its config and data on each OS, mirroring the lookup in // s3d's cmd/s3d/main.go (configSearchPaths / applicationDirectoryOS) so the // GUI and a hand-run `s3d` always agree on which instance they are touching. import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; export function defaultDataDir() { switch (process.platform) { case 'win32': return path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 's3d'); case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support', 's3d'); default: return '/var/lib/s3d'; } } export function configSearchPaths() { const paths = []; if (process.env.S3D_DATA_DIR) paths.push(path.join(process.env.S3D_DATA_DIR, 's3d.yml')); switch (process.platform) { case 'win32': paths.push(path.join(defaultDataDir(), 's3d.yml')); break; case 'darwin': paths.push(path.join(defaultDataDir(), 's3d.yml')); break; default: paths.push('/etc/s3d/s3d.yml', '/var/lib/s3d/s3d.yml'); } return paths; } // The config file this GUI manages: an explicit S3D_CONFIG_FILE wins, then the // first existing search path, then the first search path (created on save). export function resolveConfigPath(explicit) { if (explicit) return path.resolve(explicit); if (process.env.S3D_CONFIG_FILE) return path.resolve(process.env.S3D_CONFIG_FILE); const candidates = configSearchPaths(); return candidates.find((p) => fs.existsSync(p)) || candidates[0]; } const EXE = process.platform === 'win32' ? 's3d.exe' : 's3d'; // Binary lookup: explicit path, S3D_BIN, the vendored copy next to Pithos, then PATH. export function resolveBinary(explicit, vendorDir) { const tries = [explicit, process.env.S3D_BIN, vendorDir && path.join(vendorDir, EXE)].filter(Boolean); for (const p of tries) if (fs.existsSync(p)) return path.resolve(p); for (const dir of (process.env.PATH || '').split(path.delimiter)) { if (!dir) continue; const p = path.join(dir, EXE); if (fs.existsSync(p)) return p; } return null; }