Add workspaces, computers, bot memory and the bots queue
- Schema: workspaces (+ workspace_bots, workspace_computers), containers, computers, bot_computers, bots.memory, workspaces.system_prompt, and bots_queue as a bun-sqlite isQueue table - Main computer (and its container record) ensured with the coordinator on signup, seed and /admin/bots; the coordinator's access to it is locked - Effective computer access: direct grants plus workspace computers - Full bot prompt: core instructions, workspace prompts, then memory; bots can rewrite their memory and system prompt - Queue helpers on the native BunSQLite.queue API, plus a handler that heartbeats, serializes per computer, recovers after restarts, reports lost jobs, supports cancelling and pushes outcomes over the WebSocket - Enable SQLite foreign keys at startup so schema cascades fire - Prune finished jobs daily Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import bunext from "@moduletrace/bunext";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import claimBotJobs from "@/src/functions/backend/db/bots-queue/claim-bot-jobs";
|
||||
import releaseWorkerBotJobs from "@/src/functions/backend/db/bots-queue/release-worker-bot-jobs";
|
||||
import { logError } from "@/src/functions/backend/logger";
|
||||
import processBotJob from "./process-bot-job";
|
||||
import reportLostBotJobs from "./report-lost-bot-jobs";
|
||||
|
||||
const worker = AppData.BotQueueWorker;
|
||||
let running = 0;
|
||||
|
||||
/**
|
||||
* Starts the `bots_queue` handler: releases jobs a restart cut off, then
|
||||
* polls every `AppData.BotQueuePollMs`, reporting lost jobs and claiming
|
||||
* up to `AppData.BotQueueConcurrency` at once. Gated in production to the
|
||||
* `MAIN_SERVER` container, like cron.
|
||||
*/
|
||||
export default async function botsQueue() {
|
||||
if (process.env.NODE_ENV == "production" && !process.env.MAIN_SERVER) {
|
||||
return;
|
||||
}
|
||||
|
||||
bunext.bunextLog.watch(`Running Bots Queue ...`);
|
||||
|
||||
const released = await releaseWorkerBotJobs({ worker });
|
||||
|
||||
if (released) {
|
||||
bunext.bunextLog.info(`Bots queue: released ${released} cut-off job(s)`);
|
||||
}
|
||||
|
||||
poll();
|
||||
}
|
||||
|
||||
async function poll() {
|
||||
try {
|
||||
await reportLostBotJobs();
|
||||
|
||||
const free = AppData.BotQueueConcurrency - running;
|
||||
|
||||
if (free > 0) {
|
||||
const jobs = await claimBotJobs({ worker, limit: free });
|
||||
|
||||
for (const job of jobs) {
|
||||
running++;
|
||||
|
||||
processBotJob({ job, worker })
|
||||
.catch((error) =>
|
||||
logError({
|
||||
title: `Bot job #${job.id} crashed`,
|
||||
error,
|
||||
}).catch(() => {}),
|
||||
)
|
||||
.finally(() => {
|
||||
running--;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
await logError({ title: "Bots queue poll failed", error }).catch(
|
||||
() => {},
|
||||
);
|
||||
} finally {
|
||||
setTimeout(poll, AppData.BotQueuePollMs);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user