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,255 @@
1
+ /**
2
+ * Candidate discovery and eligibility for source workflows (Spec §7 steps
3
+ * 1–5), shared by the planning step and the dry-run.
4
+ *
5
+ * - Step 1: poll the source with the pushed-down part of the filter and
6
+ * evaluate the filter with CHEAP fields only. Strong Kleene logic is
7
+ * monotonic, so a cheap `false` is final (item dropped) and a cheap `true`
8
+ * is final; only a cheap `unknown` needs the expensive fields (step 5).
9
+ * - Step 2: de-duplicate by `(source, item, workflow)`.
10
+ * - Step 3: item priority (same field source → one batch of expensive fields).
11
+ * - Step 4: active lock / retry block of the workflow+item.
12
+ * - Step 5 (lazy): workflow conditions (once per step), expensive declarative
13
+ * filter, shell filter, item conditions. The dry-run never executes shell
14
+ * checks: they are reported as `not_evaluated`.
15
+ */
16
+ import { errorMessage, } from '../core/index.js';
17
+ import { collectFields, createItemFieldSource, evaluateFilter, resolveItemPriority, splitForPushdown, } from '../filters/index.js';
18
+ import { notEvaluatedCheck, notEvaluatedConditions } from './checks.js';
19
+ /** Field source that treats expensive fields as unknown without resolving them. */
20
+ export function cheapOnly(fields) {
21
+ return {
22
+ isExpensive: (f) => fields.isExpensive?.(f) ?? false,
23
+ fieldType: (f) => fields.fieldType?.(f),
24
+ isCaseInsensitive: (f) => fields.isCaseInsensitive?.(f) ?? false,
25
+ resolve(field) {
26
+ if (fields.isExpensive?.(field))
27
+ return { known: false, reason: 'expensive field, evaluated later' };
28
+ return fields.resolve(field);
29
+ },
30
+ };
31
+ }
32
+ function emptyDiagnosis(workflow, at, sourceId) {
33
+ return {
34
+ workflowId: workflow.id,
35
+ at,
36
+ status: 'ok',
37
+ error: null,
38
+ sourceId,
39
+ polled: 0,
40
+ pushdown: null,
41
+ conditions: notEvaluatedConditions(workflow, 'workflow_condition'),
42
+ candidates: [],
43
+ omittedCandidates: 0,
44
+ startedRunIds: [],
45
+ };
46
+ }
47
+ /**
48
+ * Steps 1–3 for one source workflow: poll, de-duplicate, cheap filter, item
49
+ * priority. Never throws; source problems end up in the diagnosis
50
+ * (`status: 'source_error'`) without affecting other workflows.
51
+ */
52
+ export async function prepareSourceWorkflow(workflow, workflowPriority, options) {
53
+ if (workflow.trigger.type !== 'source')
54
+ throw new TypeError(`Workflow ${workflow.id} is not source-triggered`);
55
+ const sourceId = workflow.trigger.source;
56
+ const diagnosis = emptyDiagnosis(workflow, options.at, sourceId);
57
+ const adapter = options.sources(sourceId);
58
+ if (!adapter) {
59
+ diagnosis.status = 'source_error';
60
+ diagnosis.error = `source '${sourceId}' is not available`;
61
+ return { diagnosis, candidates: [], rejected: [] };
62
+ }
63
+ const filter = workflow.filter;
64
+ const definitions = adapter.fieldDefinitions();
65
+ let pushdown = null;
66
+ if (filter)
67
+ pushdown = splitForPushdown(filter, definitions).pushdown;
68
+ diagnosis.pushdown = pushdown;
69
+ let items;
70
+ try {
71
+ const key = `${sourceId}\u0000${JSON.stringify(pushdown)}`;
72
+ let listing = options.listCache?.get(key);
73
+ if (!listing) {
74
+ listing = adapter.listCandidates({
75
+ ...(pushdown ? { filter: pushdown } : {}),
76
+ ...(options.signal ? { signal: options.signal } : {}),
77
+ });
78
+ options.listCache?.set(key, listing);
79
+ }
80
+ items = await listing;
81
+ }
82
+ catch (err) {
83
+ diagnosis.status = 'source_error';
84
+ diagnosis.error = `polling source '${sourceId}' failed: ${errorMessage(err)}`;
85
+ return { diagnosis, candidates: [], rejected: [] };
86
+ }
87
+ // Step 2: normalize (source-id, item-id, workflow-id).
88
+ const unique = new Map();
89
+ for (const item of items)
90
+ if (!unique.has(item.key))
91
+ unique.set(item.key, item);
92
+ diagnosis.polled = unique.size;
93
+ const wanted = new Set(filter ? collectFields(filter) : []);
94
+ for (const rule of workflow.itemPriority?.rules ?? [])
95
+ for (const f of collectFields(rule.when))
96
+ wanted.add(f);
97
+ const wantedFields = [...wanted];
98
+ const candidates = [];
99
+ const rejected = [];
100
+ for (const item of unique.values()) {
101
+ const fields = createItemFieldSource(item, {
102
+ definitions,
103
+ resolveFields: (i, f) => adapter.resolveFields(i, f, options.signal ? { signal: options.signal } : {}),
104
+ wantedFields,
105
+ });
106
+ const cheap = filter ? await evaluateFilter(filter, cheapOnly(fields)) : null;
107
+ const title = item.fields.title;
108
+ const diag = {
109
+ workflowId: workflow.id,
110
+ sourceId,
111
+ itemId: item.itemId,
112
+ itemKey: item.key,
113
+ url: item.url,
114
+ title: typeof title === 'string' ? title : null,
115
+ createdAt: item.createdAt ?? null,
116
+ workflowPriority,
117
+ itemPriority: 0,
118
+ matchedPriorityRule: null,
119
+ decision: 'filtered',
120
+ reason: null,
121
+ filter: cheap?.trace ?? null,
122
+ shellFilter: null,
123
+ itemConditions: [],
124
+ runId: null,
125
+ blockId: null,
126
+ };
127
+ if (cheap?.value === false) {
128
+ diag.reason = 'declarative filter does not match';
129
+ rejected.push(diag);
130
+ continue;
131
+ }
132
+ // Step 3: item priority (rules may need expensive fields: same memoized batch).
133
+ const priority = await resolveItemPriority(workflow.itemPriority, fields);
134
+ diag.itemPriority = priority.priority;
135
+ diag.matchedPriorityRule = priority.matchedRule;
136
+ candidates.push({
137
+ workflowId: workflow.id,
138
+ workflowPriority,
139
+ itemPriority: priority.priority,
140
+ createdAt: item.createdAt,
141
+ itemKey: item.key,
142
+ workflow,
143
+ adapter,
144
+ item,
145
+ fields,
146
+ cheap,
147
+ diagnosis: diag,
148
+ });
149
+ }
150
+ return { diagnosis, candidates, rejected };
151
+ }
152
+ /**
153
+ * Steps 4–5 for one candidate (lock, block, workflow conditions, expensive
154
+ * filter, shell filter, item conditions). Updates `candidate.diagnosis` with
155
+ * decision/reason for rejected candidates. Capacity is checked by the caller.
156
+ */
157
+ export async function checkEligibility(candidate, ctx) {
158
+ const { workflow, diagnosis: diag } = candidate;
159
+ const reject = (decision, reason) => {
160
+ diag.decision = decision;
161
+ diag.reason = reason;
162
+ return { eligible: false };
163
+ };
164
+ // Step 4: active lock / retry block.
165
+ const holder = ctx.state.activeRunForItem(workflow.id, candidate.itemKey);
166
+ if (holder) {
167
+ diag.runId = holder.id;
168
+ return reject('locked', `run ${holder.id} of this workflow is active for the item`);
169
+ }
170
+ const block = ctx.state.activeBlock(workflow.id, candidate.itemKey);
171
+ if (block) {
172
+ diag.blockId = block.id;
173
+ return reject('blocked', `retry block ${block.id}: ${block.reason}`);
174
+ }
175
+ let pending = false;
176
+ // Step 5a: workflow conditions (once per planning step).
177
+ if (workflow.conditions.workflow.length > 0) {
178
+ if (ctx.checks) {
179
+ const outcome = await ctx.workflowConditions(workflow);
180
+ if (outcome.value !== true) {
181
+ const f = outcome.failed;
182
+ return outcome.value === false
183
+ ? reject('condition_failed', `workflow condition ${f?.index ?? '?'} (${f?.command ?? ''}) is false`)
184
+ : reject('condition_unknown', `workflow condition ${f?.index ?? '?'} (${f?.command ?? ''}) is unknown: ${f?.error ?? 'check error'}`);
185
+ }
186
+ }
187
+ else {
188
+ pending = true;
189
+ }
190
+ }
191
+ // Step 5b: expensive declarative filter (only if the cheap pass could not decide).
192
+ if (workflow.filter && candidate.cheap && candidate.cheap.value !== true) {
193
+ const full = await evaluateFilter(workflow.filter, candidate.fields);
194
+ diag.filter = full.trace;
195
+ if (full.value === false)
196
+ return reject('filtered', 'declarative filter does not match');
197
+ if (full.value === 'unknown')
198
+ return reject('filter_unknown', 'declarative filter is unknown (see trace)');
199
+ }
200
+ const target = {
201
+ item: candidate.item,
202
+ sourceType: candidate.adapter.type,
203
+ ...(candidate.adapter.repository !== undefined ? { repository: candidate.adapter.repository } : {}),
204
+ };
205
+ // Step 5c: shell filter.
206
+ if (workflow.shellFilter) {
207
+ if (ctx.checks) {
208
+ const sf = await ctx.checks.shellFilter(workflow, workflow.shellFilter, target, ctx.signal);
209
+ diag.shellFilter = sf;
210
+ if (sf.value === false)
211
+ return reject('shell_filter_rejected', 'shell filter exited with 1');
212
+ if (sf.value !== true)
213
+ return reject('shell_filter_unknown', `shell filter check error: ${sf.error ?? 'unknown'}`);
214
+ }
215
+ else {
216
+ diag.shellFilter = notEvaluatedCheck('shell_filter', 0, workflow.shellFilter.command);
217
+ pending = true;
218
+ }
219
+ }
220
+ // Step 5d: item conditions.
221
+ if (workflow.conditions.item.length > 0) {
222
+ if (ctx.checks) {
223
+ const outcome = await ctx.checks.itemConditions(workflow, target, ctx.signal);
224
+ diag.itemConditions = outcome.checks;
225
+ if (outcome.value !== true) {
226
+ const f = outcome.failed;
227
+ return outcome.value === false
228
+ ? reject('condition_failed', `item condition ${f.index} (${f.command}) is false`)
229
+ : reject('condition_unknown', `item condition ${f.index} (${f.command}) is unknown: ${f.error ?? 'check error'}`);
230
+ }
231
+ }
232
+ else {
233
+ diag.itemConditions = notEvaluatedConditions(workflow, 'item_condition');
234
+ pending = true;
235
+ }
236
+ }
237
+ return { eligible: true, pendingChecks: pending };
238
+ }
239
+ /**
240
+ * Re-check for the item re-read by the run controller right before `on_start`
241
+ * (Spec §7 races): the cheap part of the filter on the fresh item. `false`
242
+ * cancels the run without hooks; `unknown` (expensive fields) does not.
243
+ */
244
+ export function verifyItemFor(workflow, adapter) {
245
+ const filter = workflow.filter;
246
+ if (!filter)
247
+ return undefined;
248
+ const definitions = adapter.fieldDefinitions();
249
+ return async (item) => {
250
+ const fields = createItemFieldSource(item, { definitions });
251
+ const result = await evaluateFilter(filter, cheapOnly(fields));
252
+ return result.value !== false;
253
+ };
254
+ }
255
+ //# sourceMappingURL=planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../src/scheduler/planner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Scheduler (Spec §5.5, §7, §14, §19): polling loop for source workflows,
3
+ * cron trigger, manual start and dry-run.
4
+ *
5
+ * Invariants:
6
+ * - Planning steps never overlap (serialized chain); the next poll is armed
7
+ * only after the previous step finished.
8
+ * - No persistent candidate queue: every step reads the sources again.
9
+ * - The final capacity check and the reservation run synchronously (no
10
+ * `await` in between), so concurrent cron firings / manual starts in the
11
+ * same process cannot overcommit the limits. Active runs of all triggers
12
+ * (incl. manual and `recovery_pending`) count towards the limits.
13
+ * - Automatic starts only when the run controller allows them (ready after
14
+ * recovery, not paused, not draining/stopping).
15
+ * - Cron: at most one run per slot (`cron_firings` primary key + slot lock);
16
+ * every handled slot gets exactly one `cron_firings` record; missed slots
17
+ * are recorded as `skipped_missed` and never caught up.
18
+ */
19
+ import { type Clock, type Logger, type Timers } from '../core/index.js';
20
+ import { type ConfigManager } from '../config/index.js';
21
+ import { ShellCheckRunner } from '../filters/index.js';
22
+ import { type Repositories, type RunRecord } from '../persistence/index.js';
23
+ import type { ControllerStatus, RunHandle, StartRunOptions } from '../runs/index.js';
24
+ import { type SourceLookup } from './planner.js';
25
+ import { type CronWorkflowStatus, type DryRunResult, type PlanningStepReport, type WorkflowPlanningDiagnosis } from './types.js';
26
+ /** The part of the run controller (WI-08) the scheduler needs. */
27
+ export interface SchedulerRunController {
28
+ readonly status: ControllerStatus;
29
+ canStartAutomatically(): boolean;
30
+ canStartManually(): boolean;
31
+ startRun(run: RunRecord, options?: StartRunOptions): RunHandle;
32
+ }
33
+ /** Dependencies and tuning of the {@link Scheduler}. */
34
+ export interface SchedulerOptions {
35
+ /** Live configuration incl. runtime overrides (effective values). */
36
+ config: ConfigManager;
37
+ repositories: Repositories;
38
+ controller: SchedulerRunController;
39
+ /** Source adapters by configured source ID. */
40
+ sources: SourceLookup;
41
+ /** Runner for shell filters/conditions (holds the workflow-condition cache). Default: a new one. */
42
+ shellChecks?: ShellCheckRunner;
43
+ clock?: Clock;
44
+ /** Timers for the polling loop and cron (FakeClock in tests). */
45
+ timers?: Timers;
46
+ logger?: Logger;
47
+ /**
48
+ * A cron slot is still started if it is handled at most this late (timer
49
+ * latency, short restart); later slots are `skipped_missed`. Default 60 s.
50
+ */
51
+ cronMisfireToleranceMs?: number;
52
+ /** Max. `skipped_missed` records written per catch-up (older ones are only counted). Default 100. */
53
+ maxRecordedMissedSlots?: number;
54
+ }
55
+ /** Request of {@link Scheduler.startManual}. */
56
+ export interface ManualStartRequest {
57
+ /** Item reference (URL or ID) – required for source workflows, rejected otherwise. */
58
+ itemRef?: string;
59
+ /** Parameter values (validated against the workflow's `parameters`). */
60
+ parameters?: Readonly<Record<string, unknown>>;
61
+ }
62
+ /** Result of {@link Scheduler.startManual}. */
63
+ export interface ManualStartResult {
64
+ /** The reserved run (status `reserved` at return time). */
65
+ run: RunRecord;
66
+ /** Resolves when the controller finished supervising the run. */
67
+ done: Promise<RunRecord>;
68
+ }
69
+ /** See module docs. One instance per process, created after the run controller's recovery. */
70
+ export declare class Scheduler {
71
+ private readonly config;
72
+ private readonly repos;
73
+ private readonly controller;
74
+ private readonly sources;
75
+ private readonly state;
76
+ private readonly shellChecks;
77
+ private readonly checks;
78
+ private readonly clock;
79
+ private readonly timers;
80
+ private readonly logger;
81
+ private readonly misfireToleranceMs;
82
+ private readonly maxRecordedMissed;
83
+ private started;
84
+ private stopped;
85
+ private pollTimer;
86
+ private stepChain;
87
+ private readonly inflight;
88
+ private readonly diagnoses;
89
+ private lastReport;
90
+ private readonly cron;
91
+ private unsubscribeConfig;
92
+ constructor(options: SchedulerOptions);
93
+ /**
94
+ * Start the polling loop (first step immediately) and arm the cron timers
95
+ * (missed slots since the last persisted firing are recorded as
96
+ * `skipped_missed`). Call after `RunController.recover()`.
97
+ */
98
+ start(): void;
99
+ /** Stop polling and cron timers; waits for running planning steps / cron handlers. Idempotent. */
100
+ stop(): Promise<void>;
101
+ /** Resolves once no planning step and no cron handler is in flight (tests, shutdown). */
102
+ whenIdle(): Promise<void>;
103
+ private track;
104
+ /** One loop iteration: planning step, then arm the next poll (never overlapping). */
105
+ private loopStep;
106
+ private armPoll;
107
+ private gateReason;
108
+ private gateInfo;
109
+ /** Limit check with live effective values (overrides apply immediately). */
110
+ private capacityProblem;
111
+ /**
112
+ * Run one planning step for all source workflows. Steps are serialized
113
+ * (a call during a running step runs after it). Never rejects for source
114
+ * or check problems – they are part of the diagnosis.
115
+ */
116
+ planOnce(): Promise<PlanningStepReport>;
117
+ private planningStep;
118
+ /** Steps 4–7 for one candidate; returns the started run ID. */
119
+ private considerCandidate;
120
+ /** Arm/re-arm cron timers for the schedule workflows of the active configuration. */
121
+ private syncCron;
122
+ private disarm;
123
+ private enqueueCron;
124
+ private armCronTimer;
125
+ /** Handle all slots in `(lastHandled, now]`: the latest within tolerance fires, the rest are missed. */
126
+ private processDue;
127
+ private decideDue;
128
+ /**
129
+ * Persist a slot decision. Quiet skips (disabled / gate closed) are written
130
+ * only for the first slot of a series; later slots of the same series only
131
+ * advance `lastHandled` (no row flood for frequent schedules).
132
+ */
133
+ private recordFiring;
134
+ /** Decide one due slot: start a run or record why not. */
135
+ private handleSlot;
136
+ /** Synchronous checks before a slot may start (enabled, gate, limits, block). */
137
+ private slotPrecondition;
138
+ /**
139
+ * Start a workflow manually. Bypasses filters, conditions, retry blocks,
140
+ * priorities and limits (and `enabled`/pause), but never the active lock of
141
+ * the workflow+item (`LockConflictError`, HTTP 409). The run counts towards
142
+ * the limits of automatic starts.
143
+ *
144
+ * @throws UnknownWorkflowError, SchedulerError, LockConflictError
145
+ */
146
+ startManual(workflowId: string, request?: ManualStartRequest): Promise<ManualStartResult>;
147
+ private assertAcceptsManual;
148
+ /** Dry-run a workflow (no reservation, no actions, no shell checks). @throws UnknownWorkflowError */
149
+ dryRun(workflowId: string, options?: {
150
+ signal?: AbortSignal;
151
+ }): Promise<DryRunResult>;
152
+ /** Last planning diagnosis of a source workflow (`undefined` before its first step). */
153
+ lastDiagnosis(workflowId: string): WorkflowPlanningDiagnosis | undefined;
154
+ /** Last planning diagnoses of all source workflows, sorted by workflow ID. */
155
+ listDiagnoses(): WorkflowPlanningDiagnosis[];
156
+ /** Summary of the last planning step (`null` before the first one). */
157
+ lastPlanningStep(): PlanningStepReport | null;
158
+ /** Cron status of all schedule workflows, sorted by workflow ID. */
159
+ cronStatus(): CronWorkflowStatus[];
160
+ }