This commit is contained in:
@@ -1,197 +1,43 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
import { execa } from 'execa';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
|
||||||
import { CreateOptions } from '../types/index';
|
import { CreateOptions } from '../types/index';
|
||||||
import { GitAgentSyncError } from './errors';
|
|
||||||
import { createEnvironmentFile, readPackageJson } from './file-utils';
|
|
||||||
|
|
||||||
const DEFAULT_ENV_TEMPLATE = `# Environment variables for agent workspace
|
export async function setupEnvironment(workspacePath: string, agentName: string, options: CreateOptions): Promise<void> {
|
||||||
AGENT_ID={agentName}
|
await fs.ensureDir(workspacePath);
|
||||||
WORKSPACE_PATH={workspacePath}
|
const envPath = path.join(workspacePath, '.env');
|
||||||
AGENT_WORKSPACE={workspacePath}
|
const envContent = generateEnvContent(agentName, workspacePath, options.environment);
|
||||||
GIT_USER_EMAIL={gitUserEmail}
|
await fs.writeFile(envPath, envContent, 'utf-8');
|
||||||
GIT_USER_NAME={gitUserName}
|
if (options.template) {
|
||||||
`;
|
await applyTemplate(workspacePath, options.template, agentName, workspacePath);
|
||||||
|
|
||||||
export async function installDependencies(workspacePath: string): Promise<void> {
|
|
||||||
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<void> {
|
|
||||||
const envVars: Record<string, string> = {
|
|
||||||
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(
|
function generateEnvContent(agentName: string, workspacePath: string, additionalEnv: Record<string, string> | undefined): string {
|
||||||
templatePath: string,
|
const lines = [`AGENT_ID=${agentName}`, `WORKSPACE_PATH=${workspacePath}`, `AGENT_WORKSPACE=${workspacePath}`];
|
||||||
workspacePath: string,
|
if (additionalEnv) {
|
||||||
agentName: string,
|
for (const [key, value] of Object.entries(additionalEnv)) {
|
||||||
additionalVars: Record<string, string>
|
lines.push(`${key}=${value}`);
|
||||||
): Promise<void> {
|
}
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
if (await fs.pathExists(templateReadmePath)) {
|
async function applyTemplate(workspacePath: string, templatePath: string, agentName: string, workspaceBase: string): Promise<void> {
|
||||||
const readmeContent = await fs.readFile(templateReadmePath, 'utf-8');
|
if (await fs.pathExists(templatePath)) {
|
||||||
await fs.writeFile(path.join(workspacePath, 'SETUP.md'), readmeContent, 'utf-8');
|
const templateFiles = await fs.readdir(templatePath);
|
||||||
}
|
for (const file of templateFiles) {
|
||||||
|
|
||||||
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 src = path.join(templatePath, file);
|
||||||
const dest = path.join(workspacePath, file);
|
const dest = path.join(workspacePath, file.replace('.template', ''));
|
||||||
if ((await fs.stat(src)).isFile()) {
|
if ((await fs.stat(src)).isFile()) {
|
||||||
await fs.copy(src, dest);
|
let content = await fs.readFile(src, 'utf-8');
|
||||||
|
content = content.replace(/\{agentName\}/g, agentName).replace(/\{workspacePath\}/g, workspaceBase).replace(/\{timestamp\}/g, new Date().toISOString());
|
||||||
|
await fs.writeFile(dest, content, 'utf-8');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processTemplate(
|
export async function runCustomSetupScript(workspacePath: string, scriptPath: string): Promise<void> {
|
||||||
content: string,
|
const { execa } = await import('execa');
|
||||||
variables: Record<string, string>
|
await execa(scriptPath, { cwd: workspacePath, shell: true });
|
||||||
): 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<void> {
|
|
||||||
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<boolean> {
|
|
||||||
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<string, string> {
|
|
||||||
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()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user