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>
105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { calculateNextRun } from '@/lib/agents/schedule'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
/**
|
|
* POST /api/cron/agents
|
|
*
|
|
* Finds all enabled, non-manual agents whose nextRun <= now,
|
|
* executes them, and schedules the next run.
|
|
*
|
|
* Optional auth: set CRON_SECRET env var, callers must pass
|
|
* Authorization: Bearer <CRON_SECRET>
|
|
*/
|
|
export async function POST(request: NextRequest) {
|
|
// Optional auth
|
|
const cronSecret = process.env.CRON_SECRET
|
|
if (cronSecret) {
|
|
const authHeader = request.headers.get('authorization')
|
|
if (authHeader !== `Bearer ${cronSecret}`) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
}
|
|
|
|
try {
|
|
const now = new Date()
|
|
|
|
const dueAgents = await prisma.agent.findMany({
|
|
where: {
|
|
isEnabled: true,
|
|
frequency: { not: 'manual' },
|
|
nextRun: { lte: now },
|
|
},
|
|
select: {
|
|
id: true,
|
|
userId: true,
|
|
frequency: true,
|
|
scheduledTime: true,
|
|
scheduledDay: true,
|
|
timezone: true,
|
|
},
|
|
})
|
|
|
|
if (dueAgents.length === 0) {
|
|
return NextResponse.json({ success: true, executed: 0 })
|
|
}
|
|
|
|
console.log(`[CronAgents] Found ${dueAgents.length} due agent(s)`)
|
|
|
|
const results: { id: string; success: boolean; error?: string }[] = []
|
|
|
|
// Execute agents sequentially (max 3 per cycle) to avoid overwhelming the AI provider
|
|
for (const agent of dueAgents.slice(0, 3)) {
|
|
try {
|
|
const { executeAgent } = await import('@/lib/ai/services/agent-executor.service')
|
|
const result = await executeAgent(agent.id, agent.userId)
|
|
|
|
// Calculate and set next run
|
|
const nextRun = calculateNextRun({
|
|
frequency: agent.frequency,
|
|
scheduledTime: agent.scheduledTime,
|
|
scheduledDay: agent.scheduledDay,
|
|
timezone: agent.timezone,
|
|
})
|
|
|
|
await prisma.agent.update({
|
|
where: { id: agent.id },
|
|
data: { nextRun },
|
|
})
|
|
|
|
results.push({ id: agent.id, success: result.success, error: result.error })
|
|
} catch (error) {
|
|
const msg = error instanceof Error ? error.message : 'Unknown error'
|
|
console.error(`[CronAgents] Agent ${agent.id} failed:`, msg)
|
|
results.push({ id: agent.id, success: false, error: msg })
|
|
|
|
// Still schedule next run even on failure
|
|
const nextRun = calculateNextRun({
|
|
frequency: agent.frequency,
|
|
scheduledTime: agent.scheduledTime,
|
|
scheduledDay: agent.scheduledDay,
|
|
timezone: agent.timezone,
|
|
})
|
|
await prisma.agent.update({
|
|
where: { id: agent.id },
|
|
data: { nextRun },
|
|
})
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
executed: results.length,
|
|
results,
|
|
})
|
|
} catch (error) {
|
|
console.error('[CronAgents] Error:', error)
|
|
return NextResponse.json(
|
|
{ success: false, error: 'Internal Server Error' },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|