All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 42s
The getAgents() function was recalculating nextRun to future dates when it found past values. This prevented the cron scheduler from ever finding due agents (nextRun <= now was always false since getAgents had already pushed it to the future). Also fix toast polling: use null sentinel for agents without initial actions so first execution is still detected. Limit cron to 3 agents per cycle and add logging. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
288 lines
8.1 KiB
TypeScript
288 lines
8.1 KiB
TypeScript
'use server'
|
|
|
|
/**
|
|
* Agent Server Actions
|
|
* CRUD operations for agents and execution triggers.
|
|
*/
|
|
|
|
import { auth } from '@/auth'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { revalidatePath } from 'next/cache'
|
|
import { calculateNextRun } from '@/lib/agents/schedule'
|
|
|
|
// --- CRUD ---
|
|
|
|
export async function createAgent(data: {
|
|
name: string
|
|
description?: string
|
|
type: string
|
|
role: string
|
|
sourceUrls?: string[]
|
|
sourceNotebookId?: string
|
|
targetNotebookId?: string
|
|
frequency?: string
|
|
tools?: string[]
|
|
maxSteps?: number
|
|
notifyEmail?: boolean
|
|
includeImages?: boolean
|
|
scheduledTime?: string
|
|
scheduledDay?: number
|
|
timezone?: string
|
|
}) {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
throw new Error('Non autorise')
|
|
}
|
|
|
|
try {
|
|
const agent = await prisma.agent.create({
|
|
data: {
|
|
name: data.name,
|
|
description: data.description,
|
|
type: data.type,
|
|
role: data.role,
|
|
sourceUrls: data.sourceUrls ? JSON.stringify(data.sourceUrls) : null,
|
|
sourceNotebookId: data.sourceNotebookId || null,
|
|
targetNotebookId: data.targetNotebookId || null,
|
|
frequency: data.frequency || 'manual',
|
|
tools: data.tools ? JSON.stringify(data.tools) : '[]',
|
|
maxSteps: data.maxSteps || 10,
|
|
notifyEmail: data.notifyEmail || false,
|
|
includeImages: data.includeImages || false,
|
|
scheduledTime: data.scheduledTime || '08:00',
|
|
scheduledDay: data.scheduledDay ?? null,
|
|
timezone: data.timezone || null,
|
|
userId: session.user.id,
|
|
}
|
|
})
|
|
|
|
// Calculate nextRun for scheduled agents
|
|
const freq = data.frequency || 'manual'
|
|
if (freq !== 'manual') {
|
|
const nextRun = calculateNextRun({
|
|
frequency: freq,
|
|
scheduledTime: data.scheduledTime || '08:00',
|
|
scheduledDay: data.scheduledDay,
|
|
timezone: data.timezone,
|
|
})
|
|
if (nextRun) {
|
|
await prisma.agent.update({
|
|
where: { id: agent.id },
|
|
data: { nextRun },
|
|
})
|
|
}
|
|
}
|
|
|
|
revalidatePath('/agents')
|
|
return { success: true, agent }
|
|
} catch (error) {
|
|
console.error('Error creating agent:', error)
|
|
throw new Error('Impossible de creer l\'agent')
|
|
}
|
|
}
|
|
|
|
export async function updateAgent(id: string, data: {
|
|
name?: string
|
|
description?: string
|
|
type?: string
|
|
role?: string
|
|
sourceUrls?: string[]
|
|
sourceNotebookId?: string | null
|
|
targetNotebookId?: string | null
|
|
frequency?: string
|
|
isEnabled?: boolean
|
|
tools?: string[]
|
|
maxSteps?: number
|
|
notifyEmail?: boolean
|
|
includeImages?: boolean
|
|
scheduledTime?: string
|
|
scheduledDay?: number | null
|
|
timezone?: string
|
|
}) {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
throw new Error('Non autorise')
|
|
}
|
|
|
|
try {
|
|
const existing = await prisma.agent.findUnique({ where: { id } })
|
|
if (!existing || existing.userId !== session.user.id) {
|
|
throw new Error('Agent non trouve')
|
|
}
|
|
|
|
const updateData: Record<string, unknown> = {}
|
|
if (data.name !== undefined) updateData.name = data.name
|
|
if (data.description !== undefined) updateData.description = data.description
|
|
if (data.type !== undefined) updateData.type = data.type
|
|
if (data.role !== undefined) updateData.role = data.role
|
|
if (data.sourceUrls !== undefined) updateData.sourceUrls = JSON.stringify(data.sourceUrls)
|
|
if (data.sourceNotebookId !== undefined) updateData.sourceNotebookId = data.sourceNotebookId
|
|
if (data.targetNotebookId !== undefined) updateData.targetNotebookId = data.targetNotebookId
|
|
if (data.frequency !== undefined) updateData.frequency = data.frequency
|
|
if (data.isEnabled !== undefined) updateData.isEnabled = data.isEnabled
|
|
if (data.tools !== undefined) updateData.tools = JSON.stringify(data.tools)
|
|
if (data.maxSteps !== undefined) updateData.maxSteps = data.maxSteps
|
|
if (data.notifyEmail !== undefined) updateData.notifyEmail = data.notifyEmail
|
|
if (data.includeImages !== undefined) updateData.includeImages = data.includeImages
|
|
if (data.scheduledTime !== undefined) updateData.scheduledTime = data.scheduledTime
|
|
if (data.scheduledDay !== undefined) updateData.scheduledDay = data.scheduledDay
|
|
if (data.timezone !== undefined) updateData.timezone = data.timezone
|
|
|
|
// Recalculate nextRun when scheduling fields change
|
|
const shouldRecalcNextRun =
|
|
data.frequency !== undefined ||
|
|
data.scheduledTime !== undefined ||
|
|
data.scheduledDay !== undefined ||
|
|
data.timezone !== undefined
|
|
|
|
if (shouldRecalcNextRun) {
|
|
const freq = data.frequency || existing.frequency
|
|
if (freq === 'manual') {
|
|
updateData.nextRun = null
|
|
} else {
|
|
const nextRun = calculateNextRun({
|
|
frequency: freq,
|
|
scheduledTime: data.scheduledTime || existing.scheduledTime || '08:00',
|
|
scheduledDay: data.scheduledDay ?? existing.scheduledDay,
|
|
timezone: data.timezone || existing.timezone,
|
|
})
|
|
updateData.nextRun = nextRun
|
|
}
|
|
}
|
|
|
|
const agent = await prisma.agent.update({
|
|
where: { id },
|
|
data: updateData
|
|
})
|
|
|
|
revalidatePath('/agents')
|
|
return { success: true, agent }
|
|
} catch (error) {
|
|
console.error('Error updating agent:', error)
|
|
throw new Error('Impossible de mettre a jour l\'agent')
|
|
}
|
|
}
|
|
|
|
export async function deleteAgent(id: string) {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
throw new Error('Non autorise')
|
|
}
|
|
|
|
try {
|
|
const existing = await prisma.agent.findUnique({ where: { id } })
|
|
if (!existing || existing.userId !== session.user.id) {
|
|
throw new Error('Agent non trouve')
|
|
}
|
|
|
|
await prisma.agent.delete({ where: { id } })
|
|
revalidatePath('/agents')
|
|
return { success: true }
|
|
} catch (error) {
|
|
console.error('Error deleting agent:', error)
|
|
throw new Error('Impossible de supprimer l\'agent')
|
|
}
|
|
}
|
|
|
|
export async function getAgents() {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
throw new Error('Non autorise')
|
|
}
|
|
|
|
try {
|
|
const agents = await prisma.agent.findMany({
|
|
where: { userId: session.user.id },
|
|
include: {
|
|
_count: { select: { actions: true } },
|
|
actions: {
|
|
orderBy: { createdAt: 'desc' },
|
|
take: 1,
|
|
},
|
|
notebook: {
|
|
select: { id: true, name: true, icon: true }
|
|
}
|
|
},
|
|
orderBy: { createdAt: 'desc' }
|
|
})
|
|
|
|
return agents
|
|
} catch (error) {
|
|
console.error('Error fetching agents:', error)
|
|
throw new Error('Impossible de charger les agents')
|
|
}
|
|
}
|
|
|
|
// --- Execution ---
|
|
|
|
export async function runAgent(id: string) {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
throw new Error('Non autorise')
|
|
}
|
|
|
|
try {
|
|
const { executeAgent } = await import('@/lib/ai/services/agent-executor.service')
|
|
const result = await executeAgent(id, session.user.id)
|
|
revalidatePath('/agents')
|
|
revalidatePath('/')
|
|
return result
|
|
} catch (error) {
|
|
console.error('Error running agent:', error)
|
|
return {
|
|
success: false,
|
|
actionId: '',
|
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- History ---
|
|
|
|
export async function getAgentActions(agentId: string) {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
throw new Error('Non autorise')
|
|
}
|
|
|
|
try {
|
|
const actions = await prisma.agentAction.findMany({
|
|
where: { agentId },
|
|
orderBy: { createdAt: 'desc' },
|
|
take: 20,
|
|
select: {
|
|
id: true,
|
|
status: true,
|
|
result: true,
|
|
log: true,
|
|
input: true,
|
|
toolLog: true,
|
|
tokensUsed: true,
|
|
createdAt: true,
|
|
}
|
|
})
|
|
return actions
|
|
} catch (error) {
|
|
console.error('Error fetching agent actions:', error)
|
|
throw new Error('Impossible de charger l\'historique')
|
|
}
|
|
}
|
|
|
|
export async function toggleAgent(id: string, isEnabled: boolean) {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
throw new Error('Non autorise')
|
|
}
|
|
|
|
try {
|
|
const agent = await prisma.agent.update({
|
|
where: { id },
|
|
data: { isEnabled }
|
|
})
|
|
return { success: true, agent }
|
|
} catch (error) {
|
|
console.error('Error toggling agent:', error)
|
|
throw new Error('Impossible de modifier l\'agent')
|
|
}
|
|
}
|