import { existsSync } from 'node:fs'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { homedir } from 'node:os'; import { dirname, resolve } from 'node:path'; import { randomBytes } from 'node:crypto'; export type CompanionConfig = { accessKey: string; workspaceRoot: string; }; export function getConfigPath(): string { return process.env.HERMES_MOBILE_CONFIG ?? resolve(homedir(), '.config/hermes-mobile/companion.json'); } export function getWorkspaceRoot(): string { return resolve(process.env.HERMES_MOBILE_WORKSPACE_ROOT ?? process.cwd()); } export function generateAccessKey(): string { return `hm_${randomBytes(24).toString('base64url')}`; } export async function readCompanionConfig(): Promise> { const configPath = getConfigPath(); if (!existsSync(configPath)) { return {}; } const rawConfig = await readFile(configPath, 'utf8'); const parsedConfig = JSON.parse(rawConfig) as Partial; return parsedConfig; } export async function writeCompanionConfig(config: CompanionConfig): Promise { const configPath = getConfigPath(); await mkdir(dirname(configPath), { recursive: true }); await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 }); } export async function ensureCompanionConfig(): Promise { const existingConfig = await readCompanionConfig(); const config: CompanionConfig = { accessKey: existingConfig.accessKey || process.env.HERMES_MOBILE_ACCESS_KEY || generateAccessKey(), workspaceRoot: resolve(existingConfig.workspaceRoot || getWorkspaceRoot()), }; await writeCompanionConfig(config); return config; } export async function getRuntimeConfig(): Promise { const existingConfig = await readCompanionConfig(); return { accessKey: process.env.HERMES_MOBILE_ACCESS_KEY || existingConfig.accessKey || '', workspaceRoot: resolve(process.env.HERMES_MOBILE_WORKSPACE_ROOT || existingConfig.workspaceRoot || process.cwd()), }; }