steroids-api 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/API/src/index.d.ts +10 -0
- package/dist/API/src/index.d.ts.map +1 -0
- package/dist/API/src/index.js +130 -0
- package/dist/API/src/index.js.map +1 -0
- package/dist/API/src/routes/activity.d.ts +7 -0
- package/dist/API/src/routes/activity.d.ts.map +1 -0
- package/dist/API/src/routes/activity.js +252 -0
- package/dist/API/src/routes/activity.js.map +1 -0
- package/dist/API/src/routes/config.d.ts +7 -0
- package/dist/API/src/routes/config.d.ts.map +1 -0
- package/dist/API/src/routes/config.js +521 -0
- package/dist/API/src/routes/config.js.map +1 -0
- package/dist/API/src/routes/health.d.ts +7 -0
- package/dist/API/src/routes/health.d.ts.map +1 -0
- package/dist/API/src/routes/health.js +172 -0
- package/dist/API/src/routes/health.js.map +1 -0
- package/dist/API/src/routes/incidents.d.ts +7 -0
- package/dist/API/src/routes/incidents.d.ts.map +1 -0
- package/dist/API/src/routes/incidents.js +117 -0
- package/dist/API/src/routes/incidents.js.map +1 -0
- package/dist/API/src/routes/projects.d.ts +7 -0
- package/dist/API/src/routes/projects.d.ts.map +1 -0
- package/dist/API/src/routes/projects.js +398 -0
- package/dist/API/src/routes/projects.js.map +1 -0
- package/dist/API/src/routes/runners.d.ts +7 -0
- package/dist/API/src/routes/runners.d.ts.map +1 -0
- package/dist/API/src/routes/runners.js +242 -0
- package/dist/API/src/routes/runners.js.map +1 -0
- package/dist/API/src/routes/tasks.d.ts +7 -0
- package/dist/API/src/routes/tasks.d.ts.map +1 -0
- package/dist/API/src/routes/tasks.js +1007 -0
- package/dist/API/src/routes/tasks.js.map +1 -0
- package/dist/API/src/utils/validation.d.ts +22 -0
- package/dist/API/src/utils/validation.d.ts.map +1 -0
- package/dist/API/src/utils/validation.js +50 -0
- package/dist/API/src/utils/validation.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +184 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/activity.d.ts +7 -0
- package/dist/routes/activity.d.ts.map +1 -0
- package/dist/routes/activity.js +252 -0
- package/dist/routes/activity.js.map +1 -0
- package/dist/routes/config.d.ts +7 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +647 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/credit-alerts.d.ts +2 -0
- package/dist/routes/credit-alerts.d.ts.map +1 -0
- package/dist/routes/credit-alerts.js +97 -0
- package/dist/routes/credit-alerts.js.map +1 -0
- package/dist/routes/health.d.ts +7 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +200 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/incidents.d.ts +7 -0
- package/dist/routes/incidents.d.ts.map +1 -0
- package/dist/routes/incidents.js +117 -0
- package/dist/routes/incidents.js.map +1 -0
- package/dist/routes/projects.d.ts +7 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +643 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/runners.d.ts +7 -0
- package/dist/routes/runners.d.ts.map +1 -0
- package/dist/routes/runners.js +299 -0
- package/dist/routes/runners.js.map +1 -0
- package/dist/routes/skills.d.ts +3 -0
- package/dist/routes/skills.d.ts.map +1 -0
- package/dist/routes/skills.js +109 -0
- package/dist/routes/skills.js.map +1 -0
- package/dist/routes/storage.d.ts +7 -0
- package/dist/routes/storage.d.ts.map +1 -0
- package/dist/routes/storage.js +93 -0
- package/dist/routes/storage.js.map +1 -0
- package/dist/routes/tasks.d.ts +7 -0
- package/dist/routes/tasks.d.ts.map +1 -0
- package/dist/routes/tasks.js +1145 -0
- package/dist/routes/tasks.js.map +1 -0
- package/dist/src/cleanup/invocation-logs.d.ts +30 -0
- package/dist/src/cleanup/invocation-logs.d.ts.map +1 -0
- package/dist/src/cleanup/invocation-logs.js +66 -0
- package/dist/src/cleanup/invocation-logs.js.map +1 -0
- package/dist/src/commands/loop-phases.d.ts +11 -0
- package/dist/src/commands/loop-phases.d.ts.map +1 -0
- package/dist/src/commands/loop-phases.js +304 -0
- package/dist/src/commands/loop-phases.js.map +1 -0
- package/dist/src/config/loader.d.ts +160 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +276 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/database/connection.d.ts +35 -0
- package/dist/src/database/connection.d.ts.map +1 -0
- package/dist/src/database/connection.js +197 -0
- package/dist/src/database/connection.js.map +1 -0
- package/dist/src/database/queries.d.ts +220 -0
- package/dist/src/database/queries.d.ts.map +1 -0
- package/dist/src/database/queries.js +589 -0
- package/dist/src/database/queries.js.map +1 -0
- package/dist/src/database/schema.d.ts +8 -0
- package/dist/src/database/schema.d.ts.map +1 -0
- package/dist/src/database/schema.js +184 -0
- package/dist/src/database/schema.js.map +1 -0
- package/dist/src/git/push.d.ts +26 -0
- package/dist/src/git/push.d.ts.map +1 -0
- package/dist/src/git/push.js +91 -0
- package/dist/src/git/push.js.map +1 -0
- package/dist/src/git/status.d.ts +83 -0
- package/dist/src/git/status.d.ts.map +1 -0
- package/dist/src/git/status.js +315 -0
- package/dist/src/git/status.js.map +1 -0
- package/dist/src/health/stuck-task-detector.d.ts +131 -0
- package/dist/src/health/stuck-task-detector.d.ts.map +1 -0
- package/dist/src/health/stuck-task-detector.js +233 -0
- package/dist/src/health/stuck-task-detector.js.map +1 -0
- package/dist/src/health/stuck-task-recovery.d.ts +45 -0
- package/dist/src/health/stuck-task-recovery.d.ts.map +1 -0
- package/dist/src/health/stuck-task-recovery.js +309 -0
- package/dist/src/health/stuck-task-recovery.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +130 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/locking/queries.d.ts +116 -0
- package/dist/src/locking/queries.d.ts.map +1 -0
- package/dist/src/locking/queries.js +232 -0
- package/dist/src/locking/queries.js.map +1 -0
- package/dist/src/locking/section-lock.d.ts +74 -0
- package/dist/src/locking/section-lock.d.ts.map +1 -0
- package/dist/src/locking/section-lock.js +196 -0
- package/dist/src/locking/section-lock.js.map +1 -0
- package/dist/src/locking/task-lock.d.ts +92 -0
- package/dist/src/locking/task-lock.d.ts.map +1 -0
- package/dist/src/locking/task-lock.js +233 -0
- package/dist/src/locking/task-lock.js.map +1 -0
- package/dist/src/migrations/index.d.ts +7 -0
- package/dist/src/migrations/index.d.ts.map +1 -0
- package/dist/src/migrations/index.js +9 -0
- package/dist/src/migrations/index.js.map +1 -0
- package/dist/src/migrations/manifest.d.ts +92 -0
- package/dist/src/migrations/manifest.d.ts.map +1 -0
- package/dist/src/migrations/manifest.js +255 -0
- package/dist/src/migrations/manifest.js.map +1 -0
- package/dist/src/migrations/runner.d.ts +84 -0
- package/dist/src/migrations/runner.d.ts.map +1 -0
- package/dist/src/migrations/runner.js +338 -0
- package/dist/src/migrations/runner.js.map +1 -0
- package/dist/src/orchestrator/coder.d.ts +32 -0
- package/dist/src/orchestrator/coder.d.ts.map +1 -0
- package/dist/src/orchestrator/coder.js +170 -0
- package/dist/src/orchestrator/coder.js.map +1 -0
- package/dist/src/orchestrator/coordinator.d.ts +28 -0
- package/dist/src/orchestrator/coordinator.d.ts.map +1 -0
- package/dist/src/orchestrator/coordinator.js +252 -0
- package/dist/src/orchestrator/coordinator.js.map +1 -0
- package/dist/src/orchestrator/fallback-handler.d.ts +24 -0
- package/dist/src/orchestrator/fallback-handler.d.ts.map +1 -0
- package/dist/src/orchestrator/fallback-handler.js +280 -0
- package/dist/src/orchestrator/fallback-handler.js.map +1 -0
- package/dist/src/orchestrator/invoke.d.ts +14 -0
- package/dist/src/orchestrator/invoke.d.ts.map +1 -0
- package/dist/src/orchestrator/invoke.js +76 -0
- package/dist/src/orchestrator/invoke.js.map +1 -0
- package/dist/src/orchestrator/post-coder.d.ts +10 -0
- package/dist/src/orchestrator/post-coder.d.ts.map +1 -0
- package/dist/src/orchestrator/post-coder.js +198 -0
- package/dist/src/orchestrator/post-coder.js.map +1 -0
- package/dist/src/orchestrator/post-reviewer.d.ts +10 -0
- package/dist/src/orchestrator/post-reviewer.d.ts.map +1 -0
- package/dist/src/orchestrator/post-reviewer.js +199 -0
- package/dist/src/orchestrator/post-reviewer.js.map +1 -0
- package/dist/src/orchestrator/reviewer.d.ts +35 -0
- package/dist/src/orchestrator/reviewer.d.ts.map +1 -0
- package/dist/src/orchestrator/reviewer.js +237 -0
- package/dist/src/orchestrator/reviewer.js.map +1 -0
- package/dist/src/orchestrator/schemas.d.ts +10 -0
- package/dist/src/orchestrator/schemas.d.ts.map +1 -0
- package/dist/src/orchestrator/schemas.js +81 -0
- package/dist/src/orchestrator/schemas.js.map +1 -0
- package/dist/src/orchestrator/task-selector.d.ts +102 -0
- package/dist/src/orchestrator/task-selector.d.ts.map +1 -0
- package/dist/src/orchestrator/task-selector.js +326 -0
- package/dist/src/orchestrator/task-selector.js.map +1 -0
- package/dist/src/orchestrator/types.d.ts +74 -0
- package/dist/src/orchestrator/types.d.ts.map +1 -0
- package/dist/src/orchestrator/types.js +5 -0
- package/dist/src/orchestrator/types.js.map +1 -0
- package/dist/src/prompts/coder.d.ts +36 -0
- package/dist/src/prompts/coder.d.ts.map +1 -0
- package/dist/src/prompts/coder.js +303 -0
- package/dist/src/prompts/coder.js.map +1 -0
- package/dist/src/prompts/prompt-helpers.d.ts +51 -0
- package/dist/src/prompts/prompt-helpers.d.ts.map +1 -0
- package/dist/src/prompts/prompt-helpers.js +299 -0
- package/dist/src/prompts/prompt-helpers.js.map +1 -0
- package/dist/src/prompts/reviewer.d.ts +40 -0
- package/dist/src/prompts/reviewer.d.ts.map +1 -0
- package/dist/src/prompts/reviewer.js +416 -0
- package/dist/src/prompts/reviewer.js.map +1 -0
- package/dist/src/providers/claude.d.ts +53 -0
- package/dist/src/providers/claude.d.ts.map +1 -0
- package/dist/src/providers/claude.js +227 -0
- package/dist/src/providers/claude.js.map +1 -0
- package/dist/src/providers/codex.d.ts +53 -0
- package/dist/src/providers/codex.d.ts.map +1 -0
- package/dist/src/providers/codex.js +253 -0
- package/dist/src/providers/codex.js.map +1 -0
- package/dist/src/providers/gemini.d.ts +58 -0
- package/dist/src/providers/gemini.d.ts.map +1 -0
- package/dist/src/providers/gemini.js +240 -0
- package/dist/src/providers/gemini.js.map +1 -0
- package/dist/src/providers/interface.d.ts +185 -0
- package/dist/src/providers/interface.d.ts.map +1 -0
- package/dist/src/providers/interface.js +92 -0
- package/dist/src/providers/interface.js.map +1 -0
- package/dist/src/providers/invocation-logger.d.ts +97 -0
- package/dist/src/providers/invocation-logger.d.ts.map +1 -0
- package/dist/src/providers/invocation-logger.js +378 -0
- package/dist/src/providers/invocation-logger.js.map +1 -0
- package/dist/src/providers/openai.d.ts +53 -0
- package/dist/src/providers/openai.d.ts.map +1 -0
- package/dist/src/providers/openai.js +230 -0
- package/dist/src/providers/openai.js.map +1 -0
- package/dist/src/providers/registry.d.ts +100 -0
- package/dist/src/providers/registry.d.ts.map +1 -0
- package/dist/src/providers/registry.js +170 -0
- package/dist/src/providers/registry.js.map +1 -0
- package/dist/src/routes/activity.d.ts +7 -0
- package/dist/src/routes/activity.d.ts.map +1 -0
- package/dist/src/routes/activity.js +252 -0
- package/dist/src/routes/activity.js.map +1 -0
- package/dist/src/routes/config.d.ts +7 -0
- package/dist/src/routes/config.d.ts.map +1 -0
- package/dist/src/routes/config.js +521 -0
- package/dist/src/routes/config.js.map +1 -0
- package/dist/src/routes/health.d.ts +7 -0
- package/dist/src/routes/health.d.ts.map +1 -0
- package/dist/src/routes/health.js +172 -0
- package/dist/src/routes/health.js.map +1 -0
- package/dist/src/routes/incidents.d.ts +7 -0
- package/dist/src/routes/incidents.d.ts.map +1 -0
- package/dist/src/routes/incidents.js +117 -0
- package/dist/src/routes/incidents.js.map +1 -0
- package/dist/src/routes/projects.d.ts +7 -0
- package/dist/src/routes/projects.d.ts.map +1 -0
- package/dist/src/routes/projects.js +398 -0
- package/dist/src/routes/projects.js.map +1 -0
- package/dist/src/routes/runners.d.ts +7 -0
- package/dist/src/routes/runners.d.ts.map +1 -0
- package/dist/src/routes/runners.js +242 -0
- package/dist/src/routes/runners.js.map +1 -0
- package/dist/src/routes/tasks.d.ts +7 -0
- package/dist/src/routes/tasks.d.ts.map +1 -0
- package/dist/src/routes/tasks.js +1007 -0
- package/dist/src/routes/tasks.js.map +1 -0
- package/dist/src/runners/activity-log.d.ts +65 -0
- package/dist/src/runners/activity-log.d.ts.map +1 -0
- package/dist/src/runners/activity-log.js +140 -0
- package/dist/src/runners/activity-log.js.map +1 -0
- package/dist/src/runners/cron.d.ts +30 -0
- package/dist/src/runners/cron.d.ts.map +1 -0
- package/dist/src/runners/cron.js +333 -0
- package/dist/src/runners/cron.js.map +1 -0
- package/dist/src/runners/daemon.d.ts +71 -0
- package/dist/src/runners/daemon.d.ts.map +1 -0
- package/dist/src/runners/daemon.js +233 -0
- package/dist/src/runners/daemon.js.map +1 -0
- package/dist/src/runners/global-db.d.ts +31 -0
- package/dist/src/runners/global-db.d.ts.map +1 -0
- package/dist/src/runners/global-db.js +220 -0
- package/dist/src/runners/global-db.js.map +1 -0
- package/dist/src/runners/hang-detector.d.ts +38 -0
- package/dist/src/runners/hang-detector.d.ts.map +1 -0
- package/dist/src/runners/hang-detector.js +130 -0
- package/dist/src/runners/hang-detector.js.map +1 -0
- package/dist/src/runners/heartbeat.d.ts +39 -0
- package/dist/src/runners/heartbeat.d.ts.map +1 -0
- package/dist/src/runners/heartbeat.js +71 -0
- package/dist/src/runners/heartbeat.js.map +1 -0
- package/dist/src/runners/lock.d.ts +47 -0
- package/dist/src/runners/lock.d.ts.map +1 -0
- package/dist/src/runners/lock.js +140 -0
- package/dist/src/runners/lock.js.map +1 -0
- package/dist/src/runners/orchestrator-loop.d.ts +20 -0
- package/dist/src/runners/orchestrator-loop.d.ts.map +1 -0
- package/dist/src/runners/orchestrator-loop.js +208 -0
- package/dist/src/runners/orchestrator-loop.js.map +1 -0
- package/dist/src/runners/projects.d.ts +96 -0
- package/dist/src/runners/projects.d.ts.map +1 -0
- package/dist/src/runners/projects.js +243 -0
- package/dist/src/runners/projects.js.map +1 -0
- package/dist/src/runners/wakeup.d.ts +37 -0
- package/dist/src/runners/wakeup.d.ts.map +1 -0
- package/dist/src/runners/wakeup.js +355 -0
- package/dist/src/runners/wakeup.js.map +1 -0
- package/dist/src/utils/validation.d.ts +22 -0
- package/dist/src/utils/validation.d.ts.map +1 -0
- package/dist/src/utils/validation.js +50 -0
- package/dist/src/utils/validation.js.map +1 -0
- package/dist/utils/sqlite.d.ts +17 -0
- package/dist/utils/sqlite.d.ts.map +1 -0
- package/dist/utils/sqlite.js +27 -0
- package/dist/utils/sqlite.js.map +1 -0
- package/dist/utils/storage-cache.d.ts +33 -0
- package/dist/utils/storage-cache.d.ts.map +1 -0
- package/dist/utils/storage-cache.js +81 -0
- package/dist/utils/storage-cache.js.map +1 -0
- package/dist/utils/validation.d.ts +22 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +51 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +39 -0
- package/src/index.ts +199 -0
- package/src/routes/activity.ts +302 -0
- package/src/routes/config.ts +723 -0
- package/src/routes/credit-alerts.ts +73 -0
- package/src/routes/health.ts +219 -0
- package/src/routes/incidents.ts +131 -0
- package/src/routes/projects.ts +854 -0
- package/src/routes/runners.ts +357 -0
- package/src/routes/skills.ts +127 -0
- package/src/routes/storage.ts +108 -0
- package/src/routes/tasks.ts +1372 -0
- package/src/utils/sqlite.ts +36 -0
- package/src/utils/storage-cache.ts +107 -0
- package/src/utils/validation.ts +61 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Steroids API Server
|
|
3
|
+
* REST API for multi-project monitoring and management
|
|
4
|
+
*/
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import cors from 'cors';
|
|
7
|
+
import { realpathSync } from 'node:fs';
|
|
8
|
+
import { resolve } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import projectsRouter from './routes/projects.js';
|
|
11
|
+
import activityRouter from './routes/activity.js';
|
|
12
|
+
import runnersRouter from './routes/runners.js';
|
|
13
|
+
import tasksRouter from './routes/tasks.js';
|
|
14
|
+
import configRouter from './routes/config.js';
|
|
15
|
+
import healthRouter from './routes/health.js';
|
|
16
|
+
import incidentsRouter from './routes/incidents.js';
|
|
17
|
+
const PORT = process.env.PORT || 3501;
|
|
18
|
+
const HOST = process.env.HOST || '0.0.0.0';
|
|
19
|
+
export function createApp() {
|
|
20
|
+
const app = express();
|
|
21
|
+
// Middleware - CORS allowing all origins for local network access
|
|
22
|
+
app.use(cors({
|
|
23
|
+
origin: true, // Allow all origins
|
|
24
|
+
credentials: true,
|
|
25
|
+
}));
|
|
26
|
+
app.use(express.json());
|
|
27
|
+
// Request logging (keep test output clean)
|
|
28
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
29
|
+
app.use((req, res, next) => {
|
|
30
|
+
console.log(`${new Date().toISOString()} ${req.method} ${req.path}`);
|
|
31
|
+
next();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// Routes
|
|
35
|
+
app.use('/api', projectsRouter);
|
|
36
|
+
app.use('/api', activityRouter);
|
|
37
|
+
app.use('/api', runnersRouter);
|
|
38
|
+
app.use('/api', tasksRouter);
|
|
39
|
+
app.use('/api', configRouter);
|
|
40
|
+
app.use('/api', healthRouter);
|
|
41
|
+
app.use('/api', incidentsRouter);
|
|
42
|
+
// Health check
|
|
43
|
+
app.get('/health', (req, res) => {
|
|
44
|
+
res.json({
|
|
45
|
+
status: 'ok',
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
version: '0.4.2',
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
// Root endpoint
|
|
51
|
+
app.get('/', (req, res) => {
|
|
52
|
+
res.json({
|
|
53
|
+
name: 'Steroids API',
|
|
54
|
+
version: '0.4.2',
|
|
55
|
+
endpoints: [
|
|
56
|
+
'GET /health',
|
|
57
|
+
'GET /api/projects',
|
|
58
|
+
'GET /api/projects/status?path=<path>',
|
|
59
|
+
'GET /api/projects/<path>/tasks',
|
|
60
|
+
'GET /api/projects/<path>/sections',
|
|
61
|
+
'POST /api/projects',
|
|
62
|
+
'POST /api/projects/remove',
|
|
63
|
+
'POST /api/projects/enable',
|
|
64
|
+
'POST /api/projects/disable',
|
|
65
|
+
'POST /api/projects/prune',
|
|
66
|
+
'GET /api/activity?hours=<hours>&project=<path>',
|
|
67
|
+
'GET /api/runners',
|
|
68
|
+
'GET /api/runners/active-tasks',
|
|
69
|
+
'GET /api/tasks/<taskId>?project=<path>',
|
|
70
|
+
'GET /api/tasks/<taskId>/logs?project=<path>',
|
|
71
|
+
'GET /api/config/schema',
|
|
72
|
+
'GET /api/config/schema/<category>',
|
|
73
|
+
'GET /api/config?scope=global|project|merged&project=<path>',
|
|
74
|
+
'PUT /api/config',
|
|
75
|
+
'GET /api/health?project=<path>',
|
|
76
|
+
'GET /api/incidents?project=<path>&limit=<n>&task=<prefix>&unresolved=<true|false>',
|
|
77
|
+
'GET /api/ai/providers',
|
|
78
|
+
'GET /api/ai/models/<provider>',
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
// 404 handler
|
|
83
|
+
app.use((req, res) => {
|
|
84
|
+
res.status(404).json({
|
|
85
|
+
success: false,
|
|
86
|
+
error: 'Not found',
|
|
87
|
+
path: req.path,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
// Error handler
|
|
91
|
+
app.use((err, req, res, next) => {
|
|
92
|
+
console.error('Unhandled error:', err);
|
|
93
|
+
res.status(500).json({
|
|
94
|
+
success: false,
|
|
95
|
+
error: 'Internal server error',
|
|
96
|
+
message: err.message,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
return app;
|
|
100
|
+
}
|
|
101
|
+
const app = createApp();
|
|
102
|
+
export function startServer() {
|
|
103
|
+
// Start server on all interfaces
|
|
104
|
+
app.listen(Number(PORT), HOST, () => {
|
|
105
|
+
console.log(`Steroids API server listening on ${HOST}:${PORT}`);
|
|
106
|
+
console.log(`Health check: http://localhost:${PORT}/health`);
|
|
107
|
+
console.log(`API docs: http://localhost:${PORT}/`);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function normalizePath(p) {
|
|
111
|
+
try {
|
|
112
|
+
return realpathSync(p);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return resolve(p);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Only start listening when executed as the entrypoint (safe to import in tests).
|
|
119
|
+
// This works for `node dist/.../index.js` and `tsx watch src/index.ts` (tsx keeps the entry file in argv).
|
|
120
|
+
const shouldAutoStart = (() => {
|
|
121
|
+
if (process.env.NODE_ENV === 'test')
|
|
122
|
+
return false;
|
|
123
|
+
const thisPath = normalizePath(fileURLToPath(import.meta.url));
|
|
124
|
+
const argvPaths = process.argv.slice(1).map((a) => normalizePath(a));
|
|
125
|
+
return argvPaths.includes(thisPath);
|
|
126
|
+
})();
|
|
127
|
+
if (shouldAutoStart)
|
|
128
|
+
startServer();
|
|
129
|
+
export default app;
|
|
130
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database queries for task and section lock management
|
|
3
|
+
* All lock operations are atomic using SQLite transactions
|
|
4
|
+
*/
|
|
5
|
+
import type Database from 'better-sqlite3';
|
|
6
|
+
export interface TaskLock {
|
|
7
|
+
task_id: string;
|
|
8
|
+
runner_id: string;
|
|
9
|
+
acquired_at: string;
|
|
10
|
+
expires_at: string;
|
|
11
|
+
heartbeat_at: string;
|
|
12
|
+
}
|
|
13
|
+
export interface SectionLock {
|
|
14
|
+
section_id: string;
|
|
15
|
+
runner_id: string;
|
|
16
|
+
acquired_at: string;
|
|
17
|
+
expires_at: string;
|
|
18
|
+
}
|
|
19
|
+
export interface LockInfo {
|
|
20
|
+
isLocked: boolean;
|
|
21
|
+
lock: TaskLock | null;
|
|
22
|
+
isExpired: boolean;
|
|
23
|
+
isOwnedByUs: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get task lock by task ID
|
|
27
|
+
*/
|
|
28
|
+
export declare function getTaskLock(db: Database.Database, taskId: string): TaskLock | null;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a task lock is expired
|
|
31
|
+
*/
|
|
32
|
+
export declare function isTaskLockExpired(lock: TaskLock): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Get task lock info with ownership and expiry status
|
|
35
|
+
*/
|
|
36
|
+
export declare function getTaskLockInfo(db: Database.Database, taskId: string, runnerId: string): LockInfo;
|
|
37
|
+
/**
|
|
38
|
+
* Try to insert a new task lock (atomic)
|
|
39
|
+
* Returns true if lock was acquired, false if already exists
|
|
40
|
+
*/
|
|
41
|
+
export declare function tryInsertTaskLock(db: Database.Database, taskId: string, runnerId: string, timeoutMinutes: number): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Claim an expired lock atomically
|
|
44
|
+
* Only succeeds if the lock is actually expired at execution time
|
|
45
|
+
*/
|
|
46
|
+
export declare function claimExpiredTaskLock(db: Database.Database, taskId: string, runnerId: string, timeoutMinutes: number): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Release a task lock (only if owned by the specified runner)
|
|
49
|
+
* Returns true if lock was released, false if not owned
|
|
50
|
+
*/
|
|
51
|
+
export declare function releaseTaskLock(db: Database.Database, taskId: string, runnerId: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Force release a task lock (admin operation)
|
|
54
|
+
* Returns true if lock was released
|
|
55
|
+
*/
|
|
56
|
+
export declare function forceReleaseTaskLock(db: Database.Database, taskId: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Update heartbeat for a task lock
|
|
59
|
+
* Returns true if heartbeat was updated, false if lock not found or not owned
|
|
60
|
+
*/
|
|
61
|
+
export declare function updateTaskLockHeartbeat(db: Database.Database, taskId: string, runnerId: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Extend a task lock's expiry time
|
|
64
|
+
* Returns true if extended, false if lock not found or not owned
|
|
65
|
+
*/
|
|
66
|
+
export declare function extendTaskLock(db: Database.Database, taskId: string, runnerId: string, additionalMinutes: number): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* List all task locks
|
|
69
|
+
*/
|
|
70
|
+
export declare function listTaskLocks(db: Database.Database): TaskLock[];
|
|
71
|
+
/**
|
|
72
|
+
* Find all expired task locks
|
|
73
|
+
*/
|
|
74
|
+
export declare function findExpiredTaskLocks(db: Database.Database): TaskLock[];
|
|
75
|
+
/**
|
|
76
|
+
* Delete all expired task locks
|
|
77
|
+
* Returns the number of locks deleted
|
|
78
|
+
*/
|
|
79
|
+
export declare function cleanupExpiredTaskLocks(db: Database.Database): number;
|
|
80
|
+
/**
|
|
81
|
+
* Get section lock by section ID
|
|
82
|
+
*/
|
|
83
|
+
export declare function getSectionLock(db: Database.Database, sectionId: string): SectionLock | null;
|
|
84
|
+
/**
|
|
85
|
+
* Check if a section lock is expired
|
|
86
|
+
*/
|
|
87
|
+
export declare function isSectionLockExpired(lock: SectionLock): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Try to insert a new section lock (atomic)
|
|
90
|
+
*/
|
|
91
|
+
export declare function tryInsertSectionLock(db: Database.Database, sectionId: string, runnerId: string, timeoutMinutes: number): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Claim an expired section lock atomically
|
|
94
|
+
*/
|
|
95
|
+
export declare function claimExpiredSectionLock(db: Database.Database, sectionId: string, runnerId: string, timeoutMinutes: number): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Release a section lock
|
|
98
|
+
*/
|
|
99
|
+
export declare function releaseSectionLock(db: Database.Database, sectionId: string, runnerId: string): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Force release a section lock
|
|
102
|
+
*/
|
|
103
|
+
export declare function forceReleaseSectionLock(db: Database.Database, sectionId: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* List all section locks
|
|
106
|
+
*/
|
|
107
|
+
export declare function listSectionLocks(db: Database.Database): SectionLock[];
|
|
108
|
+
/**
|
|
109
|
+
* Find all expired section locks
|
|
110
|
+
*/
|
|
111
|
+
export declare function findExpiredSectionLocks(db: Database.Database): SectionLock[];
|
|
112
|
+
/**
|
|
113
|
+
* Delete all expired section locks
|
|
114
|
+
*/
|
|
115
|
+
export declare function cleanupExpiredSectionLocks(db: Database.Database): number;
|
|
116
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../../src/locking/queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database queries for task and section lock management
|
|
3
|
+
* All lock operations are atomic using SQLite transactions
|
|
4
|
+
*/
|
|
5
|
+
// ============ Task Lock Queries ============
|
|
6
|
+
/**
|
|
7
|
+
* Get task lock by task ID
|
|
8
|
+
*/
|
|
9
|
+
export function getTaskLock(db, taskId) {
|
|
10
|
+
return db
|
|
11
|
+
.prepare('SELECT * FROM task_locks WHERE task_id = ?')
|
|
12
|
+
.get(taskId);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check if a task lock is expired
|
|
16
|
+
*/
|
|
17
|
+
export function isTaskLockExpired(lock) {
|
|
18
|
+
const expiresAt = new Date(lock.expires_at).getTime();
|
|
19
|
+
return Date.now() > expiresAt;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get task lock info with ownership and expiry status
|
|
23
|
+
*/
|
|
24
|
+
export function getTaskLockInfo(db, taskId, runnerId) {
|
|
25
|
+
const lock = getTaskLock(db, taskId);
|
|
26
|
+
if (!lock) {
|
|
27
|
+
return {
|
|
28
|
+
isLocked: false,
|
|
29
|
+
lock: null,
|
|
30
|
+
isExpired: false,
|
|
31
|
+
isOwnedByUs: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const isExpired = isTaskLockExpired(lock);
|
|
35
|
+
const isOwnedByUs = lock.runner_id === runnerId;
|
|
36
|
+
return {
|
|
37
|
+
isLocked: !isExpired,
|
|
38
|
+
lock,
|
|
39
|
+
isExpired,
|
|
40
|
+
isOwnedByUs,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Try to insert a new task lock (atomic)
|
|
45
|
+
* Returns true if lock was acquired, false if already exists
|
|
46
|
+
*/
|
|
47
|
+
export function tryInsertTaskLock(db, taskId, runnerId, timeoutMinutes) {
|
|
48
|
+
const expiresAt = new Date(Date.now() + timeoutMinutes * 60 * 1000).toISOString();
|
|
49
|
+
try {
|
|
50
|
+
db.prepare(`INSERT INTO task_locks (task_id, runner_id, expires_at)
|
|
51
|
+
VALUES (?, ?, ?)`).run(taskId, runnerId, expiresAt);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// IntegrityError means lock already exists
|
|
56
|
+
if (err instanceof Error && err.message.includes('UNIQUE constraint')) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Claim an expired lock atomically
|
|
64
|
+
* Only succeeds if the lock is actually expired at execution time
|
|
65
|
+
*/
|
|
66
|
+
export function claimExpiredTaskLock(db, taskId, runnerId, timeoutMinutes) {
|
|
67
|
+
const expiresAt = new Date(Date.now() + timeoutMinutes * 60 * 1000).toISOString();
|
|
68
|
+
const result = db.prepare(`UPDATE task_locks
|
|
69
|
+
SET runner_id = ?,
|
|
70
|
+
acquired_at = datetime('now'),
|
|
71
|
+
expires_at = ?,
|
|
72
|
+
heartbeat_at = datetime('now')
|
|
73
|
+
WHERE task_id = ?
|
|
74
|
+
AND expires_at < datetime('now')`).run(runnerId, expiresAt, taskId);
|
|
75
|
+
return result.changes > 0;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Release a task lock (only if owned by the specified runner)
|
|
79
|
+
* Returns true if lock was released, false if not owned
|
|
80
|
+
*/
|
|
81
|
+
export function releaseTaskLock(db, taskId, runnerId) {
|
|
82
|
+
const result = db.prepare(`DELETE FROM task_locks
|
|
83
|
+
WHERE task_id = ?
|
|
84
|
+
AND runner_id = ?`).run(taskId, runnerId);
|
|
85
|
+
return result.changes > 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Force release a task lock (admin operation)
|
|
89
|
+
* Returns true if lock was released
|
|
90
|
+
*/
|
|
91
|
+
export function forceReleaseTaskLock(db, taskId) {
|
|
92
|
+
const result = db.prepare(`DELETE FROM task_locks WHERE task_id = ?`).run(taskId);
|
|
93
|
+
return result.changes > 0;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Update heartbeat for a task lock
|
|
97
|
+
* Returns true if heartbeat was updated, false if lock not found or not owned
|
|
98
|
+
*/
|
|
99
|
+
export function updateTaskLockHeartbeat(db, taskId, runnerId) {
|
|
100
|
+
const result = db.prepare(`UPDATE task_locks
|
|
101
|
+
SET heartbeat_at = datetime('now')
|
|
102
|
+
WHERE task_id = ?
|
|
103
|
+
AND runner_id = ?`).run(taskId, runnerId);
|
|
104
|
+
return result.changes > 0;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Extend a task lock's expiry time
|
|
108
|
+
* Returns true if extended, false if lock not found or not owned
|
|
109
|
+
*/
|
|
110
|
+
export function extendTaskLock(db, taskId, runnerId, additionalMinutes) {
|
|
111
|
+
const newExpiresAt = new Date(Date.now() + additionalMinutes * 60 * 1000).toISOString();
|
|
112
|
+
const result = db.prepare(`UPDATE task_locks
|
|
113
|
+
SET expires_at = ?,
|
|
114
|
+
heartbeat_at = datetime('now')
|
|
115
|
+
WHERE task_id = ?
|
|
116
|
+
AND runner_id = ?`).run(newExpiresAt, taskId, runnerId);
|
|
117
|
+
return result.changes > 0;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* List all task locks
|
|
121
|
+
*/
|
|
122
|
+
export function listTaskLocks(db) {
|
|
123
|
+
return db
|
|
124
|
+
.prepare('SELECT * FROM task_locks ORDER BY acquired_at DESC')
|
|
125
|
+
.all();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Find all expired task locks
|
|
129
|
+
*/
|
|
130
|
+
export function findExpiredTaskLocks(db) {
|
|
131
|
+
return db
|
|
132
|
+
.prepare(`SELECT * FROM task_locks
|
|
133
|
+
WHERE expires_at < datetime('now')
|
|
134
|
+
ORDER BY expires_at ASC`)
|
|
135
|
+
.all();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Delete all expired task locks
|
|
139
|
+
* Returns the number of locks deleted
|
|
140
|
+
*/
|
|
141
|
+
export function cleanupExpiredTaskLocks(db) {
|
|
142
|
+
const result = db.prepare(`DELETE FROM task_locks WHERE expires_at < datetime('now')`).run();
|
|
143
|
+
return result.changes;
|
|
144
|
+
}
|
|
145
|
+
// ============ Section Lock Queries ============
|
|
146
|
+
/**
|
|
147
|
+
* Get section lock by section ID
|
|
148
|
+
*/
|
|
149
|
+
export function getSectionLock(db, sectionId) {
|
|
150
|
+
return db
|
|
151
|
+
.prepare('SELECT * FROM section_locks WHERE section_id = ?')
|
|
152
|
+
.get(sectionId);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if a section lock is expired
|
|
156
|
+
*/
|
|
157
|
+
export function isSectionLockExpired(lock) {
|
|
158
|
+
const expiresAt = new Date(lock.expires_at).getTime();
|
|
159
|
+
return Date.now() > expiresAt;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Try to insert a new section lock (atomic)
|
|
163
|
+
*/
|
|
164
|
+
export function tryInsertSectionLock(db, sectionId, runnerId, timeoutMinutes) {
|
|
165
|
+
const expiresAt = new Date(Date.now() + timeoutMinutes * 60 * 1000).toISOString();
|
|
166
|
+
try {
|
|
167
|
+
db.prepare(`INSERT INTO section_locks (section_id, runner_id, expires_at)
|
|
168
|
+
VALUES (?, ?, ?)`).run(sectionId, runnerId, expiresAt);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
if (err instanceof Error && err.message.includes('UNIQUE constraint')) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Claim an expired section lock atomically
|
|
180
|
+
*/
|
|
181
|
+
export function claimExpiredSectionLock(db, sectionId, runnerId, timeoutMinutes) {
|
|
182
|
+
const expiresAt = new Date(Date.now() + timeoutMinutes * 60 * 1000).toISOString();
|
|
183
|
+
const result = db.prepare(`UPDATE section_locks
|
|
184
|
+
SET runner_id = ?,
|
|
185
|
+
acquired_at = datetime('now'),
|
|
186
|
+
expires_at = ?
|
|
187
|
+
WHERE section_id = ?
|
|
188
|
+
AND expires_at < datetime('now')`).run(runnerId, expiresAt, sectionId);
|
|
189
|
+
return result.changes > 0;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Release a section lock
|
|
193
|
+
*/
|
|
194
|
+
export function releaseSectionLock(db, sectionId, runnerId) {
|
|
195
|
+
const result = db.prepare(`DELETE FROM section_locks
|
|
196
|
+
WHERE section_id = ?
|
|
197
|
+
AND runner_id = ?`).run(sectionId, runnerId);
|
|
198
|
+
return result.changes > 0;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Force release a section lock
|
|
202
|
+
*/
|
|
203
|
+
export function forceReleaseSectionLock(db, sectionId) {
|
|
204
|
+
const result = db.prepare(`DELETE FROM section_locks WHERE section_id = ?`).run(sectionId);
|
|
205
|
+
return result.changes > 0;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* List all section locks
|
|
209
|
+
*/
|
|
210
|
+
export function listSectionLocks(db) {
|
|
211
|
+
return db
|
|
212
|
+
.prepare('SELECT * FROM section_locks ORDER BY acquired_at DESC')
|
|
213
|
+
.all();
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Find all expired section locks
|
|
217
|
+
*/
|
|
218
|
+
export function findExpiredSectionLocks(db) {
|
|
219
|
+
return db
|
|
220
|
+
.prepare(`SELECT * FROM section_locks
|
|
221
|
+
WHERE expires_at < datetime('now')
|
|
222
|
+
ORDER BY expires_at ASC`)
|
|
223
|
+
.all();
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Delete all expired section locks
|
|
227
|
+
*/
|
|
228
|
+
export function cleanupExpiredSectionLocks(db) {
|
|
229
|
+
const result = db.prepare(`DELETE FROM section_locks WHERE expires_at < datetime('now')`).run();
|
|
230
|
+
return result.changes;
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../../src/locking/queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section locking for parallel work prevention
|
|
3
|
+
* Prevents multiple runners from working on tasks in the same section
|
|
4
|
+
*/
|
|
5
|
+
import type Database from 'better-sqlite3';
|
|
6
|
+
import { type SectionLock } from './queries.js';
|
|
7
|
+
export interface SectionLockAcquisitionResult {
|
|
8
|
+
acquired: boolean;
|
|
9
|
+
lock?: SectionLock;
|
|
10
|
+
reason?: 'already_owned' | 'acquired_new' | 'claimed_expired';
|
|
11
|
+
error?: SectionLockError;
|
|
12
|
+
}
|
|
13
|
+
export interface SectionLockError {
|
|
14
|
+
code: 'SECTION_LOCKED' | 'LOCK_NOT_FOUND' | 'PERMISSION_DENIED';
|
|
15
|
+
message: string;
|
|
16
|
+
details?: {
|
|
17
|
+
sectionId: string;
|
|
18
|
+
runnerId?: string;
|
|
19
|
+
expiresAt?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface SectionLockReleaseResult {
|
|
23
|
+
released: boolean;
|
|
24
|
+
error?: SectionLockError;
|
|
25
|
+
}
|
|
26
|
+
export interface SectionLockManager {
|
|
27
|
+
acquire: () => SectionLockAcquisitionResult;
|
|
28
|
+
release: () => SectionLockReleaseResult;
|
|
29
|
+
isHeld: () => boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Acquire a section lock with atomic operations
|
|
33
|
+
*
|
|
34
|
+
* Flow:
|
|
35
|
+
* 1. Try INSERT (fails if lock exists)
|
|
36
|
+
* 2. If exists, check if we own it
|
|
37
|
+
* 3. If owned by another, check if expired
|
|
38
|
+
* 4. If expired, claim atomically
|
|
39
|
+
* 5. If not expired, return failure
|
|
40
|
+
*/
|
|
41
|
+
export declare function acquireSectionLock(db: Database.Database, sectionId: string, runnerId: string, timeoutMinutes?: number): SectionLockAcquisitionResult;
|
|
42
|
+
/**
|
|
43
|
+
* Release a section lock (only by owner)
|
|
44
|
+
*/
|
|
45
|
+
export declare function releaseSectionLock(db: Database.Database, sectionId: string, runnerId: string): SectionLockReleaseResult;
|
|
46
|
+
/**
|
|
47
|
+
* Force release a section lock (admin operation)
|
|
48
|
+
*/
|
|
49
|
+
export declare function forceRelease(db: Database.Database, sectionId: string): SectionLockReleaseResult;
|
|
50
|
+
/**
|
|
51
|
+
* Create a section lock manager for a specific section
|
|
52
|
+
*/
|
|
53
|
+
export declare function createSectionLockManager(db: Database.Database, sectionId: string, runnerId: string, timeoutMinutes?: number): SectionLockManager;
|
|
54
|
+
/**
|
|
55
|
+
* Check if a section is locked
|
|
56
|
+
*/
|
|
57
|
+
export declare function isSectionLocked(db: Database.Database, sectionId: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Check if a section is locked by a specific runner
|
|
60
|
+
*/
|
|
61
|
+
export declare function isSectionLockedBy(db: Database.Database, sectionId: string, runnerId: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Get time until section lock expires (in milliseconds)
|
|
64
|
+
*/
|
|
65
|
+
export declare function getTimeUntilExpiry(db: Database.Database, sectionId: string): number | null;
|
|
66
|
+
/**
|
|
67
|
+
* Check if any tasks in the section are in progress by another runner
|
|
68
|
+
* This can be used before acquiring a section lock
|
|
69
|
+
*/
|
|
70
|
+
export declare function getSectionLockHolder(db: Database.Database, sectionId: string): SectionLock | null;
|
|
71
|
+
export declare const SECTION_LOCK_CONSTANTS: {
|
|
72
|
+
readonly DEFAULT_TIMEOUT_MINUTES: 120;
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=section-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-lock.d.ts","sourceRoot":"","sources":["../../../../src/locking/section-lock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAOL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAOtB,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,eAAe,GAAG,cAAc,GAAG,iBAAiB,CAAC;IAC9D,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,4BAA4B,CAAC;IAC5C,OAAO,EAAE,MAAM,wBAAwB,CAAC;IACxC,MAAM,EAAE,MAAM,OAAO,CAAC;CACvB;AAID;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,MAAwC,GACvD,4BAA4B,CAqD9B;AAyBD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,wBAAwB,CA+B1B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,GAChB,wBAAwB,CAe1B;AAID;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,MAAwC,GACvD,kBAAkB,CASpB;AAID;;GAEG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAIT;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAIT;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAOf;AAID;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,GAChB,WAAW,GAAG,IAAI,CAMpB;AAID,eAAO,MAAM,sBAAsB;;CAEzB,CAAC"}
|