data-dir.ts
1 import path from 'path' 2 import os from 'os' 3 import fs from 'fs' 4 5 function isBuildBootstrapEnv(env: NodeJS.ProcessEnv = process.env, argv: string[] = process.argv): boolean { 6 if (env.SWARMCLAW_BUILD_MODE === '1') return true 7 if (env.NEXT_PHASE === 'phase-production-build') return true 8 const lifecycle = env.npm_lifecycle_event?.trim().toLowerCase() 9 if (lifecycle === 'build' || lifecycle === 'build:ci' || lifecycle?.startsWith('build:')) return true 10 return argv.some((arg) => /\bnext(?:[\\/](?:dist[\\/]bin[\\/])?next)?\b/.test(arg)) 11 && argv.some((arg) => /\bbuild\b/.test(arg)) 12 } 13 14 export const IS_BUILD_BOOTSTRAP = isBuildBootstrapEnv() 15 16 function resolveSwarmclawHome(): string | null { 17 const configured = process.env.SWARMCLAW_HOME?.trim() 18 return configured ? path.resolve(configured) : null 19 } 20 21 function resolveDataDir(): string { 22 if (process.env.DATA_DIR) return process.env.DATA_DIR 23 if (IS_BUILD_BOOTSTRAP) return path.join(os.tmpdir(), 'swarmclaw-build-data') 24 const appHome = resolveSwarmclawHome() 25 if (appHome) return path.join(appHome, 'data') 26 return path.join(process.cwd(), 'data') 27 } 28 29 export const DATA_DIR = resolveDataDir() 30 export const CONNECTORS_DATA_DIR = path.join(DATA_DIR, 'connectors') 31 export const OPENCLAW_DATA_DIR = path.join(DATA_DIR, 'openclaw') 32 export const MEMORY_IMAGES_DIR = path.join(DATA_DIR, 'memory-images') 33 export const APP_LOG_PATH = path.join(DATA_DIR, 'app.log') 34 35 function supportsChildWrites(dir: string): boolean { 36 try { 37 fs.mkdirSync(dir, { recursive: true }) 38 const probeDir = fs.mkdtempSync(path.join(dir, '.swarmclaw-probe-')) 39 fs.rmSync(probeDir, { recursive: true, force: true }) 40 return true 41 } catch { 42 return false 43 } 44 } 45 46 // Workspace lives outside the project directory to avoid triggering Next.js HMR 47 // when agents create/modify files. Falls back to data/workspace for Docker/CI. 48 function resolveWorkspaceDir(): string { 49 if (process.env.WORKSPACE_DIR) return process.env.WORKSPACE_DIR 50 if (IS_BUILD_BOOTSTRAP) return path.join(DATA_DIR, 'workspace') 51 const appHome = resolveSwarmclawHome() 52 if (appHome) return path.join(appHome, 'workspace') 53 const external = path.join(os.homedir(), '.swarmclaw', 'workspace') 54 if (supportsChildWrites(external)) { 55 return external 56 } 57 return path.join(DATA_DIR, 'workspace') 58 } 59 60 export const WORKSPACE_DIR = resolveWorkspaceDir() 61 62 function resolveBrowserProfilesDir(): string { 63 if (process.env.BROWSER_PROFILES_DIR) return process.env.BROWSER_PROFILES_DIR 64 if (IS_BUILD_BOOTSTRAP) return path.join(DATA_DIR, 'browser-profiles') 65 const appHome = resolveSwarmclawHome() 66 if (appHome) return path.join(appHome, 'browser-profiles') 67 const external = path.join(os.homedir(), '.swarmclaw', 'browser-profiles') 68 if (supportsChildWrites(external)) { 69 return external 70 } 71 return path.join(DATA_DIR, 'browser-profiles') 72 } 73 74 export const BROWSER_PROFILES_DIR = resolveBrowserProfilesDir()