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
|
+
* Subprocess hang detection
|
|
3
|
+
* Monitors log output and kills processes that appear stuck
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_HANG_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
|
|
6
|
+
/**
|
|
7
|
+
* Create a hang detector for a subprocess
|
|
8
|
+
* Monitors output activity and detects when a process appears stuck
|
|
9
|
+
*/
|
|
10
|
+
export function createHangDetector(options = {}) {
|
|
11
|
+
const { timeoutMs = DEFAULT_HANG_TIMEOUT_MS, onHang, onOutput } = options;
|
|
12
|
+
let lastOutputTime = new Date();
|
|
13
|
+
let checkInterval = null;
|
|
14
|
+
let hasNotifiedHang = false;
|
|
15
|
+
const recordOutput = () => {
|
|
16
|
+
lastOutputTime = new Date();
|
|
17
|
+
hasNotifiedHang = false;
|
|
18
|
+
if (onOutput) {
|
|
19
|
+
onOutput();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const getElapsedSinceOutput = () => {
|
|
23
|
+
return Date.now() - lastOutputTime.getTime();
|
|
24
|
+
};
|
|
25
|
+
const isHung = () => {
|
|
26
|
+
return getElapsedSinceOutput() > timeoutMs;
|
|
27
|
+
};
|
|
28
|
+
const getLastOutputTime = () => {
|
|
29
|
+
return lastOutputTime;
|
|
30
|
+
};
|
|
31
|
+
// Start periodic check
|
|
32
|
+
checkInterval = setInterval(() => {
|
|
33
|
+
if (isHung() && !hasNotifiedHang) {
|
|
34
|
+
hasNotifiedHang = true;
|
|
35
|
+
if (onHang) {
|
|
36
|
+
onHang(lastOutputTime, getElapsedSinceOutput());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, 60 * 1000); // Check every minute
|
|
40
|
+
const stop = () => {
|
|
41
|
+
if (checkInterval) {
|
|
42
|
+
clearInterval(checkInterval);
|
|
43
|
+
checkInterval = null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
recordOutput,
|
|
48
|
+
isHung,
|
|
49
|
+
getLastOutputTime,
|
|
50
|
+
getElapsedSinceOutput,
|
|
51
|
+
stop,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Attach hang detection to a child process
|
|
56
|
+
* Monitors stdout/stderr for output activity
|
|
57
|
+
*/
|
|
58
|
+
export function attachHangDetector(child, options = {}) {
|
|
59
|
+
const { killOnHang = true, ...detectorOptions } = options;
|
|
60
|
+
const detector = createHangDetector({
|
|
61
|
+
...detectorOptions,
|
|
62
|
+
onHang: (lastOutput, elapsed) => {
|
|
63
|
+
const minutes = Math.floor(elapsed / 60000);
|
|
64
|
+
console.error(`Process appears hung (no output for ${minutes} minutes)`);
|
|
65
|
+
if (killOnHang && child.pid) {
|
|
66
|
+
console.error(`Killing hung process (PID: ${child.pid})`);
|
|
67
|
+
try {
|
|
68
|
+
child.kill('SIGTERM');
|
|
69
|
+
// Force kill after 10 seconds if still alive
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
try {
|
|
72
|
+
child.kill('SIGKILL');
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Process already dead
|
|
76
|
+
}
|
|
77
|
+
}, 10000);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Process already dead
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (detectorOptions.onHang) {
|
|
84
|
+
detectorOptions.onHang(lastOutput, elapsed);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
// Monitor stdout
|
|
89
|
+
if (child.stdout) {
|
|
90
|
+
child.stdout.on('data', () => {
|
|
91
|
+
detector.recordOutput();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Monitor stderr
|
|
95
|
+
if (child.stderr) {
|
|
96
|
+
child.stderr.on('data', () => {
|
|
97
|
+
detector.recordOutput();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// Clean up on process exit
|
|
101
|
+
child.on('exit', () => {
|
|
102
|
+
detector.stop();
|
|
103
|
+
});
|
|
104
|
+
child.on('error', () => {
|
|
105
|
+
detector.stop();
|
|
106
|
+
});
|
|
107
|
+
return detector;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get default hang timeout in milliseconds
|
|
111
|
+
*/
|
|
112
|
+
export function getDefaultHangTimeout() {
|
|
113
|
+
return DEFAULT_HANG_TIMEOUT_MS;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Format elapsed time for display
|
|
117
|
+
*/
|
|
118
|
+
export function formatElapsed(ms) {
|
|
119
|
+
const seconds = Math.floor(ms / 1000);
|
|
120
|
+
const minutes = Math.floor(seconds / 60);
|
|
121
|
+
const hours = Math.floor(minutes / 60);
|
|
122
|
+
if (hours > 0) {
|
|
123
|
+
return `${hours}h ${minutes % 60}m`;
|
|
124
|
+
}
|
|
125
|
+
if (minutes > 0) {
|
|
126
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
127
|
+
}
|
|
128
|
+
return `${seconds}s`;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=hang-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hang-detector.js","sourceRoot":"","sources":["../../../../src/runners/hang-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat system for runner liveness detection
|
|
3
|
+
*/
|
|
4
|
+
import type Database from 'better-sqlite3';
|
|
5
|
+
export interface HeartbeatManager {
|
|
6
|
+
start: () => void;
|
|
7
|
+
stop: () => void;
|
|
8
|
+
beat: () => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Update heartbeat timestamp for a runner
|
|
12
|
+
*/
|
|
13
|
+
export declare function updateHeartbeat(db: Database.Database, runnerId: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Check if a runner's heartbeat is stale
|
|
16
|
+
*/
|
|
17
|
+
export declare function isHeartbeatStale(db: Database.Database, runnerId: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Find stale runners (no heartbeat for > 5 minutes)
|
|
20
|
+
*/
|
|
21
|
+
export declare function findStaleRunners(db: Database.Database): Array<{
|
|
22
|
+
id: string;
|
|
23
|
+
pid: number | null;
|
|
24
|
+
heartbeat_at: string;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Create a heartbeat manager for a runner
|
|
28
|
+
* Automatically updates heartbeat at regular intervals
|
|
29
|
+
*/
|
|
30
|
+
export declare function createHeartbeatManager(db: Database.Database, runnerId: string): HeartbeatManager;
|
|
31
|
+
/**
|
|
32
|
+
* Get heartbeat interval in milliseconds
|
|
33
|
+
*/
|
|
34
|
+
export declare function getHeartbeatInterval(): number;
|
|
35
|
+
/**
|
|
36
|
+
* Get stale timeout in milliseconds
|
|
37
|
+
*/
|
|
38
|
+
export declare function getStaleTimeout(): number;
|
|
39
|
+
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../../../src/runners/heartbeat.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAI7E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAWT;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAUjE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAuBlB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat system for runner liveness detection
|
|
3
|
+
*/
|
|
4
|
+
const HEARTBEAT_INTERVAL_MS = 30 * 1000; // 30 seconds
|
|
5
|
+
const STALE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
6
|
+
/**
|
|
7
|
+
* Update heartbeat timestamp for a runner
|
|
8
|
+
*/
|
|
9
|
+
export function updateHeartbeat(db, runnerId) {
|
|
10
|
+
db.prepare(`UPDATE runners SET heartbeat_at = datetime('now') WHERE id = ?`).run(runnerId);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Check if a runner's heartbeat is stale
|
|
14
|
+
*/
|
|
15
|
+
export function isHeartbeatStale(db, runnerId) {
|
|
16
|
+
// Use SQLite's datetime comparison to avoid JavaScript/SQLite format issues
|
|
17
|
+
const result = db
|
|
18
|
+
.prepare(`SELECT 1 FROM runners
|
|
19
|
+
WHERE id = ? AND heartbeat_at >= datetime('now', '-5 minutes')`)
|
|
20
|
+
.get(runnerId);
|
|
21
|
+
// If no row returned, either runner doesn't exist or heartbeat is stale
|
|
22
|
+
return result === undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Find stale runners (no heartbeat for > 5 minutes)
|
|
26
|
+
*/
|
|
27
|
+
export function findStaleRunners(db) {
|
|
28
|
+
// Use SQLite's datetime function to avoid timestamp format mismatches
|
|
29
|
+
// JavaScript ISO format ("2026-02-08T22:28:49.000Z") differs from
|
|
30
|
+
// SQLite datetime format ("2026-02-08 22:33:49"), breaking string comparison
|
|
31
|
+
return db
|
|
32
|
+
.prepare(`SELECT id, pid, heartbeat_at FROM runners
|
|
33
|
+
WHERE heartbeat_at < datetime('now', '-5 minutes') AND status != 'idle'`)
|
|
34
|
+
.all();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a heartbeat manager for a runner
|
|
38
|
+
* Automatically updates heartbeat at regular intervals
|
|
39
|
+
*/
|
|
40
|
+
export function createHeartbeatManager(db, runnerId) {
|
|
41
|
+
let intervalId = null;
|
|
42
|
+
const beat = () => {
|
|
43
|
+
updateHeartbeat(db, runnerId);
|
|
44
|
+
};
|
|
45
|
+
const start = () => {
|
|
46
|
+
// Update immediately
|
|
47
|
+
beat();
|
|
48
|
+
// Then update every 30 seconds
|
|
49
|
+
intervalId = setInterval(beat, HEARTBEAT_INTERVAL_MS);
|
|
50
|
+
};
|
|
51
|
+
const stop = () => {
|
|
52
|
+
if (intervalId) {
|
|
53
|
+
clearInterval(intervalId);
|
|
54
|
+
intervalId = null;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return { start, stop, beat };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get heartbeat interval in milliseconds
|
|
61
|
+
*/
|
|
62
|
+
export function getHeartbeatInterval() {
|
|
63
|
+
return HEARTBEAT_INTERVAL_MS;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get stale timeout in milliseconds
|
|
67
|
+
*/
|
|
68
|
+
export function getStaleTimeout() {
|
|
69
|
+
return STALE_TIMEOUT_MS;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../../../src/runners/heartbeat.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AACtD,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAQpD;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,EAAqB,EAAE,QAAgB;IACrE,EAAE,CAAC,OAAO,CACR,gEAAgE,CACjE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,EAAqB,EACrB,QAAgB;IAEhB,4EAA4E;IAC5E,MAAM,MAAM,GAAG,EAAE;SACd,OAAO,CACN;sEACgE,CACjE;SACA,GAAG,CAAC,QAAQ,CAA8B,CAAC;IAE9C,wEAAwE;IACxE,OAAO,MAAM,KAAK,SAAS,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,EAAqB;IAErB,sEAAsE;IACtE,kEAAkE;IAClE,6EAA6E;IAC7E,OAAO,EAAE;SACN,OAAO,CACN;+EACyE,CAC1E;SACA,GAAG,EAAqE,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,EAAqB,EACrB,QAAgB;IAEhB,IAAI,UAAU,GAA0B,IAAI,CAAC;IAE7C,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,eAAe,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,qBAAqB;QACrB,IAAI,EAAE,CAAC;QAEP,+BAA+B;QAC/B,UAAU,GAAG,WAAW,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;IACxD,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,IAAI,UAAU,EAAE,CAAC;YACf,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1B,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton lock for runner daemon
|
|
3
|
+
* Uses directory-based locking with PID file
|
|
4
|
+
*/
|
|
5
|
+
export interface LockResult {
|
|
6
|
+
acquired: boolean;
|
|
7
|
+
existingPid?: number;
|
|
8
|
+
isZombie?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get the lock directory path
|
|
12
|
+
*/
|
|
13
|
+
export declare function getLockDir(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Get the PID file path
|
|
16
|
+
*/
|
|
17
|
+
export declare function getPidFilePath(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a process with given PID is running
|
|
20
|
+
*/
|
|
21
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Read the PID from lock file
|
|
24
|
+
*/
|
|
25
|
+
export declare function readLockPid(): number | null;
|
|
26
|
+
/**
|
|
27
|
+
* Remove the lock directory
|
|
28
|
+
*/
|
|
29
|
+
export declare function removeLock(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Attempt to acquire the singleton lock
|
|
32
|
+
* Returns result indicating success or existing lock holder
|
|
33
|
+
*/
|
|
34
|
+
export declare function acquireLock(): LockResult;
|
|
35
|
+
/**
|
|
36
|
+
* Release the lock (should be called on shutdown)
|
|
37
|
+
*/
|
|
38
|
+
export declare function releaseLock(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Check lock status without acquiring
|
|
41
|
+
*/
|
|
42
|
+
export declare function checkLockStatus(): {
|
|
43
|
+
locked: boolean;
|
|
44
|
+
pid: number | null;
|
|
45
|
+
isZombie: boolean;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../../../src/runners/lock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQnD;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAa3C;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAKjC;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,UAAU,CAiDxC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAOlC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAkBA"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton lock for runner daemon
|
|
3
|
+
* Uses directory-based locking with PID file
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { getGlobalSteroidsDir } from './global-db.js';
|
|
8
|
+
const LOCK_DIR = 'runners/lock';
|
|
9
|
+
const PID_FILE = 'pid';
|
|
10
|
+
/**
|
|
11
|
+
* Get the lock directory path
|
|
12
|
+
*/
|
|
13
|
+
export function getLockDir() {
|
|
14
|
+
return join(getGlobalSteroidsDir(), LOCK_DIR);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get the PID file path
|
|
18
|
+
*/
|
|
19
|
+
export function getPidFilePath() {
|
|
20
|
+
return join(getLockDir(), PID_FILE);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if a process with given PID is running
|
|
24
|
+
*/
|
|
25
|
+
export function isProcessAlive(pid) {
|
|
26
|
+
try {
|
|
27
|
+
// Sending signal 0 checks if process exists without killing it
|
|
28
|
+
process.kill(pid, 0);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read the PID from lock file
|
|
37
|
+
*/
|
|
38
|
+
export function readLockPid() {
|
|
39
|
+
const pidPath = getPidFilePath();
|
|
40
|
+
if (!existsSync(pidPath)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const content = readFileSync(pidPath, 'utf-8').trim();
|
|
45
|
+
const pid = parseInt(content, 10);
|
|
46
|
+
return isNaN(pid) ? null : pid;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Remove the lock directory
|
|
54
|
+
*/
|
|
55
|
+
export function removeLock() {
|
|
56
|
+
const lockDir = getLockDir();
|
|
57
|
+
if (existsSync(lockDir)) {
|
|
58
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Attempt to acquire the singleton lock
|
|
63
|
+
* Returns result indicating success or existing lock holder
|
|
64
|
+
*/
|
|
65
|
+
export function acquireLock() {
|
|
66
|
+
const lockDir = getLockDir();
|
|
67
|
+
const pidPath = getPidFilePath();
|
|
68
|
+
// Check if lock exists
|
|
69
|
+
if (existsSync(lockDir)) {
|
|
70
|
+
const existingPid = readLockPid();
|
|
71
|
+
if (existingPid !== null) {
|
|
72
|
+
if (isProcessAlive(existingPid)) {
|
|
73
|
+
// Another runner is active
|
|
74
|
+
return {
|
|
75
|
+
acquired: false,
|
|
76
|
+
existingPid,
|
|
77
|
+
isZombie: false,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// Zombie lock - process is dead
|
|
81
|
+
removeLock();
|
|
82
|
+
// Fall through to acquire
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// Lock dir exists but no valid PID - clean up
|
|
86
|
+
removeLock();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Create lock directory
|
|
90
|
+
try {
|
|
91
|
+
mkdirSync(lockDir, { recursive: true });
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
// Race condition - another process may have created it
|
|
95
|
+
if (existsSync(lockDir)) {
|
|
96
|
+
const existingPid = readLockPid();
|
|
97
|
+
if (existingPid !== null && isProcessAlive(existingPid)) {
|
|
98
|
+
return {
|
|
99
|
+
acquired: false,
|
|
100
|
+
existingPid,
|
|
101
|
+
isZombie: false,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
// Write our PID
|
|
108
|
+
writeFileSync(pidPath, String(process.pid));
|
|
109
|
+
return { acquired: true };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Release the lock (should be called on shutdown)
|
|
113
|
+
*/
|
|
114
|
+
export function releaseLock() {
|
|
115
|
+
const existingPid = readLockPid();
|
|
116
|
+
// Only release if we own the lock
|
|
117
|
+
if (existingPid === process.pid) {
|
|
118
|
+
removeLock();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Check lock status without acquiring
|
|
123
|
+
*/
|
|
124
|
+
export function checkLockStatus() {
|
|
125
|
+
const lockDir = getLockDir();
|
|
126
|
+
if (!existsSync(lockDir)) {
|
|
127
|
+
return { locked: false, pid: null, isZombie: false };
|
|
128
|
+
}
|
|
129
|
+
const pid = readLockPid();
|
|
130
|
+
if (pid === null) {
|
|
131
|
+
return { locked: false, pid: null, isZombie: false };
|
|
132
|
+
}
|
|
133
|
+
const alive = isProcessAlive(pid);
|
|
134
|
+
return {
|
|
135
|
+
locked: alive,
|
|
136
|
+
pid,
|
|
137
|
+
isZombie: !alive,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../../../src/runners/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator loop - core task processing logic
|
|
3
|
+
* Used by both the daemon and the loop command
|
|
4
|
+
*/
|
|
5
|
+
export interface LoopOptions {
|
|
6
|
+
projectPath: string;
|
|
7
|
+
once?: boolean;
|
|
8
|
+
sectionId?: string;
|
|
9
|
+
runnerId?: string;
|
|
10
|
+
onIteration?: (iteration: number) => void;
|
|
11
|
+
onTaskStart?: (taskId: string, action: string) => void;
|
|
12
|
+
onTaskComplete?: (taskId: string) => void;
|
|
13
|
+
shouldStop?: () => boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run the orchestrator loop
|
|
17
|
+
* Processes tasks until all are complete or shouldStop returns true
|
|
18
|
+
*/
|
|
19
|
+
export declare function runOrchestratorLoop(options: LoopOptions): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=orchestrator-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator-loop.d.ts","sourceRoot":"","sources":["../../../../src/runners/orchestrator-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwBH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAmP7E"}
|