import { app } from 'electron'; import { existsSync, mkdirSync, realpathSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; export const OPENCLAW_CONFIG_DIR_NAME = '.openclaw'; export const OPENCLAW_RUNTIME_DIR_NAME = 'runtime'; export const OPENCLAW_PACKAGE_DIR_NAME = 'openclaw'; export const OPENCLAW_ENTRY_FILE_NAME = 'openclaw.mjs'; export const USER_DATA_DIR_ENV_NAME = 'ZN_AI_USER_DATA_DIR'; export interface OpenClawRuntimePaths { configDir: string; runtimeDir: string; dir: string; resolvedDir: string; entryPath: string; } export function getOpenClawConfigDir(): string { return join(homedir(), OPENCLAW_CONFIG_DIR_NAME); } export function getUserDataDir(): string { const override = process.env[USER_DATA_DIR_ENV_NAME]?.trim(); if (override) { return override; } return app.getPath('userData'); } export function getOpenClawRuntimeDir(): string { return join(getOpenClawConfigDir(), OPENCLAW_RUNTIME_DIR_NAME); } export function getOpenClawDir(): string { if (app.isPackaged) { return join(process.resourcesPath, OPENCLAW_PACKAGE_DIR_NAME); } return join(app.getAppPath(), 'node_modules', OPENCLAW_PACKAGE_DIR_NAME); } export function getResourcesDir(): string { if (app.isPackaged) { return process.resourcesPath; } return join(app.getAppPath(), 'resources'); } export function getOpenClawResolvedDir(): string { const dir = getOpenClawDir(); if (!existsSync(dir)) { return dir; } try { return realpathSync(dir); } catch { return dir; } } export function getOpenClawEntryPath(): string { return join(getOpenClawDir(), OPENCLAW_ENTRY_FILE_NAME); } export function getOpenClawNodeModulesDir(): string { return join(getOpenClawDir(), 'node_modules'); } export function getOpenClawBuildDir(): string { return join(app.getAppPath(), 'build', OPENCLAW_PACKAGE_DIR_NAME); } export function normalizeNodeRequirePathForNodeOptions(modulePath: string): string { if (process.platform !== 'win32') { return modulePath; } return modulePath.replace(/\\/g, '/'); } export function appendNodeRequireToNodeOptions( nodeOptions: string | undefined, modulePath: string, ): string { const normalizedPath = normalizeNodeRequirePathForNodeOptions(modulePath); return `${nodeOptions ?? ''} --require "${normalizedPath}"`.trim(); } export function getOpenClawPackageStatus(): { dir: string; entryPath: string; nodeModulesDir: string; packageExists: boolean; entryExists: boolean; nodeModulesExists: boolean; }; export function getOpenClawPackageStatus( overrides?: Partial>, ): { dir: string; entryPath: string; nodeModulesDir: string; packageExists: boolean; entryExists: boolean; nodeModulesExists: boolean; } { const dir = overrides?.dir ?? getOpenClawDir(); const entryPath = overrides?.entryPath ?? join(dir, OPENCLAW_ENTRY_FILE_NAME); const nodeModulesDir = join(dir, 'node_modules'); const entryExists = existsSync(entryPath); const nodeModulesExists = existsSync(nodeModulesDir); return { dir, entryPath, nodeModulesDir, packageExists: existsSync(dir), entryExists, nodeModulesExists, }; } export function getClawHubCliBinPath(): string { const binName = process.platform === 'win32' ? 'clawhub.cmd' : 'clawhub'; return join(app.getAppPath(), 'node_modules', '.bin', binName); } export function getClawHubCliEntryPath(): string { return join(app.getAppPath(), 'node_modules', 'clawhub', 'bin', 'clawdhub.js'); } export function ensureDir(dir: string): string { if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } return dir; } export function ensureOpenClawRuntimeLayout( paths: OpenClawRuntimePaths = getOpenClawRuntimePaths(), ): OpenClawRuntimePaths { ensureDir(paths.configDir); ensureDir(paths.runtimeDir); return paths; } export function getOpenClawRuntimePaths(): OpenClawRuntimePaths { return { configDir: getOpenClawConfigDir(), runtimeDir: getOpenClawRuntimeDir(), dir: getOpenClawDir(), resolvedDir: getOpenClawResolvedDir(), entryPath: getOpenClawEntryPath(), }; }