seedproject-web/api/cli/tasks-next.php
2026-07-24 00:14:28 +00:00

53 lines
1.7 KiB
PHP

<?php
/**
* Atomically claim the oldest queued Web Designer task and print it as JSON.
* Prints an empty JSON object ({}) when the queue is empty. Used by the console
* runner's drain loop (agents/console/server.mjs) — the runner has no DB driver,
* so it shells out to this.
*
* php api/cli/tasks-next.php
*
* The claim is a single conditional UPDATE (status queued -> running) guarded by
* task_id, so two concurrent callers can never claim the same row.
*/
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../config.php';
$pdo = new PDO(
sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', DB_HOST, DB_NAME),
DB_USER,
DB_PASS,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
// Find + claim in a loop: SELECT the next candidate, then a guarded UPDATE.
// If the UPDATE affects 0 rows (someone else took it), try the next candidate.
for ($attempt = 0; $attempt < 20; $attempt++) {
$row = $pdo->query(
"SELECT task_id FROM cja_tasks WHERE status = 'queued'
ORDER BY sort_order ASC, task_id ASC LIMIT 1"
)->fetch();
if (!$row) {
echo "{}";
exit;
}
$id = (int) $row['task_id'];
$upd = $pdo->prepare(
"UPDATE cja_tasks SET status = 'running', started_at = NOW()
WHERE task_id = ? AND status = 'queued'"
);
$upd->execute([$id]);
if ($upd->rowCount() === 1) {
$task = $pdo->prepare('SELECT task_id, title, target_page, prompt, draft, assets FROM cja_tasks WHERE task_id = ?');
$task->execute([$id]);
echo json_encode($task->fetch(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
exit;
}
// lost the race — loop and grab the next one
}
echo "{}";