- Added telemetry utility to capture application events and metrics. - Integrated PostHog for event tracking with distinct user identification. - Implemented telemetry initialization, event capturing, and shutdown procedures. feat: add UV environment setup for Python management - Created utilities to manage Python installation and configuration. - Implemented network optimization checks for Python installation mirrors. - Added functions to set up managed Python environments with error handling. feat: enhance host API communication with token management - Introduced host API token retrieval and management for secure requests. - Updated host API fetch functions to include token in headers. - Added support for creating event sources with authentication. test: add comprehensive tests for gateway protocol and startup helpers - Implemented unit tests for gateway protocol helpers, event dispatching, and state management. - Added tests for startup recovery strategies and process policies. - Ensured coverage for connection monitoring and restart governance logic.
159 lines
4.1 KiB
TypeScript
159 lines
4.1 KiB
TypeScript
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<Pick<OpenClawRuntimePaths, 'dir' | 'entryPath'>>,
|
|
): {
|
|
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(),
|
|
};
|
|
}
|