import { NextRequest, NextResponse } from 'next/server' import * as fs from 'fs/promises' import * as path from 'path' /** * Agent SOUL File API * * GET - Get SOUL file content * PUT - Update SOUL file content * GET /history - Get version history */ const AGENT_CORE_PATH = process.env.AGENT_CORE_PATH || '/app/agent-core' const SOUL_PATH = path.join(AGENT_CORE_PATH, 'soul') interface SoulVersion { version: string timestamp: string content: string author: string changes: string } // Read SOUL file content async function readSoulFile(agentId: string): Promise { const soulFile = `${agentId}.soul.md` const soulFilePath = path.join(SOUL_PATH, soulFile) try { const content = await fs.readFile(soulFilePath, 'utf-8') return content } catch { return null } } // Get version history from backup directory async function getVersionHistory(agentId: string): Promise { const backupDir = path.join(SOUL_PATH, '.backups') const versions: SoulVersion[] = [] try { const files = await fs.readdir(backupDir) const agentBackups = files.filter(f => f.startsWith(`${agentId}-`) && f.endsWith('.soul.md')) for (const file of agentBackups.slice(-10)) { // Last 10 versions const filePath = path.join(backupDir, file) const stats = await fs.stat(filePath) const content = await fs.readFile(filePath, 'utf-8') // Extract timestamp from filename const match = file.match(/-(\d+)\.soul\.md$/) const timestamp = match ? new Date(parseInt(match[1])).toISOString() : stats.mtime.toISOString() versions.push({ version: file.replace('.soul.md', ''), timestamp, content, author: 'Admin', changes: 'Manual update' }) } // Sort by timestamp descending versions.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) } catch { // No backup directory or can't read } return versions } // GET - Get SOUL file content export async function GET( request: NextRequest, { params }: { params: Promise<{ agentId: string }> } ) { try { const { agentId } = await params const url = new URL(request.url) const includeHistory = url.searchParams.get('history') === 'true' const content = await readSoulFile(agentId) if (!content) { return NextResponse.json( { error: 'SOUL file not found' }, { status: 404 } ) } const soulFile = `${agentId}.soul.md` const soulFilePath = path.join(SOUL_PATH, soulFile) const stats = await fs.stat(soulFilePath) const response: { agentId: string soulFile: string content: string updatedAt: string size: number history?: SoulVersion[] } = { agentId, soulFile, content, updatedAt: stats.mtime.toISOString(), size: stats.size } if (includeHistory) { response.history = await getVersionHistory(agentId) } return NextResponse.json(response) } catch (error) { console.error('Error fetching SOUL file:', error) return NextResponse.json( { error: error instanceof Error ? error.message : 'Failed to fetch SOUL file' }, { status: 500 } ) } } // PUT - Update SOUL file content export async function PUT( request: NextRequest, { params }: { params: Promise<{ agentId: string }> } ) { try { const { agentId } = await params const body = await request.json() const { content, author, changeDescription } = body if (!content) { return NextResponse.json( { error: 'content is required' }, { status: 400 } ) } const soulFile = `${agentId}.soul.md` const soulFilePath = path.join(SOUL_PATH, soulFile) // Check if file exists try { await fs.access(soulFilePath) } catch { return NextResponse.json( { error: 'SOUL file not found' }, { status: 404 } ) } // Create backup before updating const backupDir = path.join(SOUL_PATH, '.backups') const backupPath = path.join(backupDir, `${agentId}-${Date.now()}.soul.md`) try { await fs.mkdir(backupDir, { recursive: true }) const currentContent = await fs.readFile(soulFilePath, 'utf-8') await fs.writeFile(backupPath, currentContent, 'utf-8') } catch (backupError) { console.warn('Failed to create backup:', backupError) } // Write new content await fs.writeFile(soulFilePath, content, 'utf-8') const stats = await fs.stat(soulFilePath) return NextResponse.json({ success: true, message: `SOUL file for ${agentId} updated successfully`, agentId, soulFile, updatedAt: stats.mtime.toISOString(), size: stats.size, author: author || 'Admin', changeDescription: changeDescription || 'Manual update' }) } catch (error) { console.error('Error updating SOUL file:', error) return NextResponse.json( { error: error instanceof Error ? error.message : 'Failed to update SOUL file' }, { status: 500 } ) } }