From 2aaf6fd8af03b59ae891312eb0ad195674f191e9 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Tue, 3 Feb 2026 07:53:17 +0000 Subject: [PATCH] Initial commit: git-agent-sync CLI tool --- .src/config/index.ts | 127 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 .src/config/index.ts diff --git a/.src/config/index.ts b/.src/config/index.ts new file mode 100644 index 0000000..0271d5c --- /dev/null +++ b/.src/config/index.ts @@ -0,0 +1,127 @@ +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 }; + } +}