import * as path from 'path'; import * as fs from 'fs-extra'; import { execa } from 'execa'; import * as os from 'os'; import { CreateOptions } from '../types/index'; import { GitAgentSyncError } from './errors'; import { createEnvironmentFile, readPackageJson } from './file-utils'; const DEFAULT_ENV_TEMPLATE = `# Environment variables for agent workspace AGENT_ID={agentName} WORKSPACE_PATH={workspacePath} AGENT_WORKSPACE={workspacePath} GIT_USER_EMAIL={gitUserEmail} GIT_USER_NAME={gitUserName} `; export async function installDependencies(workspacePath: string): Promise { const pkgJson = await readPackageJson(workspacePath); if (!pkgJson) { return; } const packageManager = detectPackageManager(workspacePath); if (!packageManager) { throw new GitAgentSyncError('No package manager detected (package.json found but no lockfile)'); } const command = packageManager === 'npm' ? 'npm install' : `${packageManager} install`; await execa(command, { cwd: workspacePath, stdio: 'inherit' }); } function detectPackageManager(workspacePath: string): 'npm' | 'yarn' | 'pnpm' | null { if (fs.existsSync(path.join(workspacePath, 'yarn.lock'))) { return 'yarn'; } if (fs.existsSync(path.join(workspacePath, 'pnpm-lock.yaml'))) { return 'pnpm'; } if (fs.existsSync(path.join(workspacePath, 'package-lock.json'))) { return 'npm'; } return null; } export async function setupEnvironment( workspacePath: string, agentName: string, options: CreateOptions ): Promise { const envVars: Record = { AGENT_ID: agentName, WORKSPACE_PATH: workspacePath, AGENT_WORKSPACE: workspacePath, ...(options.environment || {}) }; const templatePath = options.template ? path.resolve(options.template) : undefined; if (templatePath && await fs.pathExists(templatePath)) { await applyEnvironmentTemplate(templatePath, workspacePath, agentName, envVars); } else { await createEnvironmentFile(workspacePath, envVars); } } async function applyEnvironmentTemplate( templatePath: string, workspacePath: string, agentName: string, additionalVars: Record ): Promise { const templateEnvPath = path.join(templatePath, '.env.template'); const templateReadmePath = path.join(templatePath, 'README.md'); if (await fs.pathExists(templateEnvPath)) { const templateContent = await fs.readFile(templateEnvPath, 'utf-8'); const processedContent = processTemplate(templateContent, { agentName, workspacePath, ...additionalVars }); await fs.writeFile(path.join(workspacePath, '.env'), processedContent, 'utf-8'); } else { const defaultContent = DEFAULT_ENV_TEMPLATE .replace(/{agentName}/g, agentName) .replace(/{workspacePath}/g, workspacePath) .replace(/{gitUserEmail}/g, '') .replace(/{gitUserName}/g, ''); await fs.writeFile(path.join(workspacePath, '.env'), defaultContent, 'utf-8'); } if (await fs.pathExists(templateReadmePath)) { const readmeContent = await fs.readFile(templateReadmePath, 'utf-8'); await fs.writeFile(path.join(workspacePath, 'SETUP.md'), readmeContent, 'utf-8'); } const otherFiles = await fs.readdir(templatePath); for (const file of otherFiles) { if (file !== '.env.template' && file !== 'README.md') { const src = path.join(templatePath, file); const dest = path.join(workspacePath, file); if ((await fs.stat(src)).isFile()) { await fs.copy(src, dest); } } } } function processTemplate( content: string, variables: Record ): string { let result = content; for (const [key, value] of Object.entries(variables)) { result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), value); } return result; } export async function runCustomSetupScript( workspacePath: string, scriptPath: string ): Promise { if (!(await fs.pathExists(scriptPath))) { throw new GitAgentSyncError(`Setup script not found: ${scriptPath}`); } const absoluteScriptPath = path.resolve(scriptPath); const extension = path.extname(absoluteScriptPath); if (extension === '.sh') { await execa('bash', [absoluteScriptPath], { cwd: workspacePath, stdio: 'inherit' }); } else if (extension === '.js' || extension === '.mjs') { await execa(process.execPath, [absoluteScriptPath], { cwd: workspacePath, stdio: 'inherit' }); } else { throw new GitAgentSyncError(`Unsupported setup script: ${extension}`); } } export async function getGitUserInfo(): Promise<{ name: string; email: string }> { try { const { stdout: name } = await execa('git', ['config', '--global', 'user.name'], { reject: false }); const { stdout: email } = await execa('git', ['config', '--global', 'user.email'], { reject: false }); return { name: name || os.userInfo().username, email: email || `${os.userInfo().username}@localhost` }; } catch { return { name: os.userInfo().username, email: `${os.userInfo().username}@localhost` }; } } export async function checkCommandAvailability(command: string): Promise { try { await execa(command, ['--version'], { reject: false }); return true; } catch { return false; } } export function generateAgentEnvironment( agentName: string, workspacePath: string, gitUserInfo: { name: string; email: string } ): Record { return { AGENT_ID: agentName, WORKSPACE_PATH: workspacePath, AGENT_WORKSPACE: workspacePath, GIT_USER_EMAIL: gitUserInfo.email, GIT_USER_NAME: gitUserInfo.name, AGENT_CREATED_AT: new Date().toISOString() }; }