Files
kxkm_clown/node-engine-queue.js
T
Codex Local cee246a6a9 Refonte globale P0→P9 : sécurité, V2 monorepo TypeScript, tests, tooling
- P0: Correctifs sécurité critiques (injection bash, path traversal, SSRF, rate limiting)
- P1: Audit logging, session pruning, WS validation, CSRF protection
- P2: Ollama SDK migration, admin API deduplication, attachment hardening
- P3: Node Engine store complet (graphs, runs, models, deployments)
- P4: Admin UI Node Engine (éditeur visuel, runs, queue viewer)
- P5: Persona pipeline (sources, feedback, Pharmacius, editorial)
- P6: V2 packages TypeScript (core, auth, chat-domain, persona-domain, node-engine, storage, tui)
- P7: V2 apps (api + worker + web React), ops TUI, migration/rollback scripts
- P8: Training adapters (TRL/Unsloth), sandboxing (subprocess/container), Turborepo, CI
- P9: Tests (102 backend node:test + 33 React Vitest/RTL), code quality review

135 tests, 0 failures. 8 packages + 3 apps V2. 42 modules V1 validés.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 06:15:04 +01:00

121 lines
2.5 KiB
JavaScript

function createNodeEngineQueue({
store,
runner,
maxConcurrency = 1,
onError = () => {},
}) {
const activeRuns = new Map();
let started = false;
let scheduling = false;
let lastRecovery = {
at: null,
recoveredRunIds: [],
};
function getActiveRunIds() {
return Array.from(activeRuns.keys());
}
function getState() {
const queuedRuns = typeof store?.listRunsByStatus === "function"
? store.listRunsByStatus(["queued"], 500)
: [];
return {
started,
maxConcurrency,
activeCount: activeRuns.size,
activeRunIds: getActiveRunIds(),
queuedCount: queuedRuns.length,
queuedRunIds: queuedRuns.map((run) => run.id),
lastRecovery,
};
}
async function pumpQueue() {
if (scheduling) return;
scheduling = true;
try {
while (activeRuns.size < maxConcurrency) {
const nextRun = store.listRunsByStatus(["queued"], 1)[0];
if (!nextRun) break;
if (activeRuns.has(nextRun.id)) break;
runQueued(nextRun.id);
}
} finally {
scheduling = false;
}
}
function scheduleSoon() {
queueMicrotask(() => {
pumpQueue().catch((error) => onError(error));
});
}
function runQueued(runId) {
const work = (async () => {
try {
await runner.executeRun(runId, {
shouldCancel() {
try {
return Boolean(store.getRun(runId).cancelRequestedAt);
} catch {
return false;
}
},
});
} catch (error) {
onError(error);
} finally {
activeRuns.delete(runId);
scheduleSoon();
}
})();
activeRuns.set(runId, work);
return work;
}
function start() {
if (started) return getState();
started = true;
const recoveredRuns = typeof store?.recoverRunnableRuns === "function"
? store.recoverRunnableRuns()
: [];
lastRecovery = {
at: new Date().toISOString(),
recoveredRunIds: recoveredRuns.map((run) => run.id),
};
scheduleSoon();
return getState();
}
function enqueueGraph(graphId, { actor = "admin" } = {}) {
const run = runner.prepareRun(graphId, { actor });
scheduleSoon();
return run;
}
function cancelRun(runId) {
const run = store.requestRunCancel(runId);
if (run.status === "cancelled") scheduleSoon();
return run;
}
return {
start,
enqueueGraph,
cancelRun,
getState,
};
}
module.exports = {
createNodeEngineQueue,
};