Repository Public
1import path from 'node:path';2import os from 'node:os';3import { agentProfile, resolveAgentId, type AgentId } from '@cr/shared';45export interface WorkspaceRoot {6 label: string;7 path: string; // absolute; realpath'd at startup by paths.ts8}9export interface RuntimeConfig {10 dataDir: string;11 socketPath: string;12 token: string;13 workspaceRoots: WorkspaceRoot[];14 /** Agent profile of this instance (`CR_AGENT`, default `claude`). */15 agent: AgentId;16 /**17 * Name or absolute path of the instance's agent CLI (historical field name): `CR_AGENT_BIN`,18 * else `CR_CLAUDE_BIN` (claude profile only), else the profile's default binary name.19 */20 claudeBin: string;21 maxSessions: number;22 execHosts: string[];23 termBufferBytes: number;24 tmuxSocketName: string;25 shell: string;26 logLevel: string;27}2829export function parseWorkspaceRoots(raw: string): WorkspaceRoot[] {30 return raw31 .split(',')32 .map((s) => s.trim())33 .filter(Boolean)34 .map((entry) => {35 const eq = entry.indexOf('=');36 const p = path.resolve(eq > 0 ? entry.slice(eq + 1) : entry);37 const label = eq > 0 ? entry.slice(0, eq) : path.basename(p);38 return { label, path: p };39 });40}4142const nonEmpty = (v: string | undefined): string | undefined => (v !== undefined && v.trim() !== '' ? v : undefined);4344/** `CR_INSTALL_AGENT_HOOKS=1`, or its historical alias `CR_INSTALL_CLAUDE_HOOKS=1` for the claude profile. */45export function agentHooksRequested(agent: AgentId, env: NodeJS.ProcessEnv = process.env): boolean {46 return env.CR_INSTALL_AGENT_HOOKS === '1' || (agent === 'claude' && env.CR_INSTALL_CLAUDE_HOOKS === '1');47}4849export function loadConfig(env: NodeJS.ProcessEnv = process.env): RuntimeConfig {50 const profile = agentProfile(resolveAgentId(env.CR_AGENT));51 const dataDir = path.resolve(env.CR_DATA_DIR ?? path.join(os.homedir(), `${profile.slug}-data`));52 const token = env.CR_RUNTIME_TOKEN ?? '';53 if (token.length < 32) throw new Error('CR_RUNTIME_TOKEN must be set (>= 32 chars)');54 const roots = parseWorkspaceRoots(env.CR_WORKSPACE_ROOTS ?? path.join(os.homedir(), 'workspace'));55 if (roots.length === 0) throw new Error('CR_WORKSPACE_ROOTS is empty');56 return {57 dataDir,58 socketPath: env.CR_RUNTIME_SOCKET ?? path.join(dataDir, 'run', 'runtime.sock'),59 token,60 workspaceRoots: roots,61 agent: profile.id,62 claudeBin: nonEmpty(env.CR_AGENT_BIN) ?? (profile.id === 'claude' ? nonEmpty(env.CR_CLAUDE_BIN) : undefined) ?? profile.defaultBinary,63 maxSessions: Number(env.CR_MAX_SESSIONS ?? 12),64 execHosts: (env.CR_EXEC_HOSTS ?? '').split(',').map((s) => s.trim()).filter(Boolean),65 termBufferBytes: Number(env.CR_TERM_BUFFER_BYTES ?? 4 * 1024 * 1024),66 tmuxSocketName: nonEmpty(env.CR_TMUX_SOCKET) ?? profile.slug,67 shell: env.CR_SHELL ?? env.SHELL ?? '/bin/zsh',68 logLevel: env.CR_LOG_LEVEL ?? 'info',69 };70}71