|
|
@@ -0,0 +1,164 @@
|
|
|
+/**
|
|
|
+ * Repository Manager - Handles Git repository cloning and updates
|
|
|
+ */
|
|
|
+
|
|
|
+import { execSync } from 'child_process';
|
|
|
+import { existsSync, mkdirSync } from 'fs';
|
|
|
+import { dirname } from 'path';
|
|
|
+
|
|
|
+export interface RepositoryInfo {
|
|
|
+ owner: string;
|
|
|
+ repo: string;
|
|
|
+ sshUrl: string;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Fetch repository information from Gogs MCP
|
|
|
+ */
|
|
|
+async function fetchRepositoryInfo(owner: string, repo: string): Promise<RepositoryInfo> {
|
|
|
+ // This will be called from the context where Gogs MCP is already connected
|
|
|
+ // For now, we'll use a simple approach: try to fetch the repo info using the Gogs API
|
|
|
+ // In the actual implementation, this will be provided by the MCP server
|
|
|
+
|
|
|
+ // Since we're running in a context where we can use the mcp__gogs__get_repository tool,
|
|
|
+ // we'll need to make this work within the Agent SDK context
|
|
|
+ // For now, return a placeholder that will be populated by the caller
|
|
|
+ return {
|
|
|
+ owner,
|
|
|
+ repo,
|
|
|
+ sshUrl: '' // Will be populated by the caller
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Clone a Git repository
|
|
|
+ */
|
|
|
+export function cloneRepository(gitUrl: string, targetPath: string): void {
|
|
|
+ console.log(`Repository not found at ${targetPath}, cloning...`);
|
|
|
+
|
|
|
+ // Create parent directory if it doesn't exist
|
|
|
+ const parentDir = dirname(targetPath);
|
|
|
+ if (!existsSync(parentDir)) {
|
|
|
+ mkdirSync(parentDir, { recursive: true });
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ execSync(`git clone "${gitUrl}" "${targetPath}"`, {
|
|
|
+ stdio: 'inherit',
|
|
|
+ encoding: 'utf-8'
|
|
|
+ });
|
|
|
+ console.log('Repository cloned successfully');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to clone repository:', error);
|
|
|
+ throw new Error('Failed to clone repository');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Pull latest changes from a Git repository
|
|
|
+ */
|
|
|
+export function pullRepository(repoPath: string): void {
|
|
|
+ console.log('Repository found, pulling latest changes...');
|
|
|
+
|
|
|
+ try {
|
|
|
+ execSync('git pull', {
|
|
|
+ cwd: repoPath,
|
|
|
+ stdio: 'inherit',
|
|
|
+ encoding: 'utf-8'
|
|
|
+ });
|
|
|
+ console.log('Repository updated successfully');
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('Warning: git pull failed, continuing with existing repository state');
|
|
|
+ console.warn(error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Ensure repository exists and is up to date
|
|
|
+ * Returns the ssh_url from Gogs MCP that should be used
|
|
|
+ */
|
|
|
+export function ensureRepository(repoPath: string, sshUrl: string): void {
|
|
|
+ if (!existsSync(repoPath)) {
|
|
|
+ cloneRepository(sshUrl, repoPath);
|
|
|
+ } else {
|
|
|
+ pullRepository(repoPath);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Setup environment variables for proper execution
|
|
|
+ */
|
|
|
+export function setupEnvironment(): void {
|
|
|
+ // Ensure HOME is set (systemd might not set it)
|
|
|
+ if (!process.env.HOME) {
|
|
|
+ // Try to get HOME from system
|
|
|
+ try {
|
|
|
+ const whoami = execSync('whoami', { encoding: 'utf-8' }).trim();
|
|
|
+ const homeDir = execSync(`getent passwd "${whoami}" | cut -d: -f6`, { encoding: 'utf-8' }).trim();
|
|
|
+ if (homeDir) {
|
|
|
+ process.env.HOME = homeDir;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('Could not determine HOME directory, using /root');
|
|
|
+ process.env.HOME = '/root';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add NODE to PATH
|
|
|
+ const localBin = `${process.env.HOME}/.local/bin`;
|
|
|
+ if (!process.env.PATH?.includes(localBin)) {
|
|
|
+ process.env.PATH = `${localBin}:${process.env.PATH || ''}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ensure XDG directories are set
|
|
|
+ process.env.XDG_CACHE_HOME = process.env.XDG_CACHE_HOME || `${process.env.HOME}/.cache`;
|
|
|
+ process.env.XDG_CONFIG_HOME = process.env.XDG_CONFIG_HOME || `${process.env.HOME}/.config`;
|
|
|
+ process.env.XDG_DATA_HOME = process.env.XDG_DATA_HOME || `${process.env.HOME}/.local/share`;
|
|
|
+ process.env.XDG_STATE_HOME = process.env.XDG_STATE_HOME || `${process.env.HOME}/.local/state`;
|
|
|
+
|
|
|
+ // Create XDG directories if they don't exist
|
|
|
+ const xdgDirs = [
|
|
|
+ process.env.XDG_CACHE_HOME,
|
|
|
+ process.env.XDG_CONFIG_HOME,
|
|
|
+ process.env.XDG_DATA_HOME,
|
|
|
+ process.env.XDG_STATE_HOME
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const dir of xdgDirs) {
|
|
|
+ if (dir && !existsSync(dir)) {
|
|
|
+ try {
|
|
|
+ mkdirSync(dir, { recursive: true });
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(`Warning: Could not create directory ${dir}:`, error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Detect Agent Manager path
|
|
|
+ */
|
|
|
+export function detectAgentManagerPath(): string {
|
|
|
+ // Try multiple possible locations for the agent-manager .env file
|
|
|
+ const possiblePaths = [
|
|
|
+ '/home/claude/agent-manager',
|
|
|
+ '/data/agent-manager',
|
|
|
+ `${process.env.HOME}/agent-manager`
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const path of possiblePaths) {
|
|
|
+ const envPath = `${path}/.env`;
|
|
|
+ if (existsSync(envPath)) {
|
|
|
+ console.log(`Found agent-manager at: ${path}`);
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Fallback: try to detect from current file location
|
|
|
+ // This file is in client/src/, so agent-manager root is ../../
|
|
|
+ const scriptDir = dirname(new URL(import.meta.url).pathname);
|
|
|
+ const agentManagerPath = dirname(dirname(scriptDir));
|
|
|
+ console.log(`Using detected path from script location: ${agentManagerPath}`);
|
|
|
+
|
|
|
+ return agentManagerPath;
|
|
|
+}
|