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.
Files changed (328) hide show
  1. package/dist/API/src/index.d.ts +10 -0
  2. package/dist/API/src/index.d.ts.map +1 -0
  3. package/dist/API/src/index.js +130 -0
  4. package/dist/API/src/index.js.map +1 -0
  5. package/dist/API/src/routes/activity.d.ts +7 -0
  6. package/dist/API/src/routes/activity.d.ts.map +1 -0
  7. package/dist/API/src/routes/activity.js +252 -0
  8. package/dist/API/src/routes/activity.js.map +1 -0
  9. package/dist/API/src/routes/config.d.ts +7 -0
  10. package/dist/API/src/routes/config.d.ts.map +1 -0
  11. package/dist/API/src/routes/config.js +521 -0
  12. package/dist/API/src/routes/config.js.map +1 -0
  13. package/dist/API/src/routes/health.d.ts +7 -0
  14. package/dist/API/src/routes/health.d.ts.map +1 -0
  15. package/dist/API/src/routes/health.js +172 -0
  16. package/dist/API/src/routes/health.js.map +1 -0
  17. package/dist/API/src/routes/incidents.d.ts +7 -0
  18. package/dist/API/src/routes/incidents.d.ts.map +1 -0
  19. package/dist/API/src/routes/incidents.js +117 -0
  20. package/dist/API/src/routes/incidents.js.map +1 -0
  21. package/dist/API/src/routes/projects.d.ts +7 -0
  22. package/dist/API/src/routes/projects.d.ts.map +1 -0
  23. package/dist/API/src/routes/projects.js +398 -0
  24. package/dist/API/src/routes/projects.js.map +1 -0
  25. package/dist/API/src/routes/runners.d.ts +7 -0
  26. package/dist/API/src/routes/runners.d.ts.map +1 -0
  27. package/dist/API/src/routes/runners.js +242 -0
  28. package/dist/API/src/routes/runners.js.map +1 -0
  29. package/dist/API/src/routes/tasks.d.ts +7 -0
  30. package/dist/API/src/routes/tasks.d.ts.map +1 -0
  31. package/dist/API/src/routes/tasks.js +1007 -0
  32. package/dist/API/src/routes/tasks.js.map +1 -0
  33. package/dist/API/src/utils/validation.d.ts +22 -0
  34. package/dist/API/src/utils/validation.d.ts.map +1 -0
  35. package/dist/API/src/utils/validation.js +50 -0
  36. package/dist/API/src/utils/validation.js.map +1 -0
  37. package/dist/index.d.ts +10 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +184 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/routes/activity.d.ts +7 -0
  42. package/dist/routes/activity.d.ts.map +1 -0
  43. package/dist/routes/activity.js +252 -0
  44. package/dist/routes/activity.js.map +1 -0
  45. package/dist/routes/config.d.ts +7 -0
  46. package/dist/routes/config.d.ts.map +1 -0
  47. package/dist/routes/config.js +647 -0
  48. package/dist/routes/config.js.map +1 -0
  49. package/dist/routes/credit-alerts.d.ts +2 -0
  50. package/dist/routes/credit-alerts.d.ts.map +1 -0
  51. package/dist/routes/credit-alerts.js +97 -0
  52. package/dist/routes/credit-alerts.js.map +1 -0
  53. package/dist/routes/health.d.ts +7 -0
  54. package/dist/routes/health.d.ts.map +1 -0
  55. package/dist/routes/health.js +200 -0
  56. package/dist/routes/health.js.map +1 -0
  57. package/dist/routes/incidents.d.ts +7 -0
  58. package/dist/routes/incidents.d.ts.map +1 -0
  59. package/dist/routes/incidents.js +117 -0
  60. package/dist/routes/incidents.js.map +1 -0
  61. package/dist/routes/projects.d.ts +7 -0
  62. package/dist/routes/projects.d.ts.map +1 -0
  63. package/dist/routes/projects.js +643 -0
  64. package/dist/routes/projects.js.map +1 -0
  65. package/dist/routes/runners.d.ts +7 -0
  66. package/dist/routes/runners.d.ts.map +1 -0
  67. package/dist/routes/runners.js +299 -0
  68. package/dist/routes/runners.js.map +1 -0
  69. package/dist/routes/skills.d.ts +3 -0
  70. package/dist/routes/skills.d.ts.map +1 -0
  71. package/dist/routes/skills.js +109 -0
  72. package/dist/routes/skills.js.map +1 -0
  73. package/dist/routes/storage.d.ts +7 -0
  74. package/dist/routes/storage.d.ts.map +1 -0
  75. package/dist/routes/storage.js +93 -0
  76. package/dist/routes/storage.js.map +1 -0
  77. package/dist/routes/tasks.d.ts +7 -0
  78. package/dist/routes/tasks.d.ts.map +1 -0
  79. package/dist/routes/tasks.js +1145 -0
  80. package/dist/routes/tasks.js.map +1 -0
  81. package/dist/src/cleanup/invocation-logs.d.ts +30 -0
  82. package/dist/src/cleanup/invocation-logs.d.ts.map +1 -0
  83. package/dist/src/cleanup/invocation-logs.js +66 -0
  84. package/dist/src/cleanup/invocation-logs.js.map +1 -0
  85. package/dist/src/commands/loop-phases.d.ts +11 -0
  86. package/dist/src/commands/loop-phases.d.ts.map +1 -0
  87. package/dist/src/commands/loop-phases.js +304 -0
  88. package/dist/src/commands/loop-phases.js.map +1 -0
  89. package/dist/src/config/loader.d.ts +160 -0
  90. package/dist/src/config/loader.d.ts.map +1 -0
  91. package/dist/src/config/loader.js +276 -0
  92. package/dist/src/config/loader.js.map +1 -0
  93. package/dist/src/database/connection.d.ts +35 -0
  94. package/dist/src/database/connection.d.ts.map +1 -0
  95. package/dist/src/database/connection.js +197 -0
  96. package/dist/src/database/connection.js.map +1 -0
  97. package/dist/src/database/queries.d.ts +220 -0
  98. package/dist/src/database/queries.d.ts.map +1 -0
  99. package/dist/src/database/queries.js +589 -0
  100. package/dist/src/database/queries.js.map +1 -0
  101. package/dist/src/database/schema.d.ts +8 -0
  102. package/dist/src/database/schema.d.ts.map +1 -0
  103. package/dist/src/database/schema.js +184 -0
  104. package/dist/src/database/schema.js.map +1 -0
  105. package/dist/src/git/push.d.ts +26 -0
  106. package/dist/src/git/push.d.ts.map +1 -0
  107. package/dist/src/git/push.js +91 -0
  108. package/dist/src/git/push.js.map +1 -0
  109. package/dist/src/git/status.d.ts +83 -0
  110. package/dist/src/git/status.d.ts.map +1 -0
  111. package/dist/src/git/status.js +315 -0
  112. package/dist/src/git/status.js.map +1 -0
  113. package/dist/src/health/stuck-task-detector.d.ts +131 -0
  114. package/dist/src/health/stuck-task-detector.d.ts.map +1 -0
  115. package/dist/src/health/stuck-task-detector.js +233 -0
  116. package/dist/src/health/stuck-task-detector.js.map +1 -0
  117. package/dist/src/health/stuck-task-recovery.d.ts +45 -0
  118. package/dist/src/health/stuck-task-recovery.d.ts.map +1 -0
  119. package/dist/src/health/stuck-task-recovery.js +309 -0
  120. package/dist/src/health/stuck-task-recovery.js.map +1 -0
  121. package/dist/src/index.d.ts +10 -0
  122. package/dist/src/index.d.ts.map +1 -0
  123. package/dist/src/index.js +130 -0
  124. package/dist/src/index.js.map +1 -0
  125. package/dist/src/locking/queries.d.ts +116 -0
  126. package/dist/src/locking/queries.d.ts.map +1 -0
  127. package/dist/src/locking/queries.js +232 -0
  128. package/dist/src/locking/queries.js.map +1 -0
  129. package/dist/src/locking/section-lock.d.ts +74 -0
  130. package/dist/src/locking/section-lock.d.ts.map +1 -0
  131. package/dist/src/locking/section-lock.js +196 -0
  132. package/dist/src/locking/section-lock.js.map +1 -0
  133. package/dist/src/locking/task-lock.d.ts +92 -0
  134. package/dist/src/locking/task-lock.d.ts.map +1 -0
  135. package/dist/src/locking/task-lock.js +233 -0
  136. package/dist/src/locking/task-lock.js.map +1 -0
  137. package/dist/src/migrations/index.d.ts +7 -0
  138. package/dist/src/migrations/index.d.ts.map +1 -0
  139. package/dist/src/migrations/index.js +9 -0
  140. package/dist/src/migrations/index.js.map +1 -0
  141. package/dist/src/migrations/manifest.d.ts +92 -0
  142. package/dist/src/migrations/manifest.d.ts.map +1 -0
  143. package/dist/src/migrations/manifest.js +255 -0
  144. package/dist/src/migrations/manifest.js.map +1 -0
  145. package/dist/src/migrations/runner.d.ts +84 -0
  146. package/dist/src/migrations/runner.d.ts.map +1 -0
  147. package/dist/src/migrations/runner.js +338 -0
  148. package/dist/src/migrations/runner.js.map +1 -0
  149. package/dist/src/orchestrator/coder.d.ts +32 -0
  150. package/dist/src/orchestrator/coder.d.ts.map +1 -0
  151. package/dist/src/orchestrator/coder.js +170 -0
  152. package/dist/src/orchestrator/coder.js.map +1 -0
  153. package/dist/src/orchestrator/coordinator.d.ts +28 -0
  154. package/dist/src/orchestrator/coordinator.d.ts.map +1 -0
  155. package/dist/src/orchestrator/coordinator.js +252 -0
  156. package/dist/src/orchestrator/coordinator.js.map +1 -0
  157. package/dist/src/orchestrator/fallback-handler.d.ts +24 -0
  158. package/dist/src/orchestrator/fallback-handler.d.ts.map +1 -0
  159. package/dist/src/orchestrator/fallback-handler.js +280 -0
  160. package/dist/src/orchestrator/fallback-handler.js.map +1 -0
  161. package/dist/src/orchestrator/invoke.d.ts +14 -0
  162. package/dist/src/orchestrator/invoke.d.ts.map +1 -0
  163. package/dist/src/orchestrator/invoke.js +76 -0
  164. package/dist/src/orchestrator/invoke.js.map +1 -0
  165. package/dist/src/orchestrator/post-coder.d.ts +10 -0
  166. package/dist/src/orchestrator/post-coder.d.ts.map +1 -0
  167. package/dist/src/orchestrator/post-coder.js +198 -0
  168. package/dist/src/orchestrator/post-coder.js.map +1 -0
  169. package/dist/src/orchestrator/post-reviewer.d.ts +10 -0
  170. package/dist/src/orchestrator/post-reviewer.d.ts.map +1 -0
  171. package/dist/src/orchestrator/post-reviewer.js +199 -0
  172. package/dist/src/orchestrator/post-reviewer.js.map +1 -0
  173. package/dist/src/orchestrator/reviewer.d.ts +35 -0
  174. package/dist/src/orchestrator/reviewer.d.ts.map +1 -0
  175. package/dist/src/orchestrator/reviewer.js +237 -0
  176. package/dist/src/orchestrator/reviewer.js.map +1 -0
  177. package/dist/src/orchestrator/schemas.d.ts +10 -0
  178. package/dist/src/orchestrator/schemas.d.ts.map +1 -0
  179. package/dist/src/orchestrator/schemas.js +81 -0
  180. package/dist/src/orchestrator/schemas.js.map +1 -0
  181. package/dist/src/orchestrator/task-selector.d.ts +102 -0
  182. package/dist/src/orchestrator/task-selector.d.ts.map +1 -0
  183. package/dist/src/orchestrator/task-selector.js +326 -0
  184. package/dist/src/orchestrator/task-selector.js.map +1 -0
  185. package/dist/src/orchestrator/types.d.ts +74 -0
  186. package/dist/src/orchestrator/types.d.ts.map +1 -0
  187. package/dist/src/orchestrator/types.js +5 -0
  188. package/dist/src/orchestrator/types.js.map +1 -0
  189. package/dist/src/prompts/coder.d.ts +36 -0
  190. package/dist/src/prompts/coder.d.ts.map +1 -0
  191. package/dist/src/prompts/coder.js +303 -0
  192. package/dist/src/prompts/coder.js.map +1 -0
  193. package/dist/src/prompts/prompt-helpers.d.ts +51 -0
  194. package/dist/src/prompts/prompt-helpers.d.ts.map +1 -0
  195. package/dist/src/prompts/prompt-helpers.js +299 -0
  196. package/dist/src/prompts/prompt-helpers.js.map +1 -0
  197. package/dist/src/prompts/reviewer.d.ts +40 -0
  198. package/dist/src/prompts/reviewer.d.ts.map +1 -0
  199. package/dist/src/prompts/reviewer.js +416 -0
  200. package/dist/src/prompts/reviewer.js.map +1 -0
  201. package/dist/src/providers/claude.d.ts +53 -0
  202. package/dist/src/providers/claude.d.ts.map +1 -0
  203. package/dist/src/providers/claude.js +227 -0
  204. package/dist/src/providers/claude.js.map +1 -0
  205. package/dist/src/providers/codex.d.ts +53 -0
  206. package/dist/src/providers/codex.d.ts.map +1 -0
  207. package/dist/src/providers/codex.js +253 -0
  208. package/dist/src/providers/codex.js.map +1 -0
  209. package/dist/src/providers/gemini.d.ts +58 -0
  210. package/dist/src/providers/gemini.d.ts.map +1 -0
  211. package/dist/src/providers/gemini.js +240 -0
  212. package/dist/src/providers/gemini.js.map +1 -0
  213. package/dist/src/providers/interface.d.ts +185 -0
  214. package/dist/src/providers/interface.d.ts.map +1 -0
  215. package/dist/src/providers/interface.js +92 -0
  216. package/dist/src/providers/interface.js.map +1 -0
  217. package/dist/src/providers/invocation-logger.d.ts +97 -0
  218. package/dist/src/providers/invocation-logger.d.ts.map +1 -0
  219. package/dist/src/providers/invocation-logger.js +378 -0
  220. package/dist/src/providers/invocation-logger.js.map +1 -0
  221. package/dist/src/providers/openai.d.ts +53 -0
  222. package/dist/src/providers/openai.d.ts.map +1 -0
  223. package/dist/src/providers/openai.js +230 -0
  224. package/dist/src/providers/openai.js.map +1 -0
  225. package/dist/src/providers/registry.d.ts +100 -0
  226. package/dist/src/providers/registry.d.ts.map +1 -0
  227. package/dist/src/providers/registry.js +170 -0
  228. package/dist/src/providers/registry.js.map +1 -0
  229. package/dist/src/routes/activity.d.ts +7 -0
  230. package/dist/src/routes/activity.d.ts.map +1 -0
  231. package/dist/src/routes/activity.js +252 -0
  232. package/dist/src/routes/activity.js.map +1 -0
  233. package/dist/src/routes/config.d.ts +7 -0
  234. package/dist/src/routes/config.d.ts.map +1 -0
  235. package/dist/src/routes/config.js +521 -0
  236. package/dist/src/routes/config.js.map +1 -0
  237. package/dist/src/routes/health.d.ts +7 -0
  238. package/dist/src/routes/health.d.ts.map +1 -0
  239. package/dist/src/routes/health.js +172 -0
  240. package/dist/src/routes/health.js.map +1 -0
  241. package/dist/src/routes/incidents.d.ts +7 -0
  242. package/dist/src/routes/incidents.d.ts.map +1 -0
  243. package/dist/src/routes/incidents.js +117 -0
  244. package/dist/src/routes/incidents.js.map +1 -0
  245. package/dist/src/routes/projects.d.ts +7 -0
  246. package/dist/src/routes/projects.d.ts.map +1 -0
  247. package/dist/src/routes/projects.js +398 -0
  248. package/dist/src/routes/projects.js.map +1 -0
  249. package/dist/src/routes/runners.d.ts +7 -0
  250. package/dist/src/routes/runners.d.ts.map +1 -0
  251. package/dist/src/routes/runners.js +242 -0
  252. package/dist/src/routes/runners.js.map +1 -0
  253. package/dist/src/routes/tasks.d.ts +7 -0
  254. package/dist/src/routes/tasks.d.ts.map +1 -0
  255. package/dist/src/routes/tasks.js +1007 -0
  256. package/dist/src/routes/tasks.js.map +1 -0
  257. package/dist/src/runners/activity-log.d.ts +65 -0
  258. package/dist/src/runners/activity-log.d.ts.map +1 -0
  259. package/dist/src/runners/activity-log.js +140 -0
  260. package/dist/src/runners/activity-log.js.map +1 -0
  261. package/dist/src/runners/cron.d.ts +30 -0
  262. package/dist/src/runners/cron.d.ts.map +1 -0
  263. package/dist/src/runners/cron.js +333 -0
  264. package/dist/src/runners/cron.js.map +1 -0
  265. package/dist/src/runners/daemon.d.ts +71 -0
  266. package/dist/src/runners/daemon.d.ts.map +1 -0
  267. package/dist/src/runners/daemon.js +233 -0
  268. package/dist/src/runners/daemon.js.map +1 -0
  269. package/dist/src/runners/global-db.d.ts +31 -0
  270. package/dist/src/runners/global-db.d.ts.map +1 -0
  271. package/dist/src/runners/global-db.js +220 -0
  272. package/dist/src/runners/global-db.js.map +1 -0
  273. package/dist/src/runners/hang-detector.d.ts +38 -0
  274. package/dist/src/runners/hang-detector.d.ts.map +1 -0
  275. package/dist/src/runners/hang-detector.js +130 -0
  276. package/dist/src/runners/hang-detector.js.map +1 -0
  277. package/dist/src/runners/heartbeat.d.ts +39 -0
  278. package/dist/src/runners/heartbeat.d.ts.map +1 -0
  279. package/dist/src/runners/heartbeat.js +71 -0
  280. package/dist/src/runners/heartbeat.js.map +1 -0
  281. package/dist/src/runners/lock.d.ts +47 -0
  282. package/dist/src/runners/lock.d.ts.map +1 -0
  283. package/dist/src/runners/lock.js +140 -0
  284. package/dist/src/runners/lock.js.map +1 -0
  285. package/dist/src/runners/orchestrator-loop.d.ts +20 -0
  286. package/dist/src/runners/orchestrator-loop.d.ts.map +1 -0
  287. package/dist/src/runners/orchestrator-loop.js +208 -0
  288. package/dist/src/runners/orchestrator-loop.js.map +1 -0
  289. package/dist/src/runners/projects.d.ts +96 -0
  290. package/dist/src/runners/projects.d.ts.map +1 -0
  291. package/dist/src/runners/projects.js +243 -0
  292. package/dist/src/runners/projects.js.map +1 -0
  293. package/dist/src/runners/wakeup.d.ts +37 -0
  294. package/dist/src/runners/wakeup.d.ts.map +1 -0
  295. package/dist/src/runners/wakeup.js +355 -0
  296. package/dist/src/runners/wakeup.js.map +1 -0
  297. package/dist/src/utils/validation.d.ts +22 -0
  298. package/dist/src/utils/validation.d.ts.map +1 -0
  299. package/dist/src/utils/validation.js +50 -0
  300. package/dist/src/utils/validation.js.map +1 -0
  301. package/dist/utils/sqlite.d.ts +17 -0
  302. package/dist/utils/sqlite.d.ts.map +1 -0
  303. package/dist/utils/sqlite.js +27 -0
  304. package/dist/utils/sqlite.js.map +1 -0
  305. package/dist/utils/storage-cache.d.ts +33 -0
  306. package/dist/utils/storage-cache.d.ts.map +1 -0
  307. package/dist/utils/storage-cache.js +81 -0
  308. package/dist/utils/storage-cache.js.map +1 -0
  309. package/dist/utils/validation.d.ts +22 -0
  310. package/dist/utils/validation.d.ts.map +1 -0
  311. package/dist/utils/validation.js +51 -0
  312. package/dist/utils/validation.js.map +1 -0
  313. package/package.json +39 -0
  314. package/src/index.ts +199 -0
  315. package/src/routes/activity.ts +302 -0
  316. package/src/routes/config.ts +723 -0
  317. package/src/routes/credit-alerts.ts +73 -0
  318. package/src/routes/health.ts +219 -0
  319. package/src/routes/incidents.ts +131 -0
  320. package/src/routes/projects.ts +854 -0
  321. package/src/routes/runners.ts +357 -0
  322. package/src/routes/skills.ts +127 -0
  323. package/src/routes/storage.ts +108 -0
  324. package/src/routes/tasks.ts +1372 -0
  325. package/src/utils/sqlite.ts +36 -0
  326. package/src/utils/storage-cache.ts +107 -0
  327. package/src/utils/validation.ts +61 -0
  328. package/tsconfig.json +20 -0
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Cron wake-up command for restarting stale/dead runners
3
+ */
4
+ import { existsSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { spawn } from 'node:child_process';
7
+ import { checkLockStatus, removeLock } from './lock.js';
8
+ import { openGlobalDatabase } from './global-db.js';
9
+ import { findStaleRunners } from './heartbeat.js';
10
+ import { getRegisteredProjects } from './projects.js';
11
+ import { openDatabase } from '../database/connection.js';
12
+ import { loadConfig } from '../config/loader.js';
13
+ import { recoverStuckTasks } from '../health/stuck-task-recovery.js';
14
+ import { cleanupInvocationLogs } from '../cleanup/invocation-logs.js';
15
+ /**
16
+ * Check if a project has pending work
17
+ */
18
+ async function projectHasPendingWork(projectPath) {
19
+ const dbPath = join(projectPath, '.steroids', 'steroids.db');
20
+ if (!existsSync(dbPath)) {
21
+ return false;
22
+ }
23
+ try {
24
+ // Use dynamic import for ESM compatibility
25
+ const { default: Database } = await import('better-sqlite3');
26
+ const db = new Database(dbPath, { readonly: true });
27
+ const result = db
28
+ .prepare(`SELECT COUNT(*) as count FROM tasks
29
+ WHERE status IN ('pending', 'in_progress', 'review')`)
30
+ .get();
31
+ db.close();
32
+ return result.count > 0;
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ }
38
+ /**
39
+ * Check if there's an active runner for a specific project
40
+ * Exported for use in daemon startup checks
41
+ */
42
+ export function hasActiveRunnerForProject(projectPath) {
43
+ const { db, close } = openGlobalDatabase();
44
+ try {
45
+ const row = db
46
+ .prepare(`SELECT 1 FROM runners
47
+ WHERE project_path = ?
48
+ AND status != 'stopped'
49
+ AND heartbeat_at > datetime('now', '-5 minutes')`)
50
+ .get(projectPath);
51
+ return row !== undefined;
52
+ }
53
+ finally {
54
+ close();
55
+ }
56
+ }
57
+ /**
58
+ * Kill a process by PID
59
+ */
60
+ function killProcess(pid) {
61
+ try {
62
+ process.kill(pid, 'SIGTERM');
63
+ return true;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /**
70
+ * Start a new runner daemon
71
+ * Uses 'steroids runners start --detach' so the runner registers in the global DB
72
+ * and updates heartbeat, allowing hasActiveRunnerForProject() to detect it
73
+ */
74
+ function startRunner(projectPath) {
75
+ try {
76
+ // Use runners start --detach so the daemon registers itself in the global DB
77
+ // This is critical: hasActiveRunnerForProject() checks the runners table,
78
+ // and only the daemon (not loop directly) writes to that table
79
+ const args = ['runners', 'start', '--detach', '--project', projectPath];
80
+ const child = spawn('steroids', args, {
81
+ detached: true,
82
+ stdio: 'ignore',
83
+ });
84
+ child.unref();
85
+ return { pid: child.pid ?? 0 };
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ /**
92
+ * Main wake-up function
93
+ * Called by cron every minute to ensure runners are healthy
94
+ * Iterates over ALL registered projects and starts runners as needed
95
+ * Returns per-project results
96
+ */
97
+ /**
98
+ * Record the last wakeup invocation time
99
+ */
100
+ function recordWakeupTime() {
101
+ const { db, close } = openGlobalDatabase();
102
+ try {
103
+ db.prepare(`INSERT INTO _global_schema (key, value) VALUES ('last_wakeup_at', datetime('now'))
104
+ ON CONFLICT(key) DO UPDATE SET value = datetime('now')`).run();
105
+ }
106
+ finally {
107
+ close();
108
+ }
109
+ }
110
+ /**
111
+ * Get the last wakeup invocation time
112
+ */
113
+ export function getLastWakeupTime() {
114
+ const { db, close } = openGlobalDatabase();
115
+ try {
116
+ const row = db
117
+ .prepare("SELECT value FROM _global_schema WHERE key = 'last_wakeup_at'")
118
+ .get();
119
+ return row?.value ?? null;
120
+ }
121
+ finally {
122
+ close();
123
+ }
124
+ }
125
+ export async function wakeup(options = {}) {
126
+ const { quiet = false, dryRun = false } = options;
127
+ const results = [];
128
+ const log = (msg) => {
129
+ if (!quiet)
130
+ console.log(msg);
131
+ };
132
+ // Record wakeup invocation time (even for dry runs)
133
+ if (!dryRun) {
134
+ recordWakeupTime();
135
+ }
136
+ // Step 1: Clean up stale runners first
137
+ const global = openGlobalDatabase();
138
+ try {
139
+ try {
140
+ const staleRunners = findStaleRunners(global.db);
141
+ if (staleRunners.length > 0) {
142
+ log(`Found ${staleRunners.length} stale runner(s), cleaning up...`);
143
+ if (!dryRun) {
144
+ for (const runner of staleRunners) {
145
+ if (runner.pid) {
146
+ killProcess(runner.pid);
147
+ }
148
+ global.db.prepare('DELETE FROM runners WHERE id = ?').run(runner.id);
149
+ }
150
+ }
151
+ results.push({
152
+ action: 'cleaned',
153
+ reason: `Cleaned ${staleRunners.length} stale runner(s)`,
154
+ staleRunners: staleRunners.length,
155
+ });
156
+ }
157
+ }
158
+ catch {
159
+ // ignore global DB issues; wakeup will still attempt per-project checks
160
+ }
161
+ // Step 2: Clean zombie lock if present
162
+ const lockStatus = checkLockStatus();
163
+ if (lockStatus.isZombie && lockStatus.pid) {
164
+ log(`Found zombie lock (PID: ${lockStatus.pid}), cleaning...`);
165
+ if (!dryRun) {
166
+ removeLock();
167
+ }
168
+ }
169
+ // Step 3: Get all registered projects from global registry
170
+ const registeredProjects = getRegisteredProjects(false); // enabled only
171
+ if (registeredProjects.length === 0) {
172
+ log('No registered projects found');
173
+ log('Run "steroids projects add <path>" to register a project');
174
+ results.push({
175
+ action: 'none',
176
+ reason: 'No registered projects',
177
+ pendingTasks: 0,
178
+ });
179
+ return results;
180
+ }
181
+ log(`Checking ${registeredProjects.length} registered project(s)...`);
182
+ // Step 4: Check each project and start runners as needed
183
+ for (const project of registeredProjects) {
184
+ // Skip if project directory doesn't exist
185
+ if (!existsSync(project.path)) {
186
+ log(`Skipping ${project.path}: directory not found`);
187
+ results.push({
188
+ action: 'none',
189
+ reason: 'Directory not found',
190
+ projectPath: project.path,
191
+ });
192
+ continue;
193
+ }
194
+ // Phase 6 (live monitoring): best-effort retention cleanup of invocation activity logs.
195
+ // This is safe to run even if the project has no pending tasks.
196
+ let deletedInvocationLogs = 0;
197
+ try {
198
+ const cleanup = cleanupInvocationLogs(project.path, { retentionDays: 7, dryRun });
199
+ deletedInvocationLogs = cleanup.deletedFiles;
200
+ if (cleanup.deletedFiles > 0 && !quiet) {
201
+ log(`Cleaned ${cleanup.deletedFiles} old invocation log(s) in ${project.path}`);
202
+ }
203
+ }
204
+ catch {
205
+ // Ignore cleanup errors; wakeup must remain robust.
206
+ }
207
+ // Skip if project already has an active runner
208
+ // Check for pending work
209
+ const hasWork = await projectHasPendingWork(project.path);
210
+ if (!hasWork) {
211
+ log(`Skipping ${project.path}: no pending tasks`);
212
+ results.push({
213
+ action: 'none',
214
+ reason: 'No pending tasks',
215
+ projectPath: project.path,
216
+ deletedInvocationLogs,
217
+ });
218
+ continue;
219
+ }
220
+ // Step 4a: Recover stuck tasks (best-effort) before deciding whether to (re)start a runner.
221
+ // This is what unblocks orphaned/infinite-hang scenarios without manual intervention.
222
+ let recoveredActions = 0;
223
+ let skippedRecoveryDueToSafetyLimit = false;
224
+ try {
225
+ const { db: projectDb, close: closeProjectDb } = openDatabase(project.path);
226
+ try {
227
+ const config = loadConfig(project.path);
228
+ const recovery = await recoverStuckTasks({
229
+ projectPath: project.path,
230
+ projectDb,
231
+ globalDb: global.db,
232
+ config,
233
+ dryRun,
234
+ });
235
+ recoveredActions = recovery.actions.length;
236
+ skippedRecoveryDueToSafetyLimit = recovery.skippedDueToSafetyLimit;
237
+ if (recoveredActions > 0 && !quiet) {
238
+ log(`Recovered ${recoveredActions} stuck item(s) in ${project.path}`);
239
+ }
240
+ if (skippedRecoveryDueToSafetyLimit && !quiet) {
241
+ log(`Skipping auto-recovery in ${project.path}: safety limit hit (maxIncidentsPerHour)`);
242
+ }
243
+ }
244
+ finally {
245
+ closeProjectDb();
246
+ }
247
+ }
248
+ catch {
249
+ // If recovery can't run (DB missing/corrupt), we still proceed with runner checks.
250
+ }
251
+ // Skip if project already has an active runner (after recovery, which may have killed/removed it).
252
+ if (hasActiveRunnerForProject(project.path)) {
253
+ log(`Skipping ${project.path}: runner already active`);
254
+ results.push({
255
+ action: 'none',
256
+ reason: recoveredActions > 0
257
+ ? `Runner already active (recovered ${recoveredActions} stuck item(s))`
258
+ : 'Runner already active',
259
+ projectPath: project.path,
260
+ recoveredActions,
261
+ skippedRecoveryDueToSafetyLimit,
262
+ deletedInvocationLogs,
263
+ });
264
+ continue;
265
+ }
266
+ // Start runner for this project
267
+ log(`Starting runner for: ${project.path}`);
268
+ if (dryRun) {
269
+ results.push({
270
+ action: 'would_start',
271
+ reason: recoveredActions > 0 ? `Recovered ${recoveredActions} stuck item(s); would start runner (dry-run)` : `Would start runner (dry-run)`,
272
+ projectPath: project.path,
273
+ recoveredActions,
274
+ skippedRecoveryDueToSafetyLimit,
275
+ deletedInvocationLogs,
276
+ });
277
+ continue;
278
+ }
279
+ const startResult = startRunner(project.path);
280
+ if (startResult) {
281
+ results.push({
282
+ action: 'started',
283
+ reason: recoveredActions > 0 ? `Recovered ${recoveredActions} stuck item(s); started runner` : `Started runner`,
284
+ pid: startResult.pid,
285
+ projectPath: project.path,
286
+ recoveredActions,
287
+ skippedRecoveryDueToSafetyLimit,
288
+ deletedInvocationLogs,
289
+ });
290
+ }
291
+ else {
292
+ results.push({
293
+ action: 'none',
294
+ reason: recoveredActions > 0 ? `Recovered ${recoveredActions} stuck item(s); failed to start runner` : 'Failed to start runner',
295
+ projectPath: project.path,
296
+ recoveredActions,
297
+ skippedRecoveryDueToSafetyLimit,
298
+ deletedInvocationLogs,
299
+ });
300
+ }
301
+ }
302
+ // If no specific results, add a summary
303
+ if (results.length === 0) {
304
+ results.push({
305
+ action: 'none',
306
+ reason: 'No action needed',
307
+ });
308
+ }
309
+ return results;
310
+ }
311
+ finally {
312
+ global.close();
313
+ }
314
+ }
315
+ /**
316
+ * Check if wake-up is needed without taking action
317
+ */
318
+ export async function checkWakeupNeeded() {
319
+ const lockStatus = checkLockStatus();
320
+ if (lockStatus.locked && lockStatus.pid) {
321
+ const { db, close } = openGlobalDatabase();
322
+ try {
323
+ const staleRunners = findStaleRunners(db);
324
+ if (staleRunners.length > 0) {
325
+ return {
326
+ needed: true,
327
+ reason: `${staleRunners.length} stale runner(s) need cleanup`,
328
+ };
329
+ }
330
+ return { needed: false, reason: 'Runner is healthy' };
331
+ }
332
+ finally {
333
+ close();
334
+ }
335
+ }
336
+ if (lockStatus.isZombie) {
337
+ return { needed: true, reason: 'Zombie lock needs cleanup' };
338
+ }
339
+ // Check registered projects
340
+ const registeredProjects = getRegisteredProjects(false);
341
+ let projectsWithWork = 0;
342
+ for (const project of registeredProjects) {
343
+ if (existsSync(project.path) && (await projectHasPendingWork(project.path))) {
344
+ projectsWithWork++;
345
+ }
346
+ }
347
+ if (projectsWithWork > 0) {
348
+ return {
349
+ needed: true,
350
+ reason: `${projectsWithWork} project(s) have pending tasks`,
351
+ };
352
+ }
353
+ return { needed: false, reason: 'No pending tasks' };
354
+ }
355
+ //# sourceMappingURL=wakeup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup.js","sourceRoot":"","sources":["../../../../src/runners/wakeup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Path validation utilities for API security
3
+ */
4
+ /**
5
+ * Validate that a path is safe and points to a valid Steroids project
6
+ *
7
+ * @param path - Path to validate
8
+ * @returns True if path is valid and safe
9
+ */
10
+ export declare function isValidProjectPath(path: string): boolean;
11
+ /**
12
+ * Validate request body for path-based operations
13
+ *
14
+ * @param body - Request body
15
+ * @returns Validation result with error message if invalid
16
+ */
17
+ export declare function validatePathRequest(body: unknown): {
18
+ valid: boolean;
19
+ error?: string;
20
+ path?: string;
21
+ };
22
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAoBxD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBpG"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Path validation utilities for API security
3
+ */
4
+ import { existsSync, realpathSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ /**
7
+ * Validate that a path is safe and points to a valid Steroids project
8
+ *
9
+ * @param path - Path to validate
10
+ * @returns True if path is valid and safe
11
+ */
12
+ export function isValidProjectPath(path) {
13
+ try {
14
+ const realPath = realpathSync(path);
15
+ // Must contain .steroids directory with database
16
+ const steroidsDb = join(realPath, '.steroids', 'steroids.db');
17
+ if (!existsSync(steroidsDb)) {
18
+ return false;
19
+ }
20
+ // Must not be system directories
21
+ const forbidden = ['/etc', '/var', '/usr', '/bin', '/sbin', '/System'];
22
+ if (forbidden.some((f) => realPath.startsWith(f))) {
23
+ return false;
24
+ }
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ /**
32
+ * Validate request body for path-based operations
33
+ *
34
+ * @param body - Request body
35
+ * @returns Validation result with error message if invalid
36
+ */
37
+ export function validatePathRequest(body) {
38
+ if (!body || typeof body !== 'object') {
39
+ return { valid: false, error: 'Request body must be an object' };
40
+ }
41
+ const { path } = body;
42
+ if (!path || typeof path !== 'string') {
43
+ return { valid: false, error: 'Request body must contain a "path" string field' };
44
+ }
45
+ if (path.trim().length === 0) {
46
+ return { valid: false, error: 'Path cannot be empty' };
47
+ }
48
+ return { valid: true, path };
49
+ }
50
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAEpC,iDAAiD;QACjD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;QAC9D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,iCAAiC;QACjC,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QACvE,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAa;IAC/C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC;IACnE,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,GAAG,IAA0B,CAAC;IAE5C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,iDAAiD,EAAE,CAAC;IACpF,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;IACzD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import Database from 'better-sqlite3';
2
+ export type SqliteOpenOptions = {
3
+ timeoutMs?: number;
4
+ };
5
+ /**
6
+ * Open a connection suitable for reading a WAL-enabled database that may be
7
+ * concurrently written by another process.
8
+ *
9
+ * IMPORTANT:
10
+ * - In WAL mode, SQLite readers participate in coordination via the `-shm` file.
11
+ * - Opening the main database as SQLITE_OPEN_READONLY can break that coordination
12
+ * and lead to transient IO errors (e.g. SHORT_READ) if the writer checkpoints/truncates.
13
+ *
14
+ * So we open read-write, then enforce read-only at the SQL layer via `PRAGMA query_only=ON`.
15
+ */
16
+ export declare function openSqliteForRead(dbPath: string, opts?: SqliteOpenOptions): Database.Database;
17
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/utils/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,QAAQ,CAAC,QAAQ,CAiBjG"}
@@ -0,0 +1,27 @@
1
+ import Database from 'better-sqlite3';
2
+ /**
3
+ * Open a connection suitable for reading a WAL-enabled database that may be
4
+ * concurrently written by another process.
5
+ *
6
+ * IMPORTANT:
7
+ * - In WAL mode, SQLite readers participate in coordination via the `-shm` file.
8
+ * - Opening the main database as SQLITE_OPEN_READONLY can break that coordination
9
+ * and lead to transient IO errors (e.g. SHORT_READ) if the writer checkpoints/truncates.
10
+ *
11
+ * So we open read-write, then enforce read-only at the SQL layer via `PRAGMA query_only=ON`.
12
+ */
13
+ export function openSqliteForRead(dbPath, opts = {}) {
14
+ const timeoutMs = opts.timeoutMs ?? 5000;
15
+ const db = new Database(dbPath, {
16
+ // Must exist; callers use existence checks only for UX, not correctness.
17
+ fileMustExist: true,
18
+ timeout: timeoutMs,
19
+ });
20
+ // Ensure the connection will wait for locks instead of failing immediately.
21
+ // (The `timeout` option also sets this, but being explicit makes intent clear.)
22
+ db.pragma(`busy_timeout = ${timeoutMs}`);
23
+ // Enforce read-only at the SQL layer while still allowing WAL shared-memory coordination.
24
+ db.pragma('query_only = ON');
25
+ return db;
26
+ }
27
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/utils/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAMtC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,OAA0B,EAAE;IAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;IAEzC,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE;QAC9B,yEAAyE;QACzE,aAAa,EAAE,IAAI;QACnB,OAAO,EAAE,SAAS;KACnB,CAAC,CAAC;IAEH,4EAA4E;IAC5E,gFAAgF;IAChF,EAAE,CAAC,MAAM,CAAC,kBAAkB,SAAS,EAAE,CAAC,CAAC;IAEzC,0FAA0F;IAC1F,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAE7B,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { type StorageBreakdown } from '../../../dist/cleanup/directory-size.js';
2
+ export interface ListStorageInfo {
3
+ storage_bytes: number;
4
+ storage_human: string;
5
+ storage_warning: 'orange' | 'red' | null;
6
+ }
7
+ /**
8
+ * Validate that a query path is a registered project.
9
+ * Uses fs.promises.realpath() to resolve symlinks, then checks the global DB.
10
+ */
11
+ export declare function validateProjectPath(path: string | undefined): Promise<{
12
+ valid: true;
13
+ realPath: string;
14
+ } | {
15
+ valid: false;
16
+ error: string;
17
+ status: number;
18
+ }>;
19
+ /**
20
+ * Get detailed storage breakdown with 60s cache.
21
+ */
22
+ export declare function getCachedStorageBreakdown(projectPath: string, retentionDays?: number, backupRetentionDays?: number): Promise<StorageBreakdown>;
23
+ /**
24
+ * Get lightweight storage info for project list with 5-minute cache.
25
+ * Returns null if not yet computed (triggers background computation).
26
+ */
27
+ export declare function getCachedListStorage(projectPath: string): ListStorageInfo | null;
28
+ /**
29
+ * Bust all caches for a given project path.
30
+ * Called after POST /clear-logs to ensure fresh data.
31
+ */
32
+ export declare function bustStorageCache(projectPath: string): void;
33
+ //# sourceMappingURL=storage-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-cache.d.ts","sourceRoot":"","sources":["../../src/utils/storage-cache.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,yCAAyC,CAAC;AAcjD,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAe9F;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,EACnB,aAAa,GAAE,MAAU,EACzB,mBAAmB,GAAE,MAAW,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CAQ3B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAOhF;AAkBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAG1D"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * In-memory cache for project storage computations.
3
+ * Keyed by project path with configurable TTL.
4
+ */
5
+ import { promises as fs } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { getStorageBreakdown, } from '../../../dist/cleanup/directory-size.js';
8
+ import { getRegisteredProject } from '../../../dist/runners/projects.js';
9
+ const detailCache = new Map();
10
+ const listCache = new Map();
11
+ const DETAIL_TTL_MS = 60_000; // 60 seconds
12
+ const LIST_TTL_MS = 300_000; // 5 minutes
13
+ /**
14
+ * Validate that a query path is a registered project.
15
+ * Uses fs.promises.realpath() to resolve symlinks, then checks the global DB.
16
+ */
17
+ export async function validateProjectPath(path) {
18
+ if (!path || typeof path !== 'string' || path.trim().length === 0) {
19
+ return { valid: false, error: 'Query parameter "path" is required', status: 400 };
20
+ }
21
+ let realPath;
22
+ try {
23
+ realPath = await fs.realpath(path);
24
+ }
25
+ catch {
26
+ return { valid: false, error: 'Path does not exist or is inaccessible', status: 404 };
27
+ }
28
+ const project = getRegisteredProject(realPath);
29
+ if (!project) {
30
+ return { valid: false, error: 'Path is not a registered project', status: 403 };
31
+ }
32
+ return { valid: true, realPath };
33
+ }
34
+ /**
35
+ * Get detailed storage breakdown with 60s cache.
36
+ */
37
+ export async function getCachedStorageBreakdown(projectPath, retentionDays = 7, backupRetentionDays = 30) {
38
+ const entry = detailCache.get(projectPath);
39
+ if (entry && Date.now() < entry.expiresAt)
40
+ return entry.data;
41
+ const steroidsDir = join(projectPath, '.steroids');
42
+ const data = await getStorageBreakdown(steroidsDir, retentionDays, backupRetentionDays);
43
+ detailCache.set(projectPath, { data, expiresAt: Date.now() + DETAIL_TTL_MS });
44
+ return data;
45
+ }
46
+ /**
47
+ * Get lightweight storage info for project list with 5-minute cache.
48
+ * Returns null if not yet computed (triggers background computation).
49
+ */
50
+ export function getCachedListStorage(projectPath) {
51
+ const entry = listCache.get(projectPath);
52
+ if (entry && Date.now() < entry.expiresAt)
53
+ return entry.data;
54
+ // Trigger background computation (don't block the list response)
55
+ computeListStorageInBackground(projectPath);
56
+ return entry?.data ?? null; // Return stale data if available, null otherwise
57
+ }
58
+ function computeListStorageInBackground(projectPath) {
59
+ const steroidsDir = join(projectPath, '.steroids');
60
+ getStorageBreakdown(steroidsDir)
61
+ .then((breakdown) => {
62
+ listCache.set(projectPath, {
63
+ data: {
64
+ storage_bytes: breakdown.total_bytes,
65
+ storage_human: breakdown.total_human,
66
+ storage_warning: breakdown.threshold_warning,
67
+ },
68
+ expiresAt: Date.now() + LIST_TTL_MS,
69
+ });
70
+ })
71
+ .catch(() => { });
72
+ }
73
+ /**
74
+ * Bust all caches for a given project path.
75
+ * Called after POST /clear-logs to ensure fresh data.
76
+ */
77
+ export function bustStorageCache(projectPath) {
78
+ detailCache.delete(projectPath);
79
+ listCache.delete(projectPath);
80
+ }
81
+ //# sourceMappingURL=storage-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-cache.js","sourceRoot":"","sources":["../../src/utils/storage-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Path validation utilities for API security
3
+ */
4
+ /**
5
+ * Validate that a path is safe and points to a valid Steroids project
6
+ *
7
+ * @param path - Path to validate
8
+ * @returns True if path is valid and safe
9
+ */
10
+ export declare function isValidProjectPath(path: string): boolean;
11
+ /**
12
+ * Validate request body for path-based operations
13
+ *
14
+ * @param body - Request body
15
+ * @returns Validation result with error message if invalid
16
+ */
17
+ export declare function validatePathRequest(body: unknown): {
18
+ valid: boolean;
19
+ error?: string;
20
+ path?: string;
21
+ };
22
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAuBxD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBpG"}