Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Haze <hazeone@users.noreply.github.com>
192 lines
4.3 KiB
TypeScript
192 lines
4.3 KiB
TypeScript
/**
|
|
* Path Utilities
|
|
* Cross-platform path resolution helpers
|
|
*/
|
|
import { app } from 'electron';
|
|
import { join } from 'path';
|
|
import { homedir } from 'os';
|
|
import { existsSync, mkdirSync, readFileSync, realpathSync } from 'fs';
|
|
import { logger } from './logger';
|
|
|
|
export { quoteForCmd, needsWinShell, prepareWinSpawn } from './win-shell';
|
|
|
|
/**
|
|
* Expand ~ to home directory
|
|
*/
|
|
export function expandPath(path: string): string {
|
|
if (path.startsWith('~')) {
|
|
return path.replace('~', homedir());
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* Get OpenClaw config directory
|
|
*/
|
|
export function getOpenClawConfigDir(): string {
|
|
return join(homedir(), '.openclaw');
|
|
}
|
|
|
|
/**
|
|
* Get OpenClaw skills directory
|
|
*/
|
|
export function getOpenClawSkillsDir(): string {
|
|
return join(getOpenClawConfigDir(), 'skills');
|
|
}
|
|
|
|
/**
|
|
* Get ClawX config directory
|
|
*/
|
|
export function getClawXConfigDir(): string {
|
|
return join(homedir(), '.clawx');
|
|
}
|
|
|
|
/**
|
|
* Get ClawX logs directory
|
|
*/
|
|
export function getLogsDir(): string {
|
|
return join(app.getPath('userData'), 'logs');
|
|
}
|
|
|
|
/**
|
|
* Get ClawX data directory
|
|
*/
|
|
export function getDataDir(): string {
|
|
return app.getPath('userData');
|
|
}
|
|
|
|
/**
|
|
* Ensure directory exists
|
|
*/
|
|
export function ensureDir(dir: string): void {
|
|
if (!existsSync(dir)) {
|
|
mkdirSync(dir, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get resources directory (for bundled assets)
|
|
*/
|
|
export function getResourcesDir(): string {
|
|
if (app.isPackaged) {
|
|
return join(process.resourcesPath, 'resources');
|
|
}
|
|
return join(__dirname, '../../resources');
|
|
}
|
|
|
|
/**
|
|
* Get preload script path
|
|
*/
|
|
export function getPreloadPath(): string {
|
|
return join(__dirname, '../preload/index.js');
|
|
}
|
|
|
|
/**
|
|
* Get OpenClaw package directory
|
|
* - Production (packaged): from resources/openclaw (copied by electron-builder extraResources)
|
|
* - Development: from node_modules/openclaw
|
|
*/
|
|
export function getOpenClawDir(): string {
|
|
if (app.isPackaged) {
|
|
return join(process.resourcesPath, 'openclaw');
|
|
}
|
|
// Development: use node_modules/openclaw
|
|
return join(__dirname, '../../node_modules/openclaw');
|
|
}
|
|
|
|
/**
|
|
* Get OpenClaw package directory resolved to a real path.
|
|
* Useful when consumers need deterministic module resolution under pnpm symlinks.
|
|
*/
|
|
export function getOpenClawResolvedDir(): string {
|
|
const dir = getOpenClawDir();
|
|
if (!existsSync(dir)) {
|
|
return dir;
|
|
}
|
|
try {
|
|
return realpathSync(dir);
|
|
} catch {
|
|
return dir;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get OpenClaw entry script path (openclaw.mjs)
|
|
*/
|
|
export function getOpenClawEntryPath(): string {
|
|
return join(getOpenClawDir(), 'openclaw.mjs');
|
|
}
|
|
|
|
/**
|
|
* Get ClawHub CLI entry script path (clawdhub.js)
|
|
*/
|
|
export function getClawHubCliEntryPath(): string {
|
|
return join(app.getAppPath(), 'node_modules', 'clawhub', 'bin', 'clawdhub.js');
|
|
}
|
|
|
|
/**
|
|
* Get ClawHub CLI binary path (node_modules/.bin)
|
|
*/
|
|
export function getClawHubCliBinPath(): string {
|
|
const binName = process.platform === 'win32' ? 'clawhub.cmd' : 'clawhub';
|
|
return join(app.getAppPath(), 'node_modules', '.bin', binName);
|
|
}
|
|
|
|
/**
|
|
* Check if OpenClaw package exists
|
|
*/
|
|
export function isOpenClawPresent(): boolean {
|
|
const dir = getOpenClawDir();
|
|
const pkgJsonPath = join(dir, 'package.json');
|
|
return existsSync(dir) && existsSync(pkgJsonPath);
|
|
}
|
|
|
|
/**
|
|
* Check if OpenClaw is built (has dist folder)
|
|
* For the npm package, this should always be true since npm publishes the built dist.
|
|
*/
|
|
export function isOpenClawBuilt(): boolean {
|
|
const dir = getOpenClawDir();
|
|
const distDir = join(dir, 'dist');
|
|
const hasDist = existsSync(distDir);
|
|
return hasDist;
|
|
}
|
|
|
|
/**
|
|
* Get OpenClaw status for environment check
|
|
*/
|
|
export interface OpenClawStatus {
|
|
packageExists: boolean;
|
|
isBuilt: boolean;
|
|
entryPath: string;
|
|
dir: string;
|
|
version?: string;
|
|
}
|
|
|
|
export function getOpenClawStatus(): OpenClawStatus {
|
|
const dir = getOpenClawDir();
|
|
let version: string | undefined;
|
|
|
|
// Try to read version from package.json
|
|
try {
|
|
const pkgPath = join(dir, 'package.json');
|
|
if (existsSync(pkgPath)) {
|
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
version = pkg.version;
|
|
}
|
|
} catch {
|
|
// Ignore version read errors
|
|
}
|
|
|
|
const status: OpenClawStatus = {
|
|
packageExists: isOpenClawPresent(),
|
|
isBuilt: isOpenClawBuilt(),
|
|
entryPath: getOpenClawEntryPath(),
|
|
dir,
|
|
version,
|
|
};
|
|
|
|
logger.info('OpenClaw status:', status);
|
|
return status;
|
|
}
|