adispatchd 0.0.0-stage → 0.1.0

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 (396) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -2
  3. package/dist/api/errors.d.ts +27 -0
  4. package/dist/api/errors.js +80 -0
  5. package/dist/api/errors.js.map +1 -0
  6. package/dist/api/index.d.ts +10 -0
  7. package/dist/api/index.js +11 -0
  8. package/dist/api/index.js.map +1 -0
  9. package/dist/api/logs.d.ts +30 -0
  10. package/dist/api/logs.js +88 -0
  11. package/dist/api/logs.js.map +1 -0
  12. package/dist/api/serialize.d.ts +65 -0
  13. package/dist/api/serialize.js +135 -0
  14. package/dist/api/serialize.js.map +1 -0
  15. package/dist/api/server.d.ts +89 -0
  16. package/dist/api/server.js +751 -0
  17. package/dist/api/server.js.map +1 -0
  18. package/dist/api/sse.d.ts +90 -0
  19. package/dist/api/sse.js +225 -0
  20. package/dist/api/sse.js.map +1 -0
  21. package/dist/app.d.ts +173 -0
  22. package/dist/app.js +488 -0
  23. package/dist/app.js.map +1 -0
  24. package/dist/cli/config-validate.d.ts +6 -0
  25. package/dist/cli/config-validate.js +39 -0
  26. package/dist/cli/config-validate.js.map +1 -0
  27. package/dist/cli/control.d.ts +31 -0
  28. package/dist/cli/control.js +186 -0
  29. package/dist/cli/control.js.map +1 -0
  30. package/dist/cli/http-client.d.ts +57 -0
  31. package/dist/cli/http-client.js +174 -0
  32. package/dist/cli/http-client.js.map +1 -0
  33. package/dist/cli/index.d.ts +14 -0
  34. package/dist/cli/index.js +14 -0
  35. package/dist/cli/index.js.map +1 -0
  36. package/dist/cli/init-templates.d.ts +20 -0
  37. package/dist/cli/init-templates.js +275 -0
  38. package/dist/cli/init-templates.js.map +1 -0
  39. package/dist/cli/init.d.ts +16 -0
  40. package/dist/cli/init.js +107 -0
  41. package/dist/cli/init.js.map +1 -0
  42. package/dist/cli/main.d.ts +2 -0
  43. package/dist/cli/main.js +25 -0
  44. package/dist/cli/main.js.map +1 -0
  45. package/dist/cli/program.d.ts +102 -0
  46. package/dist/cli/program.js +133 -0
  47. package/dist/cli/program.js.map +1 -0
  48. package/dist/cli/service.d.ts +27 -0
  49. package/dist/cli/service.js +252 -0
  50. package/dist/cli/service.js.map +1 -0
  51. package/dist/cli/start.d.ts +22 -0
  52. package/dist/cli/start.js +133 -0
  53. package/dist/cli/start.js.map +1 -0
  54. package/dist/cli/workflow-dry-run.d.ts +13 -0
  55. package/dist/cli/workflow-dry-run.js +144 -0
  56. package/dist/cli/workflow-dry-run.js.map +1 -0
  57. package/dist/config/field-catalog.d.ts +33 -0
  58. package/dist/config/field-catalog.js +172 -0
  59. package/dist/config/field-catalog.js.map +1 -0
  60. package/dist/config/filter-schema.d.ts +15 -0
  61. package/dist/config/filter-schema.js +110 -0
  62. package/dist/config/filter-schema.js.map +1 -0
  63. package/dist/config/index.d.ts +15 -0
  64. package/dist/config/index.js +16 -0
  65. package/dist/config/index.js.map +1 -0
  66. package/dist/config/issues.d.ts +27 -0
  67. package/dist/config/issues.js +37 -0
  68. package/dist/config/issues.js.map +1 -0
  69. package/dist/config/loader.d.ts +32 -0
  70. package/dist/config/loader.js +250 -0
  71. package/dist/config/loader.js.map +1 -0
  72. package/dist/config/manager.d.ts +153 -0
  73. package/dist/config/manager.js +276 -0
  74. package/dist/config/manager.js.map +1 -0
  75. package/dist/config/model.d.ts +334 -0
  76. package/dist/config/model.js +2 -0
  77. package/dist/config/model.js.map +1 -0
  78. package/dist/config/normalize.d.ts +20 -0
  79. package/dist/config/normalize.js +200 -0
  80. package/dist/config/normalize.js.map +1 -0
  81. package/dist/config/parameters.d.ts +37 -0
  82. package/dist/config/parameters.js +71 -0
  83. package/dist/config/parameters.js.map +1 -0
  84. package/dist/config/public.d.ts +6 -0
  85. package/dist/config/public.js +18 -0
  86. package/dist/config/public.js.map +1 -0
  87. package/dist/config/schemas.d.ts +538 -0
  88. package/dist/config/schemas.js +321 -0
  89. package/dist/config/schemas.js.map +1 -0
  90. package/dist/config/snapshot.d.ts +38 -0
  91. package/dist/config/snapshot.js +57 -0
  92. package/dist/config/snapshot.js.map +1 -0
  93. package/dist/config/validate.d.ts +33 -0
  94. package/dist/config/validate.js +346 -0
  95. package/dist/config/validate.js.map +1 -0
  96. package/dist/core/clock.d.ts +59 -0
  97. package/dist/core/clock.js +133 -0
  98. package/dist/core/clock.js.map +1 -0
  99. package/dist/core/duration.d.ts +19 -0
  100. package/dist/core/duration.js +93 -0
  101. package/dist/core/duration.js.map +1 -0
  102. package/dist/core/errors.d.ts +62 -0
  103. package/dist/core/errors.js +72 -0
  104. package/dist/core/errors.js.map +1 -0
  105. package/dist/core/ids.d.ts +57 -0
  106. package/dist/core/ids.js +88 -0
  107. package/dist/core/ids.js.map +1 -0
  108. package/dist/core/index.d.ts +13 -0
  109. package/dist/core/index.js +14 -0
  110. package/dist/core/index.js.map +1 -0
  111. package/dist/core/logger.d.ts +100 -0
  112. package/dist/core/logger.js +207 -0
  113. package/dist/core/logger.js.map +1 -0
  114. package/dist/core/paths.d.ts +84 -0
  115. package/dist/core/paths.js +138 -0
  116. package/dist/core/paths.js.map +1 -0
  117. package/dist/core/template.d.ts +136 -0
  118. package/dist/core/template.js +261 -0
  119. package/dist/core/template.js.map +1 -0
  120. package/dist/core/types.d.ts +544 -0
  121. package/dist/core/types.js +172 -0
  122. package/dist/core/types.js.map +1 -0
  123. package/dist/core/worker-protocol.d.ts +102 -0
  124. package/dist/core/worker-protocol.js +75 -0
  125. package/dist/core/worker-protocol.js.map +1 -0
  126. package/dist/executors/classify.d.ts +25 -0
  127. package/dist/executors/classify.js +54 -0
  128. package/dist/executors/classify.js.map +1 -0
  129. package/dist/executors/dispatcher.d.ts +35 -0
  130. package/dist/executors/dispatcher.js +68 -0
  131. package/dist/executors/dispatcher.js.map +1 -0
  132. package/dist/executors/env.d.ts +43 -0
  133. package/dist/executors/env.js +66 -0
  134. package/dist/executors/env.js.map +1 -0
  135. package/dist/executors/event-parser.d.ts +41 -0
  136. package/dist/executors/event-parser.js +106 -0
  137. package/dist/executors/event-parser.js.map +1 -0
  138. package/dist/executors/index.d.ts +12 -0
  139. package/dist/executors/index.js +13 -0
  140. package/dist/executors/index.js.map +1 -0
  141. package/dist/executors/log-capture.d.ts +57 -0
  142. package/dist/executors/log-capture.js +155 -0
  143. package/dist/executors/log-capture.js.map +1 -0
  144. package/dist/executors/process-identity.d.ts +124 -0
  145. package/dist/executors/process-identity.js +283 -0
  146. package/dist/executors/process-identity.js.map +1 -0
  147. package/dist/executors/shell-executor.d.ts +35 -0
  148. package/dist/executors/shell-executor.js +478 -0
  149. package/dist/executors/shell-executor.js.map +1 -0
  150. package/dist/executors/stream-redactor.d.ts +40 -0
  151. package/dist/executors/stream-redactor.js +89 -0
  152. package/dist/executors/stream-redactor.js.map +1 -0
  153. package/dist/executors/workspace.d.ts +10 -0
  154. package/dist/executors/workspace.js +47 -0
  155. package/dist/executors/workspace.js.map +1 -0
  156. package/dist/filters/evaluate.d.ts +77 -0
  157. package/dist/filters/evaluate.js +187 -0
  158. package/dist/filters/evaluate.js.map +1 -0
  159. package/dist/filters/fields.d.ts +35 -0
  160. package/dist/filters/fields.js +75 -0
  161. package/dist/filters/fields.js.map +1 -0
  162. package/dist/filters/index.d.ts +11 -0
  163. package/dist/filters/index.js +12 -0
  164. package/dist/filters/index.js.map +1 -0
  165. package/dist/filters/iso-date.d.ts +10 -0
  166. package/dist/filters/iso-date.js +42 -0
  167. package/dist/filters/iso-date.js.map +1 -0
  168. package/dist/filters/predicate.d.ts +24 -0
  169. package/dist/filters/predicate.js +178 -0
  170. package/dist/filters/predicate.js.map +1 -0
  171. package/dist/filters/priority.d.ts +51 -0
  172. package/dist/filters/priority.js +91 -0
  173. package/dist/filters/priority.js.map +1 -0
  174. package/dist/filters/pushdown.d.ts +12 -0
  175. package/dist/filters/pushdown.js +52 -0
  176. package/dist/filters/pushdown.js.map +1 -0
  177. package/dist/filters/shell-check.d.ts +110 -0
  178. package/dist/filters/shell-check.js +304 -0
  179. package/dist/filters/shell-check.js.map +1 -0
  180. package/dist/identities/github-app.d.ts +55 -0
  181. package/dist/identities/github-app.js +160 -0
  182. package/dist/identities/github-app.js.map +1 -0
  183. package/dist/identities/index.d.ts +6 -0
  184. package/dist/identities/index.js +7 -0
  185. package/dist/identities/index.js.map +1 -0
  186. package/dist/identities/jwt.d.ts +24 -0
  187. package/dist/identities/jwt.js +56 -0
  188. package/dist/identities/jwt.js.map +1 -0
  189. package/dist/lifecycle/context.d.ts +106 -0
  190. package/dist/lifecycle/context.js +170 -0
  191. package/dist/lifecycle/context.js.map +1 -0
  192. package/dist/lifecycle/engine.d.ts +185 -0
  193. package/dist/lifecycle/engine.js +551 -0
  194. package/dist/lifecycle/engine.js.map +1 -0
  195. package/dist/lifecycle/errors.d.ts +48 -0
  196. package/dist/lifecycle/errors.js +71 -0
  197. package/dist/lifecycle/errors.js.map +1 -0
  198. package/dist/lifecycle/index.d.ts +9 -0
  199. package/dist/lifecycle/index.js +9 -0
  200. package/dist/lifecycle/index.js.map +1 -0
  201. package/dist/lifecycle/performers.d.ts +66 -0
  202. package/dist/lifecycle/performers.js +179 -0
  203. package/dist/lifecycle/performers.js.map +1 -0
  204. package/dist/notifications/index.d.ts +6 -0
  205. package/dist/notifications/index.js +7 -0
  206. package/dist/notifications/index.js.map +1 -0
  207. package/dist/notifications/service.d.ts +115 -0
  208. package/dist/notifications/service.js +364 -0
  209. package/dist/notifications/service.js.map +1 -0
  210. package/dist/persistence/actions.d.ts +101 -0
  211. package/dist/persistence/actions.js +196 -0
  212. package/dist/persistence/actions.js.map +1 -0
  213. package/dist/persistence/blocks.d.ts +85 -0
  214. package/dist/persistence/blocks.js +140 -0
  215. package/dist/persistence/blocks.js.map +1 -0
  216. package/dist/persistence/cron-firings.d.ts +55 -0
  217. package/dist/persistence/cron-firings.js +70 -0
  218. package/dist/persistence/cron-firings.js.map +1 -0
  219. package/dist/persistence/database.d.ts +60 -0
  220. package/dist/persistence/database.js +124 -0
  221. package/dist/persistence/database.js.map +1 -0
  222. package/dist/persistence/errors.d.ts +39 -0
  223. package/dist/persistence/errors.js +67 -0
  224. package/dist/persistence/errors.js.map +1 -0
  225. package/dist/persistence/events.d.ts +72 -0
  226. package/dist/persistence/events.js +83 -0
  227. package/dist/persistence/events.js.map +1 -0
  228. package/dist/persistence/index.d.ts +18 -0
  229. package/dist/persistence/index.js +19 -0
  230. package/dist/persistence/index.js.map +1 -0
  231. package/dist/persistence/instance-lock.d.ts +26 -0
  232. package/dist/persistence/instance-lock.js +117 -0
  233. package/dist/persistence/instance-lock.js.map +1 -0
  234. package/dist/persistence/lock-keys.d.ts +16 -0
  235. package/dist/persistence/lock-keys.js +41 -0
  236. package/dist/persistence/lock-keys.js.map +1 -0
  237. package/dist/persistence/notifications.d.ts +113 -0
  238. package/dist/persistence/notifications.js +213 -0
  239. package/dist/persistence/notifications.js.map +1 -0
  240. package/dist/persistence/persistence.d.ts +46 -0
  241. package/dist/persistence/persistence.js +42 -0
  242. package/dist/persistence/persistence.js.map +1 -0
  243. package/dist/persistence/retention.d.ts +46 -0
  244. package/dist/persistence/retention.js +68 -0
  245. package/dist/persistence/retention.js.map +1 -0
  246. package/dist/persistence/runs.d.ts +215 -0
  247. package/dist/persistence/runs.js +417 -0
  248. package/dist/persistence/runs.js.map +1 -0
  249. package/dist/persistence/schema.d.ts +16 -0
  250. package/dist/persistence/schema.js +183 -0
  251. package/dist/persistence/schema.js.map +1 -0
  252. package/dist/persistence/sqlite.d.ts +49 -0
  253. package/dist/persistence/sqlite.js +131 -0
  254. package/dist/persistence/sqlite.js.map +1 -0
  255. package/dist/runs/controller.d.ts +307 -0
  256. package/dist/runs/controller.js +1078 -0
  257. package/dist/runs/controller.js.map +1 -0
  258. package/dist/runs/errors.d.ts +20 -0
  259. package/dist/runs/errors.js +26 -0
  260. package/dist/runs/errors.js.map +1 -0
  261. package/dist/runs/event-bus.d.ts +106 -0
  262. package/dist/runs/event-bus.js +105 -0
  263. package/dist/runs/event-bus.js.map +1 -0
  264. package/dist/runs/execution.d.ts +39 -0
  265. package/dist/runs/execution.js +146 -0
  266. package/dist/runs/execution.js.map +1 -0
  267. package/dist/runs/index.d.ts +12 -0
  268. package/dist/runs/index.js +13 -0
  269. package/dist/runs/index.js.map +1 -0
  270. package/dist/runs/retention.d.ts +64 -0
  271. package/dist/runs/retention.js +232 -0
  272. package/dist/runs/retention.js.map +1 -0
  273. package/dist/runs/workspace.d.ts +20 -0
  274. package/dist/runs/workspace.js +51 -0
  275. package/dist/runs/workspace.js.map +1 -0
  276. package/dist/scheduler/checks.d.ts +41 -0
  277. package/dist/scheduler/checks.js +111 -0
  278. package/dist/scheduler/checks.js.map +1 -0
  279. package/dist/scheduler/cron.d.ts +32 -0
  280. package/dist/scheduler/cron.js +83 -0
  281. package/dist/scheduler/cron.js.map +1 -0
  282. package/dist/scheduler/dry-run.d.ts +42 -0
  283. package/dist/scheduler/dry-run.js +167 -0
  284. package/dist/scheduler/dry-run.js.map +1 -0
  285. package/dist/scheduler/errors.d.ts +22 -0
  286. package/dist/scheduler/errors.js +22 -0
  287. package/dist/scheduler/errors.js.map +1 -0
  288. package/dist/scheduler/format.d.ts +3 -0
  289. package/dist/scheduler/format.js +73 -0
  290. package/dist/scheduler/format.js.map +1 -0
  291. package/dist/scheduler/index.d.ts +13 -0
  292. package/dist/scheduler/index.js +14 -0
  293. package/dist/scheduler/index.js.map +1 -0
  294. package/dist/scheduler/planner.d.ts +88 -0
  295. package/dist/scheduler/planner.js +255 -0
  296. package/dist/scheduler/planner.js.map +1 -0
  297. package/dist/scheduler/scheduler.d.ts +160 -0
  298. package/dist/scheduler/scheduler.js +680 -0
  299. package/dist/scheduler/scheduler.js.map +1 -0
  300. package/dist/scheduler/state.d.ts +26 -0
  301. package/dist/scheduler/state.js +25 -0
  302. package/dist/scheduler/state.js.map +1 -0
  303. package/dist/scheduler/types.d.ts +197 -0
  304. package/dist/scheduler/types.js +3 -0
  305. package/dist/scheduler/types.js.map +1 -0
  306. package/dist/service/index.d.ts +6 -0
  307. package/dist/service/index.js +7 -0
  308. package/dist/service/index.js.map +1 -0
  309. package/dist/service/install.d.ts +93 -0
  310. package/dist/service/install.js +250 -0
  311. package/dist/service/install.js.map +1 -0
  312. package/dist/service/unit.d.ts +50 -0
  313. package/dist/service/unit.js +171 -0
  314. package/dist/service/unit.js.map +1 -0
  315. package/dist/sources/github/adapter.d.ts +99 -0
  316. package/dist/sources/github/adapter.js +336 -0
  317. package/dist/sources/github/adapter.js.map +1 -0
  318. package/dist/sources/github/fields.d.ts +8 -0
  319. package/dist/sources/github/fields.js +72 -0
  320. package/dist/sources/github/fields.js.map +1 -0
  321. package/dist/sources/github/http.d.ts +100 -0
  322. package/dist/sources/github/http.js +288 -0
  323. package/dist/sources/github/http.js.map +1 -0
  324. package/dist/sources/github/index.d.ts +6 -0
  325. package/dist/sources/github/index.js +7 -0
  326. package/dist/sources/github/index.js.map +1 -0
  327. package/dist/sources/github/pushdown.d.ts +14 -0
  328. package/dist/sources/github/pushdown.js +47 -0
  329. package/dist/sources/github/pushdown.js.map +1 -0
  330. package/dist/sources/github/refs.d.ts +17 -0
  331. package/dist/sources/github/refs.js +61 -0
  332. package/dist/sources/github/refs.js.map +1 -0
  333. package/dist/sources/index.d.ts +6 -0
  334. package/dist/sources/index.js +7 -0
  335. package/dist/sources/index.js.map +1 -0
  336. package/dist/ui/index.d.ts +12 -0
  337. package/dist/ui/index.js +28 -0
  338. package/dist/ui/index.js.map +1 -0
  339. package/dist/ui/public/app.css +239 -0
  340. package/dist/ui/public/favicon.svg +1 -0
  341. package/dist/ui/public/index.html +27 -0
  342. package/dist/ui/public/js/client/api.js +68 -0
  343. package/dist/ui/public/js/client/app.js +146 -0
  344. package/dist/ui/public/js/client/dom.js +34 -0
  345. package/dist/ui/public/js/client/page.js +9 -0
  346. package/dist/ui/public/js/client/pages/actions.js +35 -0
  347. package/dist/ui/public/js/client/pages/blocked.js +58 -0
  348. package/dist/ui/public/js/client/pages/config.js +126 -0
  349. package/dist/ui/public/js/client/pages/dashboard.js +175 -0
  350. package/dist/ui/public/js/client/pages/run.js +367 -0
  351. package/dist/ui/public/js/client/pages/runs.js +72 -0
  352. package/dist/ui/public/js/client/pages/views.js +80 -0
  353. package/dist/ui/public/js/client/pages/workflows.js +291 -0
  354. package/dist/ui/public/js/client/types.js +1 -0
  355. package/dist/ui/public/js/client/ui.js +152 -0
  356. package/dist/ui/public/js/shared/dom.js +77 -0
  357. package/dist/ui/public/js/shared/events.js +88 -0
  358. package/dist/ui/public/js/shared/format.js +213 -0
  359. package/dist/ui/public/js/shared/logs.js +136 -0
  360. package/dist/ui/public/js/shared/params.js +90 -0
  361. package/dist/ui/public/js/shared/route.js +36 -0
  362. package/dist/ui/public/js/shared/sort.js +70 -0
  363. package/dist/ui/public/js/shared/sse.js +193 -0
  364. package/dist/ui/public/js/shared/throttle.js +65 -0
  365. package/dist/views/index.d.ts +6 -0
  366. package/dist/views/index.js +7 -0
  367. package/dist/views/index.js.map +1 -0
  368. package/dist/views/service.d.ts +131 -0
  369. package/dist/views/service.js +264 -0
  370. package/dist/views/service.js.map +1 -0
  371. package/docs/api.md +239 -0
  372. package/docs/configuration-reference.md +1785 -0
  373. package/docs/cron-semantics.md +96 -0
  374. package/docs/manual-tests/github-fixture/config.yaml +30 -0
  375. package/docs/manual-tests/github-fixture/helpers.sh +93 -0
  376. package/docs/manual-tests/github-fixture/identities.yaml +18 -0
  377. package/docs/manual-tests/github-fixture/scripts/test-worker.sh +100 -0
  378. package/docs/manual-tests/github-fixture/sources.yaml +13 -0
  379. package/docs/manual-tests/github-fixture/views/dependencies.yaml +17 -0
  380. package/docs/manual-tests/github-fixture/views/needs-maintainer.yaml +17 -0
  381. package/docs/manual-tests/github-fixture/workflows/coder-test.yaml +76 -0
  382. package/docs/manual-tests/github-fixture/workflows/commenter-test.yaml +63 -0
  383. package/docs/manual-tests/github-fixture/workflows/reviewer-test.yaml +60 -0
  384. package/docs/manual-tests/github.md +1308 -0
  385. package/docs/manual-tests/process-supervision.md +64 -0
  386. package/docs/manual-tests/systemd.md +68 -0
  387. package/docs/operations.md +271 -0
  388. package/docs/worker-protocol.md +145 -0
  389. package/examples/config/config.yaml +35 -0
  390. package/examples/config/identities.yaml +18 -0
  391. package/examples/config/sources.yaml +13 -0
  392. package/examples/config/views/needs-maintainer.yaml +15 -0
  393. package/examples/config/workflows/coder-project-a.yaml +86 -0
  394. package/examples/config/workflows/deploy.yaml +15 -0
  395. package/examples/config/workflows/nightly-maintenance.yaml +13 -0
  396. package/package.json +75 -4
@@ -0,0 +1,64 @@
1
+ import { type Clock, type Logger, type Timers } from '../core/index.js';
2
+ import type { RetentionConfig } from '../config/index.js';
3
+ import { type Repositories, type RunRetentionCategory } from '../persistence/index.js';
4
+ /** Result of one retention pass. */
5
+ export interface RetentionReport {
6
+ startedAt: string;
7
+ finishedAt: string;
8
+ deletedRuns: Record<RunRetentionCategory, number>;
9
+ deletedRunIds: string[];
10
+ deletedBlocks: number;
11
+ deletedNotifications: number;
12
+ deletedCronFirings: number;
13
+ orphanDirsRemoved: number;
14
+ /** File/workspace deletion problems (rows are already gone). */
15
+ errors: Array<{
16
+ runId?: string;
17
+ path?: string;
18
+ message: string;
19
+ }>;
20
+ }
21
+ /** Options of {@link RetentionService}. */
22
+ export interface RetentionServiceOptions {
23
+ repositories: Pick<Repositories, 'runs' | 'retention'>;
24
+ dataDir: string;
25
+ /** Live retention settings (`config.global.retention`). */
26
+ settings: () => RetentionConfig;
27
+ clock?: Clock;
28
+ timers?: Timers;
29
+ logger?: Logger;
30
+ /** Runs per query batch (default 200). */
31
+ batchSize?: number;
32
+ /** Min. age of orphaned run directories before they are removed (default 24 h). */
33
+ orphanMinAgeMs?: number;
34
+ /** Called after each pass (diagnostics/SSE). */
35
+ onReport?: (report: RetentionReport) => void;
36
+ }
37
+ /** See module docs. */
38
+ export declare class RetentionService {
39
+ private readonly options;
40
+ private readonly clock;
41
+ private readonly timers;
42
+ private readonly logger;
43
+ private readonly batchSize;
44
+ private timer;
45
+ private running;
46
+ private stopped;
47
+ private last;
48
+ constructor(options: RetentionServiceOptions);
49
+ /** Report of the last pass, if any. */
50
+ get lastReport(): RetentionReport | null;
51
+ /** Schedule periodic passes: first after `initialDelayMs` (default 60 s), then every `cleanup_interval`. */
52
+ start(initialDelayMs?: number): void;
53
+ /** Stop the timer and wait for a running pass. */
54
+ stop(): Promise<void>;
55
+ private arm;
56
+ /** Run one pass now (a call during a running pass returns that pass). */
57
+ runOnce(): Promise<RetentionReport>;
58
+ private pass;
59
+ private deleteRuns;
60
+ private removeRunFiles;
61
+ private removeDir;
62
+ /** Remove `runs/<id>` / `logs/<id>` directories whose run row no longer exists (e.g. after a failed delete). */
63
+ private removeOrphans;
64
+ }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Periodic retention (Spec §5.1, §21): deletes old runs (DB rows incl.
3
+ * events/actions, `runs/<id>/`, `logs/<id>/`, the workspace below
4
+ * `workspace.base_dir`), cleared blocks, finished notifications and old cron
5
+ * firings according to `retention.*`.
6
+ *
7
+ * Protection rules are enforced by the persistence layer and re-checked in
8
+ * the deleting transaction: active runs / lock holders, unfinished or failed
9
+ * lifecycle chains and runs of an active retry block are never deleted.
10
+ * Files are deleted only after their row was deleted; the instance lock files
11
+ * (`adispatchd.lock*`) are never touched – only the `runs/`, `logs/`
12
+ * sub-directories and workspaces strictly below the base directory are.
13
+ */
14
+ import { readdir, rm, stat } from 'node:fs/promises';
15
+ import path from 'node:path';
16
+ import { dataPaths, errorMessage, isValidRunId, noopLogger, resolveStrictlyWithin, systemClock, systemTimers, } from '../core/index.js';
17
+ import { INSTANCE_LOCK_FILES } from '../persistence/index.js';
18
+ import { removeWorkspace } from './workspace.js';
19
+ /** Fallback age of cron firings when `completed_runs` is `never`. */
20
+ const CRON_FIRINGS_FALLBACK_MS = 30 * 24 * 60 * 60_000;
21
+ /** Directories in `runs/`/`logs/` without a DB row are removed only after this age. */
22
+ const DEFAULT_ORPHAN_MIN_AGE_MS = 24 * 60 * 60_000;
23
+ /** See module docs. */
24
+ export class RetentionService {
25
+ options;
26
+ clock;
27
+ timers;
28
+ logger;
29
+ batchSize;
30
+ timer;
31
+ running;
32
+ stopped = false;
33
+ last = null;
34
+ constructor(options) {
35
+ this.options = options;
36
+ this.clock = options.clock ?? systemClock;
37
+ this.timers = options.timers ?? systemTimers;
38
+ this.logger = (options.logger ?? noopLogger).child({ component: 'retention' });
39
+ this.batchSize = options.batchSize ?? 200;
40
+ }
41
+ /** Report of the last pass, if any. */
42
+ get lastReport() {
43
+ return this.last;
44
+ }
45
+ /** Schedule periodic passes: first after `initialDelayMs` (default 60 s), then every `cleanup_interval`. */
46
+ start(initialDelayMs = 60_000) {
47
+ if (this.stopped)
48
+ return;
49
+ this.arm(initialDelayMs);
50
+ }
51
+ /** Stop the timer and wait for a running pass. */
52
+ async stop() {
53
+ this.stopped = true;
54
+ if (this.timer)
55
+ this.timers.clearTimeout(this.timer);
56
+ this.timer = undefined;
57
+ await this.running?.catch(() => undefined);
58
+ }
59
+ arm(delayMs) {
60
+ if (this.stopped)
61
+ return;
62
+ if (this.timer)
63
+ this.timers.clearTimeout(this.timer);
64
+ this.timer = this.timers.setTimeout(() => {
65
+ this.timer = undefined;
66
+ this.runOnce()
67
+ .catch((err) => this.logger.error('retention pass failed', { error: errorMessage(err) }))
68
+ .finally(() => this.arm(this.options.settings().cleanupIntervalMs));
69
+ }, Math.max(0, delayMs));
70
+ }
71
+ /** Run one pass now (a call during a running pass returns that pass). */
72
+ runOnce() {
73
+ this.running ??= this.pass().finally(() => {
74
+ this.running = undefined;
75
+ });
76
+ return this.running;
77
+ }
78
+ async pass() {
79
+ const settings = this.options.settings();
80
+ const now = this.clock.now();
81
+ const report = {
82
+ startedAt: now.toISOString(),
83
+ finishedAt: now.toISOString(),
84
+ deletedRuns: { successful: 0, failed: 0, blocked: 0 },
85
+ deletedRunIds: [],
86
+ deletedBlocks: 0,
87
+ deletedNotifications: 0,
88
+ deletedCronFirings: 0,
89
+ orphanDirsRemoved: 0,
90
+ errors: [],
91
+ };
92
+ const cutoff = (ms) => (ms === null ? null : new Date(now.getTime() - ms));
93
+ const categories = [
94
+ ['successful', cutoff(settings.completedRunsMs)],
95
+ ['failed', cutoff(settings.failedRunsMs)],
96
+ ['blocked', cutoff(settings.blockedRunsMs)],
97
+ ];
98
+ for (const [category, before] of categories) {
99
+ if (before === null || this.stopped)
100
+ continue;
101
+ await this.deleteRuns(category, before, report);
102
+ }
103
+ const { retention } = this.options.repositories;
104
+ const blocksBefore = cutoff(settings.blockedRunsMs);
105
+ if (blocksBefore)
106
+ report.deletedBlocks = retention.deleteClearedBlocks(blocksBefore);
107
+ const notificationsBefore = cutoff(settings.failedRunsMs);
108
+ if (notificationsBefore)
109
+ report.deletedNotifications = retention.deleteNotifications(notificationsBefore);
110
+ const firingsBefore = cutoff(settings.completedRunsMs ?? CRON_FIRINGS_FALLBACK_MS);
111
+ if (firingsBefore)
112
+ report.deletedCronFirings = retention.deleteCronFirings(firingsBefore);
113
+ if (!this.stopped)
114
+ await this.removeOrphans(report);
115
+ report.finishedAt = this.clock.now().toISOString();
116
+ this.last = report;
117
+ const total = report.deletedRuns.successful + report.deletedRuns.failed + report.deletedRuns.blocked;
118
+ this.logger.info('retention pass finished', {
119
+ deletedRuns: report.deletedRuns,
120
+ deletedBlocks: report.deletedBlocks,
121
+ deletedNotifications: report.deletedNotifications,
122
+ deletedCronFirings: report.deletedCronFirings,
123
+ orphanDirsRemoved: report.orphanDirsRemoved,
124
+ errors: report.errors.length,
125
+ total,
126
+ });
127
+ try {
128
+ this.options.onReport?.(report);
129
+ }
130
+ catch (err) {
131
+ this.logger.warn('retention report listener failed', { error: errorMessage(err) });
132
+ }
133
+ return report;
134
+ }
135
+ async deleteRuns(category, before, report) {
136
+ const { runs, retention } = this.options.repositories;
137
+ const seen = new Set();
138
+ for (;;) {
139
+ if (this.stopped)
140
+ return;
141
+ const batch = retention.runCandidates(category, before, this.batchSize).filter((c) => !seen.has(c.runId));
142
+ if (batch.length === 0)
143
+ return;
144
+ for (const candidate of batch) {
145
+ seen.add(candidate.runId);
146
+ const run = runs.get(candidate.runId);
147
+ const baseDir = snapshotBaseDir(run?.configSnapshot);
148
+ // Re-checks the protection rules in the deleting transaction.
149
+ if (!retention.deleteRun(candidate.runId))
150
+ continue;
151
+ report.deletedRuns[category]++;
152
+ report.deletedRunIds.push(candidate.runId);
153
+ await this.removeRunFiles(candidate.runId, candidate.workspacePath, baseDir, report);
154
+ }
155
+ // Yield between batches so HTTP/SSE stay responsive.
156
+ await new Promise((resolve) => setImmediate(resolve));
157
+ }
158
+ }
159
+ async removeRunFiles(runId, workspacePath, baseDir, report) {
160
+ let paths;
161
+ try {
162
+ paths = dataPaths(this.options.dataDir).run(runId);
163
+ }
164
+ catch (err) {
165
+ report.errors.push({ runId, message: errorMessage(err) });
166
+ return;
167
+ }
168
+ for (const dir of [paths.runDir, paths.logDir])
169
+ await this.removeDir(dir, report, runId);
170
+ if (workspacePath && baseDir) {
171
+ const removal = await removeWorkspace(baseDir, workspacePath);
172
+ if (!removal.removed && removal.reason === 'error') {
173
+ report.errors.push({ runId, path: removal.path, message: removal.message ?? 'workspace removal failed' });
174
+ }
175
+ else if (!removal.removed && removal.reason !== 'missing') {
176
+ this.logger.debug('workspace not removed by retention', { runId, reason: removal.reason });
177
+ }
178
+ }
179
+ }
180
+ async removeDir(dir, report, runId) {
181
+ if (INSTANCE_LOCK_FILES.includes(path.basename(dir)))
182
+ return false;
183
+ try {
184
+ await rm(dir, { recursive: true, force: true });
185
+ return true;
186
+ }
187
+ catch (err) {
188
+ report.errors.push({ ...(runId ? { runId } : {}), path: dir, message: errorMessage(err) });
189
+ return false;
190
+ }
191
+ }
192
+ /** Remove `runs/<id>` / `logs/<id>` directories whose run row no longer exists (e.g. after a failed delete). */
193
+ async removeOrphans(report) {
194
+ const paths = dataPaths(this.options.dataDir);
195
+ const minAge = this.options.orphanMinAgeMs ?? DEFAULT_ORPHAN_MIN_AGE_MS;
196
+ const now = this.clock.now().getTime();
197
+ for (const parent of [paths.runsDir, paths.logsDir]) {
198
+ let names;
199
+ try {
200
+ names = await readdir(parent);
201
+ }
202
+ catch {
203
+ continue;
204
+ }
205
+ for (const name of names) {
206
+ if (!isValidRunId(name) || this.options.repositories.runs.get(name))
207
+ continue;
208
+ let dir;
209
+ try {
210
+ dir = resolveStrictlyWithin(parent, name);
211
+ const st = await stat(dir);
212
+ if (!st.isDirectory() || now - st.mtimeMs < minAge)
213
+ continue;
214
+ }
215
+ catch {
216
+ continue;
217
+ }
218
+ // Re-check right before deleting (a run could have been created meanwhile – IDs are unique, so only a stale dir matches).
219
+ if (this.options.repositories.runs.get(name))
220
+ continue;
221
+ if (await this.removeDir(dir, report))
222
+ report.orphanDirsRemoved++;
223
+ }
224
+ }
225
+ }
226
+ }
227
+ /** `workspace.baseDir` from a run's config snapshot, if present. */
228
+ function snapshotBaseDir(snapshot) {
229
+ const ws = snapshot?.workspace;
230
+ return typeof ws?.baseDir === 'string' ? ws.baseDir : null;
231
+ }
232
+ //# sourceMappingURL=retention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/runs/retention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,WAAW,EACX,YAAY,GAKb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,mBAAmB,EAAgD,MAAM,yBAAyB,CAAC;AAC5G,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,qEAAqE;AACrE,MAAM,wBAAwB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;AACvD,uFAAuF;AACvF,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;AAiCnD,uBAAuB;AACvB,MAAM,OAAO,gBAAgB;IAUE,OAAO;IATnB,KAAK,CAAQ;IACb,MAAM,CAAS;IACf,MAAM,CAAS;IACf,SAAS,CAAS;IAC3B,KAAK,CAA0B;IAC/B,OAAO,CAAuC;IAC9C,OAAO,GAAG,KAAK,CAAC;IAChB,IAAI,GAA2B,IAAI,CAAC;IAE5C,YAA6B,OAAgC;uBAAhC,OAAO;QAClC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,WAAW,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC;IAC5C,CAAC;IAED,uCAAuC;IACvC,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,4GAA4G;IAC5G,KAAK,CAAC,cAAc,GAAG,MAAM;QAC3B,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3B,CAAC;IAED,kDAAkD;IAClD,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,MAAM,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IAEO,GAAG,CAAC,OAAe;QACzB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;YACvC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACvB,IAAI,CAAC,OAAO,EAAE;iBACX,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;iBACjG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACxE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,yEAAyE;IACzE,OAAO;QACL,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACxC,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAoB;YAC9B,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE;YAC5B,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE;YAC7B,WAAW,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;YACrD,aAAa,EAAE,EAAE;YACjB,aAAa,EAAE,CAAC;YAChB,oBAAoB,EAAE,CAAC;YACvB,kBAAkB,EAAE,CAAC;YACrB,iBAAiB,EAAE,CAAC;YACpB,MAAM,EAAE,EAAE;SACX,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,EAAiB,EAAe,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACvG,MAAM,UAAU,GAA+C;YAC7D,CAAC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;YAChD,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YACzC,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;SAC5C,CAAC;QACF,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC5C,IAAI,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO;gBAAE,SAAS;YAC9C,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;QAChD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACpD,IAAI,YAAY;YAAE,MAAM,CAAC,aAAa,GAAG,SAAS,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;QACrF,MAAM,mBAAmB,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,mBAAmB;YAAE,MAAM,CAAC,oBAAoB,GAAG,SAAS,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;QAC1G,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,IAAI,wBAAwB,CAAC,CAAC;QACnF,IAAI,aAAa;YAAE,MAAM,CAAC,kBAAkB,GAAG,SAAS,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QAC1F,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;QACnD,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QACnB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;QACrG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE;YAC1C,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;YACjD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;YAC7C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;YAC3C,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM;YAC5B,KAAK;SACN,CAAC,CAAC;QACH,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,QAA8B,EAAE,MAAY,EAAE,MAAuB;QAC5F,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;QACtD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,SAAS,CAAC;YACR,IAAI,IAAI,CAAC,OAAO;gBAAE,OAAO;YACzB,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1G,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAC/B,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;gBAC9B,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBACtC,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;gBACrD,8DAA8D;gBAC9D,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;oBAAE,SAAS;gBACpD,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/B,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBAC3C,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YACvF,CAAC;YACD,qDAAqD;YACrD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,KAAa,EAAE,aAA4B,EAAE,OAAsB,EAAE,MAAuB;QACvH,IAAI,KAAK,CAAC;QACV,IAAI,CAAC;YACH,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC1D,OAAO;QACT,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACzF,IAAI,aAAa,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;YAC9D,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBACnD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,0BAA0B,EAAE,CAAC,CAAC;YAC5G,CAAC;iBAAM,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC5D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7F,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,MAAuB,EAAE,KAAc;QAC1E,IAAK,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1F,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3F,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,gHAAgH;IACxG,KAAK,CAAC,aAAa,CAAC,MAAuB;QACjD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,yBAAyB,CAAC;QACxE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QACvC,KAAK,MAAM,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACpD,IAAI,KAAe,CAAC;YACpB,IAAI,CAAC;gBACH,KAAK,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAC9E,IAAI,GAAW,CAAC;gBAChB,IAAI,CAAC;oBACH,GAAG,GAAG,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;oBAC1C,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;oBAC3B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,GAAG,GAAG,EAAE,CAAC,OAAO,GAAG,MAAM;wBAAE,SAAS;gBAC/D,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS;gBACX,CAAC;gBACD,0HAA0H;gBAC1H,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAS;gBACvD,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC;oBAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACpE,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,oEAAoE;AACpE,SAAS,eAAe,CAAC,QAAiB;IACxC,MAAM,EAAE,GAAI,QAAqE,EAAE,SAAS,CAAC;IAC7F,OAAO,OAAO,EAAE,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type BusinessResult, type TerminalStatus } from '../core/index.js';
2
+ import type { WorkspaceCleanup } from '../config/index.js';
3
+ /** True if the policy asks to delete the workspace of a run with this outcome. */
4
+ export declare function shouldCleanupWorkspace(policy: WorkspaceCleanup, status: TerminalStatus, businessResult: BusinessResult): boolean;
5
+ /** Outcome of {@link removeWorkspace}. */
6
+ export type WorkspaceRemoval = {
7
+ removed: true;
8
+ path: string;
9
+ } | {
10
+ removed: false;
11
+ path: string;
12
+ reason: 'missing' | 'outside_base_dir' | 'symlink' | 'error';
13
+ message?: string;
14
+ };
15
+ /**
16
+ * Delete the run workspace `workspacePath` if – lexically AND after resolving
17
+ * symlinks of its parents – it lies strictly below `baseDir`. A workspace
18
+ * that is itself a symlink is never followed or deleted. Never throws.
19
+ */
20
+ export declare function removeWorkspace(baseDir: string, workspacePath: string): Promise<WorkspaceRemoval>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Workspace cleanup after a run (Spec §5.1 `workspace.cleanup`, §9, §20):
3
+ * only directories strictly below `workspace.base_dir` are ever deleted.
4
+ */
5
+ import { lstat, realpath, rm } from 'node:fs/promises';
6
+ import path from 'node:path';
7
+ import { isWithin, resolveStrictlyWithin } from '../core/index.js';
8
+ /** True if the policy asks to delete the workspace of a run with this outcome. */
9
+ export function shouldCleanupWorkspace(policy, status, businessResult) {
10
+ switch (policy) {
11
+ case 'always':
12
+ return true;
13
+ case 'never':
14
+ return false;
15
+ case 'on_success':
16
+ return status === 'completed' && businessResult === 'success';
17
+ }
18
+ }
19
+ /**
20
+ * Delete the run workspace `workspacePath` if – lexically AND after resolving
21
+ * symlinks of its parents – it lies strictly below `baseDir`. A workspace
22
+ * that is itself a symlink is never followed or deleted. Never throws.
23
+ */
24
+ export async function removeWorkspace(baseDir, workspacePath) {
25
+ let target;
26
+ try {
27
+ target = resolveStrictlyWithin(baseDir, workspacePath);
28
+ }
29
+ catch (err) {
30
+ return { removed: false, path: workspacePath, reason: 'outside_base_dir', message: err.message };
31
+ }
32
+ try {
33
+ const st = await lstat(target);
34
+ if (st.isSymbolicLink())
35
+ return { removed: false, path: target, reason: 'symlink' };
36
+ const [realBase, realTarget] = await Promise.all([realpath(baseDir), realpath(target)]);
37
+ if (!isWithin(realBase, realTarget) || path.resolve(realBase) === path.resolve(realTarget)) {
38
+ return { removed: false, path: target, reason: 'outside_base_dir', message: `resolves to ${realTarget}` };
39
+ }
40
+ // `rm` does not follow symlinks inside the tree; it removes the links themselves.
41
+ await rm(realTarget, { recursive: true, force: true });
42
+ return { removed: true, path: target };
43
+ }
44
+ catch (err) {
45
+ const code = err.code;
46
+ if (code === 'ENOENT')
47
+ return { removed: false, path: target, reason: 'missing' };
48
+ return { removed: false, path: target, reason: 'error', message: err.message };
49
+ }
50
+ }
51
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/runs/workspace.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAA4C,MAAM,kBAAkB,CAAC;AAG7G,kFAAkF;AAClF,MAAM,UAAU,sBAAsB,CAAC,MAAwB,EAAE,MAAsB,EAAE,cAA8B;IACrH,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC;QACd,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,YAAY;YACf,OAAO,MAAM,KAAK,WAAW,IAAI,cAAc,KAAK,SAAS,CAAC;IAClE,CAAC;AACH,CAAC;AAOD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe,EAAE,aAAqB;IAC1E,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,qBAAqB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;IAC9G,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,EAAE,CAAC,cAAc,EAAE;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QACpF,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxF,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,eAAe,UAAU,EAAE,EAAE,CAAC;QAC5G,CAAC;QACD,kFAAkF;QAClF,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAClF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;IAC5F,CAAC;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Shell filter and conditions for the scheduler (Spec §6, §7 step 5):
3
+ * argument templates, check context, workflow-condition cache, diagnoses.
4
+ */
5
+ import { type SourceItem, type TriValue } from '../core/index.js';
6
+ import type { ShellFilterConfig, WorkflowConfig } from '../config/index.js';
7
+ import { type ShellCheckRunner } from '../filters/index.js';
8
+ import type { CheckDiagnosis, CheckKind } from './types.js';
9
+ /** Combined result of a list of checks (all must be `true`). */
10
+ export interface ChecksOutcome {
11
+ value: TriValue;
12
+ checks: CheckDiagnosis[];
13
+ /** First check that was not `true`. */
14
+ failed: CheckDiagnosis | null;
15
+ }
16
+ /** Diagnosis entry for a check that was not executed. */
17
+ export declare function notEvaluatedCheck(kind: CheckKind, index: number, command: string): CheckDiagnosis;
18
+ /** Diagnoses for all workflow conditions, none executed. */
19
+ export declare function notEvaluatedConditions(workflow: WorkflowConfig, kind: 'workflow_condition' | 'item_condition'): CheckDiagnosis[];
20
+ /** Item context for item-bound checks. */
21
+ export interface CheckItem {
22
+ item: SourceItem;
23
+ sourceType: string;
24
+ repository?: string;
25
+ }
26
+ /** Runs the scheduler's shell checks. */
27
+ export declare class SchedulerChecks {
28
+ private readonly runner;
29
+ private readonly now;
30
+ constructor(runner: ShellCheckRunner, now: () => Date);
31
+ /** Cache key of a workflow condition (WI-04 proposal). */
32
+ static conditionCacheKey(workflowId: string, index: number): string;
33
+ /** Workflow conditions in order; stops at the first non-`true` (rest `not_evaluated`). Cached per `cache` TTL. */
34
+ workflowConditions(workflow: WorkflowConfig, signal?: AbortSignal): Promise<ChecksOutcome>;
35
+ /** Item conditions in order (never cached). */
36
+ itemConditions(workflow: WorkflowConfig, target: CheckItem, signal?: AbortSignal): Promise<ChecksOutcome>;
37
+ /** Shell filter of a workflow for one item (never cached). */
38
+ shellFilter(workflow: WorkflowConfig, filter: ShellFilterConfig, target: CheckItem, signal?: AbortSignal): Promise<CheckDiagnosis>;
39
+ private runList;
40
+ private runOne;
41
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Shell filter and conditions for the scheduler (Spec §6, §7 step 5):
3
+ * argument templates, check context, workflow-condition cache, diagnoses.
4
+ */
5
+ import { buildTemplateValues, errorMessage, renderTemplate } from '../core/index.js';
6
+ import { buildCheckContext } from '../filters/index.js';
7
+ /** Diagnosis entry for a check that was not executed. */
8
+ export function notEvaluatedCheck(kind, index, command) {
9
+ return { kind, index, command, value: 'not_evaluated', cached: false, exitCode: null, error: null, durationMs: null };
10
+ }
11
+ /** Diagnoses for all workflow conditions, none executed. */
12
+ export function notEvaluatedConditions(workflow, kind) {
13
+ const list = kind === 'workflow_condition' ? workflow.conditions.workflow : workflow.conditions.item;
14
+ return list.map((c, i) => notEvaluatedCheck(kind, i, c.command));
15
+ }
16
+ function fromResult(kind, index, command, r) {
17
+ return {
18
+ kind,
19
+ index,
20
+ command,
21
+ value: r.value,
22
+ cached: r.cached,
23
+ exitCode: r.exitCode,
24
+ error: r.value === 'unknown' ? (r.error ?? 'check error') : null,
25
+ durationMs: r.durationMs,
26
+ };
27
+ }
28
+ function templateValues(workflow, now, item, sourceType, repository) {
29
+ const title = item?.fields.title;
30
+ return buildTemplateValues({
31
+ workflow: { id: workflow.id, name: workflow.name },
32
+ ...(item
33
+ ? {
34
+ source: { id: item.sourceId, type: sourceType ?? '', ...(repository !== undefined ? { repository } : {}) },
35
+ item: {
36
+ id: item.itemId,
37
+ key: item.key,
38
+ ...(/^\d+$/.test(item.itemId) ? { number: item.itemId } : {}),
39
+ url: item.url,
40
+ ...(typeof title === 'string' ? { title } : {}),
41
+ },
42
+ }
43
+ : {}),
44
+ now,
45
+ });
46
+ }
47
+ /** Runs the scheduler's shell checks. */
48
+ export class SchedulerChecks {
49
+ runner;
50
+ now;
51
+ constructor(runner, now) {
52
+ this.runner = runner;
53
+ this.now = now;
54
+ }
55
+ /** Cache key of a workflow condition (WI-04 proposal). */
56
+ static conditionCacheKey(workflowId, index) {
57
+ return `${workflowId}:condition:${index}`;
58
+ }
59
+ /** Workflow conditions in order; stops at the first non-`true` (rest `not_evaluated`). Cached per `cache` TTL. */
60
+ async workflowConditions(workflow, signal) {
61
+ return this.runList(workflow, 'workflow_condition', workflow.conditions.workflow, undefined, signal);
62
+ }
63
+ /** Item conditions in order (never cached). */
64
+ async itemConditions(workflow, target, signal) {
65
+ return this.runList(workflow, 'item_condition', workflow.conditions.item, target, signal);
66
+ }
67
+ /** Shell filter of a workflow for one item (never cached). */
68
+ async shellFilter(workflow, filter, target, signal) {
69
+ const spec = { command: filter.command, timeoutMs: filter.timeoutMs };
70
+ const ctx = buildCheckContext('shell_filter', workflow.id, target.item, target.sourceType);
71
+ const result = await this.runner.run(spec, ctx, signal ? { signal } : {});
72
+ return fromResult('shell_filter', 0, filter.command, result);
73
+ }
74
+ async runList(workflow, kind, list, target, signal) {
75
+ const checks = [];
76
+ let failed = null;
77
+ for (const [index, condition] of list.entries()) {
78
+ if (failed) {
79
+ checks.push(notEvaluatedCheck(kind, index, condition.command));
80
+ continue;
81
+ }
82
+ const diag = await this.runOne(workflow, kind, index, condition, target, signal);
83
+ checks.push(diag);
84
+ if (diag.value !== true)
85
+ failed = diag;
86
+ }
87
+ const value = failed === null ? true : failed.value === false ? false : 'unknown';
88
+ return { value, checks, failed };
89
+ }
90
+ async runOne(workflow, kind, index, condition, target, signal) {
91
+ let args;
92
+ try {
93
+ const values = templateValues(workflow, this.now(), target?.item, target?.sourceType, target?.repository);
94
+ const scope = kind === 'item_condition' ? { source: true } : {};
95
+ args = condition.args.map((a) => renderTemplate(a, values, { mode: 'text', scope }));
96
+ }
97
+ catch (err) {
98
+ return { ...notEvaluatedCheck(kind, index, condition.command), value: 'unknown', error: `argument template: ${errorMessage(err)}` };
99
+ }
100
+ const spec = { command: condition.command, args, timeoutMs: condition.timeoutMs };
101
+ const ctx = kind === 'item_condition' && target
102
+ ? buildCheckContext('item_condition', workflow.id, target.item, target.sourceType)
103
+ : buildCheckContext('workflow_condition', workflow.id);
104
+ const opts = signal ? { signal } : {};
105
+ const result = kind === 'workflow_condition' && condition.cacheMs !== null && condition.cacheMs > 0
106
+ ? await this.runner.runCached(SchedulerChecks.conditionCacheKey(workflow.id, index), condition.cacheMs, spec, ctx, opts)
107
+ : await this.runner.run(spec, ctx, opts);
108
+ return fromResult(kind, index, condition.command, result);
109
+ }
110
+ }
111
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/scheduler/checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /** A parsed cron schedule (paused croner instance – it never fires on its own). */
2
+ export interface CronSchedule {
3
+ readonly expression: string;
4
+ readonly timezone: string;
5
+ /** First slot strictly after `after`, or `null` if the expression never fires again. */
6
+ next(after: Date): Date | null;
7
+ }
8
+ /**
9
+ * Parse a cron expression for a time zone (croner syntax, 5 or 6 fields).
10
+ * @throws Error for invalid expressions/time zones (config validation rejects them earlier).
11
+ */
12
+ export declare function parseCron(expression: string, timezone: string): CronSchedule;
13
+ /** The next `count` slots strictly after `after`. */
14
+ export declare function nextSlots(schedule: CronSchedule, after: Date, count: number): Date[];
15
+ /** Result of {@link dueSlots}. */
16
+ export interface DueSlots {
17
+ /** The most recent due slots (ascending), at most `keep`. */
18
+ slots: Date[];
19
+ /** Total number of due slots found (≥ `slots.length`). */
20
+ total: number;
21
+ /** True if scanning stopped at `maxScan` before reaching `now` (older slots were not counted). */
22
+ scanLimited: boolean;
23
+ }
24
+ /**
25
+ * All slots in `(after, now]`, keeping only the most recent `keep` of them.
26
+ * Scanning is bounded by `maxScan`; if hit, scanning restarts closer to
27
+ * `now` so that the most recent slots are still found.
28
+ */
29
+ export declare function dueSlots(schedule: CronSchedule, after: Date, now: Date, options: {
30
+ keep: number;
31
+ maxScan: number;
32
+ }): DueSlots;