import { z } from 'zod'; import * as fs from 'fs-extra'; import * as path from 'path'; import * as os from 'os'; import { GlobalConfig, WorkspaceConfig, CreateOptions } from '../types/index'; import { ConfigurationError, GitAgentSyncError } from '../utils/errors'; const GlobalConfigSchema = z.object({ workspacePath: z.string().default('./.agent-workspaces'), defaultBranch: z.string().default('main'), autoInstall: z.boolean().default(true), templates: z.record(z.string()).default({ default: path.join(os.homedir(), '.git-agent-sync', 'templates', 'default') }) }); const WorkspaceConfigSchema = z.object({ agentName: z.string().min(1).max(100), branch: z.string().min(1), createdAt: z.string().datetime(), mainBranch: z.string().default('main'), environment: z.record(z.string()).default({}), customSetupScript: z.string().optional() }); export function validateGlobalConfig(config: unknown): GlobalConfig { const result = GlobalConfigSchema.safeParse(config); if (!result.success) { throw new ConfigurationError('Invalid global configuration', { errors: result.error.errors.map(e => ({ path: e.path.join('.'), message: e.message })) }); } return result.data; } export function validateWorkspaceConfig(config: unknown): WorkspaceConfig { const result = WorkspaceConfigSchema.safeParse(config); if (!result.success) { throw new ConfigurationError('Invalid workspace configuration', { errors: result.error.errors.map(e => ({ path: e.path.join('.'), message: e.message })) }); } return result.data; } export function validateCreateOptions(options: Partial): CreateOptions { return { path: options.path, template: options.template, installDeps: options.installDeps ?? true, environment: options.environment || {} }; } export async function migrateConfig( oldConfig: Record, version: string ): Promise { const migratedConfig: Record = { ...oldConfig }; if (version === '1.0.0') { if (migratedConfig.templates && typeof migratedConfig.templates === 'object') { const templates = migratedConfig.templates as Record; if (templates['default'] === undefined) { templates['default'] = path.join(os.homedir(), '.git-agent-sync', 'templates', 'default'); } } } return validateGlobalConfig(migratedConfig); } export function getConfigVersion(): string { return '1.0.0'; } export async function validateWorkspacePath( basePath: string, workspacePath: string ): Promise<{ valid: boolean; error?: string }> { try { if (!path.isAbsolute(workspacePath)) { return { valid: false, error: 'Workspace path must be absolute or relative to the git repository' }; } if (!workspacePath.startsWith(basePath)) { return { valid: false, error: 'Workspace path must be within the git repository' }; } if (await fs.pathExists(workspacePath)) { const stat = await fs.stat(workspacePath); if (!stat.isDirectory()) { return { valid: false, error: 'Workspace path exists but is not a directory' }; } } return { valid: true }; } catch (error) { return { valid: false, error: `Invalid workspace path: ${(error as Error).message}` }; } } export async function checkWorkspacePermissions( workspacePath: string, operations: ('read' | 'write' | 'execute')[] ): Promise<{ allowed: boolean; missing?: string }> { try { for (const op of operations) { if (op === 'read' || op === 'execute') { await fs.access(workspacePath, fs.constants.R_OK); } else if (op === 'write') { await fs.access(workspacePath, fs.constants.W_OK); } } return { allowed: true }; } catch { const missingOps = operations.join(', '); return { allowed: false, missing: missingOps }; } }