jobs
uses node:path
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import type { routeJobs } from "#codebase/tcmts/cli/commands"; import { cyan, dim, green, red, yellow } from "#codebase/tcmts/cli/style"; import { UsageError } from "#codebase/tcmts/cli/ui"; import type { parse } from "@bomb.sh/router"; import fs from "node:fs"; import path_2 from "node:path"; import { DatabaseSync } from "node:sqlite"; export function jobs( cb: Codebase, r: Extract<ReturnType<typeof parse<ReturnType<typeof routeJobs>>>, { method: "execute" }>, ): number { const file = path_2.join(cb.lay.tcmDir, "data", "jobs.sqlite"); if (!fs.existsSync(file)) return ( console.log( dim("no jobs yet (enqueue() from a server function, or every() for recurring ones)"), ), 0 ); const db = new DatabaseSync(file); try { const find = (prefix: string) => { const rows = db.prepare("SELECT id FROM jobs WHERE id LIKE ?").all(`${prefix}%`) as { id: string; }[]; if (rows.length !== 1) throw new UsageError( rows.length ? `'${prefix}' matches ${rows.length} jobs` : `no job '${prefix}'`, ); return rows[0]!.id; }; if (r.route === "/retry") { db.prepare( "UPDATE jobs SET status = 'queued', run_at = ?, attempts = 0, last_error = NULL, finished = NULL WHERE id = ?", ).run(Date.now(), find(r.model.id)); return (console.log("queued again"), 0); } if (r.route === "/cancel") { const c = db .prepare( "UPDATE jobs SET status = 'cancelled', finished = ? WHERE id = ? AND status = 'queued'", ) .run(Date.now(), find(r.model.id)); return ( console.log(c.changes ? "cancelled" : dim("not queued (already running or finished)")), 0 ); } const rows = db .prepare("SELECT * FROM jobs ORDER BY created DESC LIMIT ?") .all(r.model.n) as Record<string, string | number | null>[]; const colour: Record<string, (s: string) => string> = { done: green, failed: red, queued: yellow, running: cyan, cancelled: dim, }; for (const row of rows) { const when = new Date( Number(row.status === "queued" ? row.run_at : (row.finished ?? row.run_at)), ) .toISOString() .replace("T", " ") .slice(0, 19); // a finished job's last error is history; a failed or retrying one's is the news const err = row.last_error && row.status !== "done" ? dim(` ${String(row.last_error).split("\n")[0]}`) : ""; console.log( `${dim(String(row.id).slice(0, 8))} ${(colour[String(row.status)] ?? dim)(String(row.status).padEnd(9))} ${String(row.fn_name).padEnd(24)} ${dim(`${row.attempts}/${row.max_attempts} ${when}`)}${err}`, ); } return 0; } finally { db.close(); } }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.commands.routeJobs
- tcmts.cli.style.cyan
- tcmts.cli.style.dim
- tcmts.cli.style.green
- tcmts.cli.style.red
- tcmts.cli.style.yellow
- tcmts.cli.ui.UsageError