@overlordai/server 1.0.52 → 1.0.54

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 (533) hide show
  1. package/database/migrations/001-init-schema.sql +73 -9
  2. package/dist/adapters/adapter.interface.d.ts +1 -4
  3. package/dist/adapters/adapter.interface.d.ts.map +1 -1
  4. package/dist/adapters/adapter.interface.js.map +1 -1
  5. package/dist/adapters/adapter.module.d.ts.map +1 -1
  6. package/dist/adapters/adapter.module.js +8 -7
  7. package/dist/adapters/adapter.module.js.map +1 -1
  8. package/dist/adapters/lark/lark-card.builder.d.ts +1 -25
  9. package/dist/adapters/lark/lark-card.builder.d.ts.map +1 -1
  10. package/dist/adapters/lark/lark-card.builder.js +6 -110
  11. package/dist/adapters/lark/lark-card.builder.js.map +1 -1
  12. package/dist/adapters/lark/lark-message.parser.d.ts +4 -4
  13. package/dist/adapters/lark/lark-message.parser.d.ts.map +1 -1
  14. package/dist/adapters/lark/lark-message.parser.js +20 -13
  15. package/dist/adapters/lark/lark-message.parser.js.map +1 -1
  16. package/dist/adapters/lark/lark-signature.d.ts.map +1 -1
  17. package/dist/adapters/lark/lark-signature.js +6 -0
  18. package/dist/adapters/lark/lark-signature.js.map +1 -1
  19. package/dist/adapters/lark/lark.adapter.d.ts +14 -9
  20. package/dist/adapters/lark/lark.adapter.d.ts.map +1 -1
  21. package/dist/adapters/lark/lark.adapter.js +99 -177
  22. package/dist/adapters/lark/lark.adapter.js.map +1 -1
  23. package/dist/adapters/lark/lark.controller.d.ts +10 -1
  24. package/dist/adapters/lark/lark.controller.d.ts.map +1 -1
  25. package/dist/adapters/lark/lark.controller.js +48 -14
  26. package/dist/adapters/lark/lark.controller.js.map +1 -1
  27. package/dist/adapters/slack/slack-block.builder.d.ts +8 -0
  28. package/dist/adapters/slack/slack-block.builder.d.ts.map +1 -0
  29. package/dist/adapters/slack/slack-block.builder.js +117 -0
  30. package/dist/adapters/slack/slack-block.builder.js.map +1 -0
  31. package/dist/adapters/slack/slack-message.parser.d.ts +15 -0
  32. package/dist/adapters/slack/slack-message.parser.d.ts.map +1 -0
  33. package/dist/adapters/slack/slack-message.parser.js +158 -0
  34. package/dist/adapters/slack/slack-message.parser.js.map +1 -0
  35. package/dist/adapters/slack/slack-signature.d.ts +7 -0
  36. package/dist/adapters/slack/slack-signature.d.ts.map +1 -0
  37. package/dist/adapters/slack/slack-signature.js +59 -0
  38. package/dist/adapters/slack/slack-signature.js.map +1 -0
  39. package/dist/adapters/slack/slack.adapter.d.ts +67 -13
  40. package/dist/adapters/slack/slack.adapter.d.ts.map +1 -1
  41. package/dist/adapters/slack/slack.adapter.js +468 -19
  42. package/dist/adapters/slack/slack.adapter.js.map +1 -1
  43. package/dist/adapters/slack/slack.controller.d.ts +20 -0
  44. package/dist/adapters/slack/slack.controller.d.ts.map +1 -0
  45. package/dist/adapters/slack/slack.controller.js +257 -0
  46. package/dist/adapters/slack/slack.controller.js.map +1 -0
  47. package/dist/app.module.d.ts.map +1 -1
  48. package/dist/app.module.js +4 -0
  49. package/dist/app.module.js.map +1 -1
  50. package/dist/auth/auth.controller.d.ts.map +1 -1
  51. package/dist/auth/auth.controller.js +1 -0
  52. package/dist/auth/auth.controller.js.map +1 -1
  53. package/dist/auth/auth.module.d.ts.map +1 -1
  54. package/dist/auth/auth.module.js +4 -5
  55. package/dist/auth/auth.module.js.map +1 -1
  56. package/dist/auth/auth.service.d.ts +9 -2
  57. package/dist/auth/auth.service.d.ts.map +1 -1
  58. package/dist/auth/auth.service.js +50 -79
  59. package/dist/auth/auth.service.js.map +1 -1
  60. package/dist/auth/authenticated-request.d.ts +10 -0
  61. package/dist/auth/authenticated-request.d.ts.map +1 -0
  62. package/dist/auth/authenticated-request.js +3 -0
  63. package/dist/auth/authenticated-request.js.map +1 -0
  64. package/dist/auth/extract-user.middleware.d.ts.map +1 -1
  65. package/dist/auth/extract-user.middleware.js +2 -1
  66. package/dist/auth/extract-user.middleware.js.map +1 -1
  67. package/dist/auth/guards/jwt-auth.guard.d.ts.map +1 -1
  68. package/dist/auth/guards/jwt-auth.guard.js +5 -2
  69. package/dist/auth/guards/jwt-auth.guard.js.map +1 -1
  70. package/dist/auth/guards/project-role.guard.d.ts.map +1 -1
  71. package/dist/auth/guards/project-role.guard.js +6 -8
  72. package/dist/auth/guards/project-role.guard.js.map +1 -1
  73. package/dist/auth/jwt.strategy.d.ts +6 -3
  74. package/dist/auth/jwt.strategy.d.ts.map +1 -1
  75. package/dist/auth/jwt.strategy.js +15 -5
  76. package/dist/auth/jwt.strategy.js.map +1 -1
  77. package/dist/common/command-parser.d.ts +29 -0
  78. package/dist/common/command-parser.d.ts.map +1 -0
  79. package/dist/common/command-parser.js +133 -0
  80. package/dist/common/command-parser.js.map +1 -0
  81. package/dist/common/config.d.ts +17 -0
  82. package/dist/common/config.d.ts.map +1 -0
  83. package/dist/common/config.js +47 -0
  84. package/dist/common/config.js.map +1 -0
  85. package/dist/common/crypto.service.d.ts +4 -1
  86. package/dist/common/crypto.service.d.ts.map +1 -1
  87. package/dist/common/crypto.service.js +14 -7
  88. package/dist/common/crypto.service.js.map +1 -1
  89. package/dist/common/error-filter.d.ts +1 -0
  90. package/dist/common/error-filter.d.ts.map +1 -1
  91. package/dist/common/error-filter.js +6 -2
  92. package/dist/common/error-filter.js.map +1 -1
  93. package/dist/common/git-utils.d.ts +9 -0
  94. package/dist/common/git-utils.d.ts.map +1 -0
  95. package/dist/common/git-utils.js +41 -0
  96. package/dist/common/git-utils.js.map +1 -0
  97. package/dist/common/health.controller.d.ts.map +1 -1
  98. package/dist/common/health.controller.js +3 -5
  99. package/dist/common/health.controller.js.map +1 -1
  100. package/dist/common/machine-utils.d.ts +32 -0
  101. package/dist/common/machine-utils.d.ts.map +1 -0
  102. package/dist/common/machine-utils.js +12 -0
  103. package/dist/common/machine-utils.js.map +1 -0
  104. package/dist/common/pagination.d.ts +12 -5
  105. package/dist/common/pagination.d.ts.map +1 -1
  106. package/dist/common/pagination.js +27 -17
  107. package/dist/common/pagination.js.map +1 -1
  108. package/dist/common/project-validation.d.ts +7 -0
  109. package/dist/common/project-validation.d.ts.map +1 -0
  110. package/dist/common/project-validation.js +86 -0
  111. package/dist/common/project-validation.js.map +1 -0
  112. package/dist/common/rate-limit.guard.d.ts +4 -3
  113. package/dist/common/rate-limit.guard.d.ts.map +1 -1
  114. package/dist/common/rate-limit.guard.js +14 -5
  115. package/dist/common/rate-limit.guard.js.map +1 -1
  116. package/dist/common/sql-utils.d.ts +6 -0
  117. package/dist/common/sql-utils.d.ts.map +1 -0
  118. package/dist/common/sql-utils.js +11 -0
  119. package/dist/common/sql-utils.js.map +1 -0
  120. package/dist/common/string-utils.d.ts +6 -0
  121. package/dist/common/string-utils.d.ts.map +1 -0
  122. package/dist/common/string-utils.js +15 -0
  123. package/dist/common/string-utils.js.map +1 -0
  124. package/dist/common/worker-utils.d.ts +31 -0
  125. package/dist/common/worker-utils.d.ts.map +1 -0
  126. package/dist/common/worker-utils.js +12 -0
  127. package/dist/common/worker-utils.js.map +1 -0
  128. package/dist/database/base.repository.d.ts +56 -0
  129. package/dist/database/base.repository.d.ts.map +1 -0
  130. package/dist/database/base.repository.js +82 -0
  131. package/dist/database/base.repository.js.map +1 -0
  132. package/dist/database/database.service.d.ts.map +1 -1
  133. package/dist/database/database.service.js +9 -1
  134. package/dist/database/database.service.js.map +1 -1
  135. package/dist/database/migration-runner.d.ts.map +1 -1
  136. package/dist/database/migration-runner.js +2 -1
  137. package/dist/database/migration-runner.js.map +1 -1
  138. package/dist/database/repositories/audit-log.repository.d.ts.map +1 -1
  139. package/dist/database/repositories/audit-log.repository.js +16 -18
  140. package/dist/database/repositories/audit-log.repository.js.map +1 -1
  141. package/dist/database/repositories/bot.repository.d.ts +18 -32
  142. package/dist/database/repositories/bot.repository.d.ts.map +1 -1
  143. package/dist/database/repositories/bot.repository.js +42 -21
  144. package/dist/database/repositories/bot.repository.js.map +1 -1
  145. package/dist/database/repositories/developer-token.repository.d.ts +7 -17
  146. package/dist/database/repositories/developer-token.repository.d.ts.map +1 -1
  147. package/dist/database/repositories/developer-token.repository.js +24 -15
  148. package/dist/database/repositories/developer-token.repository.js.map +1 -1
  149. package/dist/database/repositories/developer.repository.d.ts +5 -1
  150. package/dist/database/repositories/developer.repository.d.ts.map +1 -1
  151. package/dist/database/repositories/developer.repository.js +60 -49
  152. package/dist/database/repositories/developer.repository.js.map +1 -1
  153. package/dist/database/repositories/machine.repository.d.ts.map +1 -1
  154. package/dist/database/repositories/machine.repository.js +2 -7
  155. package/dist/database/repositories/machine.repository.js.map +1 -1
  156. package/dist/database/repositories/notification.repository.d.ts +1 -0
  157. package/dist/database/repositories/notification.repository.d.ts.map +1 -1
  158. package/dist/database/repositories/notification.repository.js +25 -20
  159. package/dist/database/repositories/notification.repository.js.map +1 -1
  160. package/dist/database/repositories/project-member.repository.d.ts +7 -16
  161. package/dist/database/repositories/project-member.repository.d.ts.map +1 -1
  162. package/dist/database/repositories/project-member.repository.js +34 -24
  163. package/dist/database/repositories/project-member.repository.js.map +1 -1
  164. package/dist/database/repositories/project.repository.d.ts +2 -1
  165. package/dist/database/repositories/project.repository.d.ts.map +1 -1
  166. package/dist/database/repositories/project.repository.js +70 -71
  167. package/dist/database/repositories/project.repository.js.map +1 -1
  168. package/dist/database/repositories/session.repository.d.ts.map +1 -1
  169. package/dist/database/repositories/session.repository.js +22 -25
  170. package/dist/database/repositories/session.repository.js.map +1 -1
  171. package/dist/database/repositories/task.repository.d.ts +31 -7
  172. package/dist/database/repositories/task.repository.d.ts.map +1 -1
  173. package/dist/database/repositories/task.repository.js +134 -86
  174. package/dist/database/repositories/task.repository.js.map +1 -1
  175. package/dist/database/repositories/worker-token.repository.d.ts.map +1 -1
  176. package/dist/database/repositories/worker-token.repository.js +18 -16
  177. package/dist/database/repositories/worker-token.repository.js.map +1 -1
  178. package/dist/database/repositories/worker.repository.d.ts +50 -0
  179. package/dist/database/repositories/worker.repository.d.ts.map +1 -0
  180. package/dist/database/repositories/worker.repository.js +215 -0
  181. package/dist/database/repositories/worker.repository.js.map +1 -0
  182. package/dist/database/repositories/workspace.repository.d.ts +3 -2
  183. package/dist/database/repositories/workspace.repository.d.ts.map +1 -1
  184. package/dist/database/repositories/workspace.repository.js +29 -21
  185. package/dist/database/repositories/workspace.repository.js.map +1 -1
  186. package/dist/database/repository.module.d.ts +3 -0
  187. package/dist/database/repository.module.d.ts.map +1 -0
  188. package/dist/database/repository.module.js +45 -0
  189. package/dist/database/repository.module.js.map +1 -0
  190. package/dist/dispatcher/capability.service.d.ts +19 -14
  191. package/dist/dispatcher/capability.service.d.ts.map +1 -1
  192. package/dist/dispatcher/capability.service.js +77 -69
  193. package/dist/dispatcher/capability.service.js.map +1 -1
  194. package/dist/dispatcher/cleanup.service.d.ts +1 -1
  195. package/dist/dispatcher/cleanup.service.d.ts.map +1 -1
  196. package/dist/dispatcher/cleanup.service.js +13 -13
  197. package/dist/dispatcher/cleanup.service.js.map +1 -1
  198. package/dist/dispatcher/dedup.service.d.ts +17 -3
  199. package/dist/dispatcher/dedup.service.d.ts.map +1 -1
  200. package/dist/dispatcher/dedup.service.js +76 -82
  201. package/dist/dispatcher/dedup.service.js.map +1 -1
  202. package/dist/dispatcher/dispatcher.module.d.ts.map +1 -1
  203. package/dist/dispatcher/dispatcher.module.js +11 -18
  204. package/dist/dispatcher/dispatcher.module.js.map +1 -1
  205. package/dist/dispatcher/dispatcher.service.d.ts +14 -116
  206. package/dist/dispatcher/dispatcher.service.d.ts.map +1 -1
  207. package/dist/dispatcher/dispatcher.service.js +62 -940
  208. package/dist/dispatcher/dispatcher.service.js.map +1 -1
  209. package/dist/dispatcher/dispatcher.types.d.ts +33 -0
  210. package/dist/dispatcher/dispatcher.types.d.ts.map +1 -0
  211. package/dist/dispatcher/dispatcher.types.js +3 -0
  212. package/dist/dispatcher/dispatcher.types.js.map +1 -0
  213. package/dist/dispatcher/heartbeat.service.d.ts +17 -10
  214. package/dist/dispatcher/heartbeat.service.d.ts.map +1 -1
  215. package/dist/dispatcher/heartbeat.service.js +47 -51
  216. package/dist/dispatcher/heartbeat.service.js.map +1 -1
  217. package/dist/dispatcher/pty-relay.service.d.ts.map +1 -1
  218. package/dist/dispatcher/pty-relay.service.js +7 -15
  219. package/dist/dispatcher/pty-relay.service.js.map +1 -1
  220. package/dist/dispatcher/reconciler.d.ts +18 -8
  221. package/dist/dispatcher/reconciler.d.ts.map +1 -1
  222. package/dist/dispatcher/reconciler.js +219 -130
  223. package/dist/dispatcher/reconciler.js.map +1 -1
  224. package/dist/dispatcher/scheduler.service.d.ts +15 -9
  225. package/dist/dispatcher/scheduler.service.d.ts.map +1 -1
  226. package/dist/dispatcher/scheduler.service.js +95 -53
  227. package/dist/dispatcher/scheduler.service.js.map +1 -1
  228. package/dist/dispatcher/state-machine.d.ts.map +1 -1
  229. package/dist/dispatcher/state-machine.js +1 -5
  230. package/dist/dispatcher/state-machine.js.map +1 -1
  231. package/dist/dispatcher/task-creation.service.d.ts +30 -0
  232. package/dist/dispatcher/task-creation.service.d.ts.map +1 -0
  233. package/dist/dispatcher/task-creation.service.js +242 -0
  234. package/dist/dispatcher/task-creation.service.js.map +1 -0
  235. package/dist/dispatcher/task-lifecycle.service.d.ts +63 -0
  236. package/dist/dispatcher/task-lifecycle.service.d.ts.map +1 -0
  237. package/dist/dispatcher/task-lifecycle.service.js +584 -0
  238. package/dist/dispatcher/task-lifecycle.service.js.map +1 -0
  239. package/dist/dispatcher/task-log-batcher.d.ts.map +1 -1
  240. package/dist/dispatcher/task-log-batcher.js +4 -11
  241. package/dist/dispatcher/task-log-batcher.js.map +1 -1
  242. package/dist/dispatcher/worker-auth.service.d.ts +29 -0
  243. package/dist/dispatcher/worker-auth.service.d.ts.map +1 -0
  244. package/dist/dispatcher/worker-auth.service.js +296 -0
  245. package/dist/dispatcher/worker-auth.service.js.map +1 -0
  246. package/dist/dispatcher/worker-connection.manager.d.ts +15 -15
  247. package/dist/dispatcher/worker-connection.manager.d.ts.map +1 -1
  248. package/dist/dispatcher/worker-connection.manager.js +35 -43
  249. package/dist/dispatcher/worker-connection.manager.js.map +1 -1
  250. package/dist/dispatcher/worker-selector.d.ts +18 -0
  251. package/dist/dispatcher/worker-selector.d.ts.map +1 -0
  252. package/dist/dispatcher/worker-selector.js +150 -0
  253. package/dist/dispatcher/worker-selector.js.map +1 -0
  254. package/dist/events/event-types.d.ts +31 -0
  255. package/dist/events/event-types.d.ts.map +1 -0
  256. package/dist/events/event-types.js +16 -0
  257. package/dist/events/event-types.js.map +1 -0
  258. package/dist/events/events.module.d.ts +7 -0
  259. package/dist/events/events.module.d.ts.map +1 -0
  260. package/dist/events/events.module.js +26 -0
  261. package/dist/events/events.module.js.map +1 -0
  262. package/dist/main.js +22 -0
  263. package/dist/main.js.map +1 -1
  264. package/dist/notifier/debouncer.d.ts +1 -1
  265. package/dist/notifier/debouncer.d.ts.map +1 -1
  266. package/dist/notifier/debouncer.js +2 -1
  267. package/dist/notifier/debouncer.js.map +1 -1
  268. package/dist/notifier/notification-consumer.d.ts +1 -1
  269. package/dist/notifier/notification-consumer.d.ts.map +1 -1
  270. package/dist/notifier/notification-consumer.js +5 -5
  271. package/dist/notifier/notification-consumer.js.map +1 -1
  272. package/dist/notifier/notifier.module.d.ts.map +1 -1
  273. package/dist/notifier/notifier.module.js +0 -6
  274. package/dist/notifier/notifier.module.js.map +1 -1
  275. package/dist/notifier/notifier.service.d.ts +1 -1
  276. package/dist/notifier/notifier.service.d.ts.map +1 -1
  277. package/dist/notifier/notifier.service.js +7 -9
  278. package/dist/notifier/notifier.service.js.map +1 -1
  279. package/dist/notifier/template.service.d.ts +1 -1
  280. package/dist/notifier/template.service.d.ts.map +1 -1
  281. package/dist/notifier/template.service.js +6 -10
  282. package/dist/notifier/template.service.js.map +1 -1
  283. package/dist/redis/redis.service.d.ts.map +1 -1
  284. package/dist/redis/redis.service.js +2 -2
  285. package/dist/redis/redis.service.js.map +1 -1
  286. package/dist/web/admin/admin-audit.controller.d.ts.map +1 -1
  287. package/dist/web/admin/admin-audit.controller.js +2 -1
  288. package/dist/web/admin/admin-audit.controller.js.map +1 -1
  289. package/dist/web/admin/admin-bot.controller.d.ts +11 -48
  290. package/dist/web/admin/admin-bot.controller.d.ts.map +1 -1
  291. package/dist/web/admin/admin-bot.controller.js +50 -18
  292. package/dist/web/admin/admin-bot.controller.js.map +1 -1
  293. package/dist/web/admin/admin-developer.controller.d.ts +14 -27
  294. package/dist/web/admin/admin-developer.controller.d.ts.map +1 -1
  295. package/dist/web/admin/admin-developer.controller.js +62 -28
  296. package/dist/web/admin/admin-developer.controller.js.map +1 -1
  297. package/dist/web/admin/admin-machine.controller.d.ts +1 -8
  298. package/dist/web/admin/admin-machine.controller.d.ts.map +1 -1
  299. package/dist/web/admin/admin-machine.controller.js +3 -6
  300. package/dist/web/admin/admin-machine.controller.js.map +1 -1
  301. package/dist/web/admin/admin-project.controller.d.ts +9 -30
  302. package/dist/web/admin/admin-project.controller.d.ts.map +1 -1
  303. package/dist/web/admin/admin-project.controller.js +15 -60
  304. package/dist/web/admin/admin-project.controller.js.map +1 -1
  305. package/dist/web/admin/admin-settings.controller.d.ts +7 -10
  306. package/dist/web/admin/admin-settings.controller.d.ts.map +1 -1
  307. package/dist/web/admin/admin-settings.controller.js +14 -6
  308. package/dist/web/admin/admin-settings.controller.js.map +1 -1
  309. package/dist/web/admin/admin-token.controller.d.ts +6 -13
  310. package/dist/web/admin/admin-token.controller.d.ts.map +1 -1
  311. package/dist/web/admin/admin-token.controller.js +15 -27
  312. package/dist/web/admin/admin-token.controller.js.map +1 -1
  313. package/dist/web/admin/admin-worker.controller.d.ts +26 -0
  314. package/dist/web/admin/admin-worker.controller.d.ts.map +1 -0
  315. package/dist/web/admin/admin-worker.controller.js +184 -0
  316. package/dist/web/admin/admin-worker.controller.js.map +1 -0
  317. package/dist/web/dashboard.controller.d.ts +6 -12
  318. package/dist/web/dashboard.controller.d.ts.map +1 -1
  319. package/dist/web/dashboard.controller.js +30 -18
  320. package/dist/web/dashboard.controller.js.map +1 -1
  321. package/dist/web/dashboard.service.d.ts +21 -12
  322. package/dist/web/dashboard.service.d.ts.map +1 -1
  323. package/dist/web/dashboard.service.js +169 -119
  324. package/dist/web/dashboard.service.js.map +1 -1
  325. package/dist/web/event.gateway.d.ts +32 -0
  326. package/dist/web/event.gateway.d.ts.map +1 -0
  327. package/dist/web/event.gateway.js +168 -0
  328. package/dist/web/event.gateway.js.map +1 -0
  329. package/dist/web/frame-handlers/frame-handler.interface.d.ts +24 -0
  330. package/dist/web/frame-handlers/frame-handler.interface.d.ts.map +1 -0
  331. package/dist/web/frame-handlers/frame-handler.interface.js +3 -0
  332. package/dist/web/frame-handlers/frame-handler.interface.js.map +1 -0
  333. package/dist/web/frame-handlers/frame-handler.registry.d.ts +16 -0
  334. package/dist/web/frame-handlers/frame-handler.registry.d.ts.map +1 -0
  335. package/dist/web/frame-handlers/frame-handler.registry.js +39 -0
  336. package/dist/web/frame-handlers/frame-handler.registry.js.map +1 -0
  337. package/dist/web/frame-handlers/heartbeat.handler.d.ts +13 -0
  338. package/dist/web/frame-handlers/heartbeat.handler.d.ts.map +1 -0
  339. package/dist/web/frame-handlers/heartbeat.handler.js +35 -0
  340. package/dist/web/frame-handlers/heartbeat.handler.js.map +1 -0
  341. package/dist/web/frame-handlers/index.d.ts +7 -0
  342. package/dist/web/frame-handlers/index.d.ts.map +1 -0
  343. package/dist/web/frame-handlers/index.js +14 -0
  344. package/dist/web/frame-handlers/index.js.map +1 -0
  345. package/dist/web/frame-handlers/progress.handler.d.ts +25 -0
  346. package/dist/web/frame-handlers/progress.handler.d.ts.map +1 -0
  347. package/dist/web/frame-handlers/progress.handler.js +69 -0
  348. package/dist/web/frame-handlers/progress.handler.js.map +1 -0
  349. package/dist/web/frame-handlers/stage-confirm.handler.d.ts +15 -0
  350. package/dist/web/frame-handlers/stage-confirm.handler.d.ts.map +1 -0
  351. package/dist/web/frame-handlers/stage-confirm.handler.js +39 -0
  352. package/dist/web/frame-handlers/stage-confirm.handler.js.map +1 -0
  353. package/dist/web/frame-handlers/tunnel.handler.d.ts +10 -0
  354. package/dist/web/frame-handlers/tunnel.handler.d.ts.map +1 -0
  355. package/dist/web/frame-handlers/tunnel.handler.js +31 -0
  356. package/dist/web/frame-handlers/tunnel.handler.js.map +1 -0
  357. package/dist/web/interaction.service.d.ts +0 -4
  358. package/dist/web/interaction.service.d.ts.map +1 -1
  359. package/dist/web/interaction.service.js +0 -10
  360. package/dist/web/interaction.service.js.map +1 -1
  361. package/dist/web/machine.controller.d.ts +1 -8
  362. package/dist/web/machine.controller.d.ts.map +1 -1
  363. package/dist/web/machine.controller.js +6 -9
  364. package/dist/web/machine.controller.js.map +1 -1
  365. package/dist/web/notification.controller.d.ts +1 -8
  366. package/dist/web/notification.controller.d.ts.map +1 -1
  367. package/dist/web/notification.controller.js +3 -2
  368. package/dist/web/notification.controller.js.map +1 -1
  369. package/dist/web/profile.controller.d.ts +19 -10
  370. package/dist/web/profile.controller.d.ts.map +1 -1
  371. package/dist/web/profile.controller.js +100 -13
  372. package/dist/web/profile.controller.js.map +1 -1
  373. package/dist/web/project-member.service.d.ts +16 -0
  374. package/dist/web/project-member.service.d.ts.map +1 -0
  375. package/dist/web/project-member.service.js +90 -0
  376. package/dist/web/project-member.service.js.map +1 -0
  377. package/dist/web/project.controller.d.ts +43 -26
  378. package/dist/web/project.controller.d.ts.map +1 -1
  379. package/dist/web/project.controller.js +73 -46
  380. package/dist/web/project.controller.js.map +1 -1
  381. package/dist/web/pty.gateway.d.ts +9 -3
  382. package/dist/web/pty.gateway.d.ts.map +1 -1
  383. package/dist/web/pty.gateway.js +56 -22
  384. package/dist/web/pty.gateway.js.map +1 -1
  385. package/dist/web/search.service.d.ts +9 -2
  386. package/dist/web/search.service.d.ts.map +1 -1
  387. package/dist/web/search.service.js +53 -26
  388. package/dist/web/search.service.js.map +1 -1
  389. package/dist/web/task.controller.d.ts +15 -24
  390. package/dist/web/task.controller.d.ts.map +1 -1
  391. package/dist/web/task.controller.js +70 -53
  392. package/dist/web/task.controller.js.map +1 -1
  393. package/dist/web/tunnel.service.d.ts +74 -0
  394. package/dist/web/tunnel.service.d.ts.map +1 -0
  395. package/dist/web/tunnel.service.js +250 -0
  396. package/dist/web/tunnel.service.js.map +1 -0
  397. package/dist/web/web-event.service.d.ts +25 -0
  398. package/dist/web/web-event.service.d.ts.map +1 -0
  399. package/dist/web/web-event.service.js +116 -0
  400. package/dist/web/web-event.service.js.map +1 -0
  401. package/dist/web/web.module.d.ts.map +1 -1
  402. package/dist/web/web.module.js +13 -28
  403. package/dist/web/web.module.js.map +1 -1
  404. package/dist/web/worker-channel.gateway.d.ts +10 -18
  405. package/dist/web/worker-channel.gateway.d.ts.map +1 -1
  406. package/dist/web/worker-channel.gateway.js +70 -144
  407. package/dist/web/worker-channel.gateway.js.map +1 -1
  408. package/dist/web/worker-web.controller.d.ts +15 -0
  409. package/dist/web/worker-web.controller.d.ts.map +1 -0
  410. package/dist/web/worker-web.controller.js +143 -0
  411. package/dist/web/worker-web.controller.js.map +1 -0
  412. package/dist/web/worker.controller.d.ts +3 -3
  413. package/dist/web/worker.controller.d.ts.map +1 -1
  414. package/dist/web/worker.controller.js +8 -8
  415. package/dist/web/worker.controller.js.map +1 -1
  416. package/dist/web/workspace.controller.d.ts +8 -33
  417. package/dist/web/workspace.controller.d.ts.map +1 -1
  418. package/dist/web/workspace.controller.js +93 -205
  419. package/dist/web/workspace.controller.js.map +1 -1
  420. package/package.json +10 -2
  421. package/public/apple-touch-icon-120x120.png +0 -0
  422. package/public/apple-touch-icon-152x152.png +0 -0
  423. package/public/apple-touch-icon-180x180.png +0 -0
  424. package/public/assets/AccessTokensPage-DPQB2fbi.js +1 -0
  425. package/public/assets/AdminPage-BqVelYNu.js +1 -0
  426. package/public/assets/ApiReferencePage-CiGvbLxL.js +1 -0
  427. package/public/assets/AuditLogPage-DSo4jVYm.js +6 -0
  428. package/public/assets/BindPlatformPage-CTqzpOmt.js +1 -0
  429. package/public/assets/BotManage-CIR0rrK7.js +6 -0
  430. package/public/assets/CliReferencePage-C8GmlwUz.js +14 -0
  431. package/public/assets/DeveloperManage-r6y2AoB4.js +16 -0
  432. package/public/assets/EditProjectPage-7WCsNltj.js +2 -0
  433. package/public/assets/{EmptyState-BOXPalWI.js → EmptyState-D3foEiul.js} +1 -1
  434. package/public/assets/HomePage-D4yv4orb.js +1 -0
  435. package/public/assets/InfoRow-DhdTYoY9.js +1 -0
  436. package/public/assets/LandingPage-CqS0E2eC.js +43 -0
  437. package/public/assets/LoginPage-DDXkdcz_.js +1 -0
  438. package/public/assets/MetricBar-DMMHfS0A.js +1 -0
  439. package/public/assets/{NotFoundPage-griwga5q.js → NotFoundPage-D5x5BrlX.js} +1 -1
  440. package/public/assets/OnboardingGuide-D8RyPcEd.js +1 -0
  441. package/public/assets/PipelineEditorPage-y2-Q8ofQ.js +3 -0
  442. package/public/assets/ProfilePage-DN7usHOi.js +1 -0
  443. package/public/assets/ProjectDetailPage-DJexg49z.js +7 -0
  444. package/public/assets/ProjectListPage-Bz7I2D0H.js +6 -0
  445. package/public/assets/QuickAuth-Dr0Q50ld.js +1 -0
  446. package/public/assets/{RemoveMemberConfirmDialog-I3k9sPON.js → RemoveMemberConfirmDialog-BCrue0AP.js} +2 -2
  447. package/public/assets/Select-BnV8yZlD.js +6 -0
  448. package/public/assets/SettingsPage-HaUCcsgl.js +6 -0
  449. package/public/assets/{Skeleton-0JE10nwo.js → Skeleton-DUgWc2LJ.js} +1 -1
  450. package/public/assets/SkillPage-BInwZTQh.js +1 -0
  451. package/public/assets/TaskDetailPage-CfwEj1hy.js +31 -0
  452. package/public/assets/TaskListPage-Dh59ldSZ.js +1 -0
  453. package/public/assets/TaskStatusBadge-DuOoGIwE.js +1 -0
  454. package/public/assets/TerminalHomePage-BwXJjr-a.js +16 -0
  455. package/public/assets/TokenManage-B0Cpv6SO.js +1 -0
  456. package/public/assets/TotpSetupPage-MSCCURj9.js +9 -0
  457. package/public/assets/WorkerDetailPage-R2veIzKo.js +1 -0
  458. package/public/assets/WorkerListPage-CserMjGO.js +6 -0
  459. package/public/assets/WorkerSetupGuidePage-SqO2lzVa.js +11 -0
  460. package/public/assets/{arrow-left-C-OTbm1J.js → arrow-left-DklRsENx.js} +1 -1
  461. package/public/assets/{arrow-right-B5aaHrGs.js → arrow-right-MDrzFe3K.js} +1 -1
  462. package/public/assets/{bot-KMbKzBkt.js → bot-DPaziJPf.js} +1 -1
  463. package/public/assets/{chevron-right-CVPdQ-cP.js → chevron-right-CqyufMDW.js} +1 -1
  464. package/public/assets/{copy-Dd1cNNWz.js → copy-BUH7P2Hf.js} +1 -1
  465. package/public/assets/date-BdNtiQTP.js +1 -0
  466. package/public/assets/{external-link-F-d1_j4T.js → external-link-ChPgQ7N_.js} +1 -1
  467. package/public/assets/index-BS0Fbx5V.css +1 -0
  468. package/public/assets/index-vL7aQJNr.js +225 -0
  469. package/public/assets/{key-CT_RkMPI.js → key-CxvwwHnW.js} +1 -1
  470. package/public/assets/{loader-circle-BnJr5Xpn.js → loader-circle-DS5g1-Od.js} +1 -1
  471. package/public/assets/password-CHk45-jw.js +1 -0
  472. package/public/assets/{pencil-Srq1Z7Yh.js → pencil-B6spIBcw.js} +1 -1
  473. package/public/assets/{plus-Ry_MQV9O.js → plus-Bnd1Vz2Y.js} +1 -1
  474. package/public/assets/{rotate-ccw-B1ZO6xeO.js → rotate-ccw-CgcLAXNR.js} +1 -1
  475. package/public/assets/{scroll-text-CP6Z7Xff.js → scroll-text-CecZ0Fk5.js} +1 -1
  476. package/public/assets/{settings-Ac7uhvR0.js → settings-C1uOD3PZ.js} +1 -1
  477. package/public/assets/status-colors-BPEUp90-.js +1 -0
  478. package/public/assets/string-B39tzdVK.js +1 -0
  479. package/public/assets/task-constants-BbFyCyKk.js +14 -0
  480. package/public/assets/task.store-BE6fEPu4.js +1 -0
  481. package/public/assets/{trash-2-DWv3OoER.js → trash-2-A2FsT1yG.js} +1 -1
  482. package/public/assets/useFetch-vGZMAvGi.js +1 -0
  483. package/public/assets/{users-BsM5ZXj8.js → users-CEdRS_A3.js} +1 -1
  484. package/public/assets/wifi-D60NkK6F.js +6 -0
  485. package/public/assets/zap-DXw1NrWz.js +6 -0
  486. package/public/icon-192x192.png +0 -0
  487. package/public/icon-512x512.png +0 -0
  488. package/public/icon-maskable-192x192.png +0 -0
  489. package/public/icon-maskable-512x512.png +0 -0
  490. package/public/index.html +21 -4
  491. package/public/manifest.webmanifest +1 -0
  492. package/public/og-image.png +0 -0
  493. package/public/registerSW.js +1 -0
  494. package/public/sw.js +1 -0
  495. package/public/workbox-6e9b121d.js +1 -0
  496. package/database/migrations/002-add-indexes.sql +0 -17
  497. package/database/migrations/003-add-settings-table.sql +0 -4
  498. package/database/migrations/004-add-developer-id-index.sql +0 -5
  499. package/database/migrations/005-add-worker-version.sql +0 -2
  500. package/database/migrations/006-add-decommission-fields.sql +0 -2
  501. package/database/migrations/007-add-ssh-url.sql +0 -1
  502. package/public/assets/AccessTokensPage-DypSjrzB.js +0 -1
  503. package/public/assets/AdminPage-BY1ub8Ur.js +0 -1
  504. package/public/assets/ApiReferencePage-X0c9Bj31.js +0 -1
  505. package/public/assets/AuditLogPage-B0bBeD2B.js +0 -6
  506. package/public/assets/BotManage-D7UIzmUX.js +0 -6
  507. package/public/assets/CliReferencePage-DAqUe3dC.js +0 -8
  508. package/public/assets/DeveloperManage-Df4qgJ4d.js +0 -16
  509. package/public/assets/EditProjectPage-B05CUiFx.js +0 -2
  510. package/public/assets/HomePage-YM1Wcq5V.js +0 -1
  511. package/public/assets/LandingPage-Dn64_5F4.js +0 -36
  512. package/public/assets/LoginPage-CcBF1jm-.js +0 -1
  513. package/public/assets/MachineDetailPage-BHzHO-jG.js +0 -1
  514. package/public/assets/MachineListPage-sTx1mDtP.js +0 -6
  515. package/public/assets/PipelineEditorPage-CZQJn5Qd.js +0 -3
  516. package/public/assets/ProfilePage-CM-HnNqC.js +0 -1
  517. package/public/assets/ProjectDetailPage-d59hpM1f.js +0 -7
  518. package/public/assets/ProjectListPage-j4xLknRG.js +0 -6
  519. package/public/assets/QuickAuth-vbGZYKLu.js +0 -1
  520. package/public/assets/Select-CmUwVfWJ.js +0 -6
  521. package/public/assets/SettingsPage-CDTheJqk.js +0 -6
  522. package/public/assets/SkillPage-ClOtPiNe.js +0 -1
  523. package/public/assets/TaskDetailPage-CG8zmgwV.js +0 -44
  524. package/public/assets/TaskListPage-BUjNGBKm.js +0 -1
  525. package/public/assets/TaskStatusBadge-C8TEMiVe.js +0 -1
  526. package/public/assets/TokenManage-U3YbhV_d.js +0 -1
  527. package/public/assets/TotpSetupPage-BlRM2OEF.js +0 -9
  528. package/public/assets/WorkerSetupGuidePage-BUTz9NXE.js +0 -16
  529. package/public/assets/index-CQojj7Zu.css +0 -1
  530. package/public/assets/index-DJxZmj6O.js +0 -212
  531. package/public/assets/protocol-C5uQmiiB.js +0 -1
  532. package/public/assets/task.store-DSX--5cK.js +0 -1
  533. /package/public/assets/{TaskDetailPage-Beg8tuEN.css → task-constants-Beg8tuEN.css} +0 -0
@@ -1,104 +1,46 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
2
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
5
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
23
7
  };
24
- var __importStar = (this && this.__importStar) || (function () {
25
- var ownKeys = function(o) {
26
- ownKeys = Object.getOwnPropertyNames || function (o) {
27
- var ar = [];
28
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
- return ar;
30
- };
31
- return ownKeys(o);
32
- };
33
- return function (mod) {
34
- if (mod && mod.__esModule) return mod;
35
- var result = {};
36
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
- __setModuleDefault(result, mod);
38
- return result;
39
- };
40
- })();
41
8
  var __metadata = (this && this.__metadata) || function (k, v) {
42
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
10
  };
44
11
  var DispatcherService_1;
45
12
  Object.defineProperty(exports, "__esModule", { value: true });
46
13
  exports.DispatcherService = void 0;
47
- const common_1 = require("@nestjs/common");
48
- const crypto = __importStar(require("node:crypto"));
49
14
  const protocol_1 = require("@overlordai/protocol");
15
+ const common_1 = require("@nestjs/common");
16
+ const event_emitter_1 = require("@nestjs/event-emitter");
50
17
  const task_repository_1 = require("../database/repositories/task.repository");
51
- const machine_repository_1 = require("../database/repositories/machine.repository");
52
- const project_repository_1 = require("../database/repositories/project.repository");
53
- const project_member_repository_1 = require("../database/repositories/project-member.repository");
54
- const worker_token_repository_1 = require("../database/repositories/worker-token.repository");
18
+ const worker_repository_1 = require("../database/repositories/worker.repository");
55
19
  const audit_log_repository_1 = require("../database/repositories/audit-log.repository");
56
20
  const developer_repository_1 = require("../database/repositories/developer.repository");
57
- const database_service_1 = require("../database/database.service");
58
- const auth_service_1 = require("../auth/auth.service");
59
- const crypto_service_1 = require("../common/crypto.service");
60
- const redis_service_1 = require("../redis/redis.service");
61
- const state_machine_1 = require("./state-machine");
21
+ const event_types_1 = require("../events/event-types");
62
22
  const dedup_service_1 = require("./dedup.service");
63
- const scheduler_service_1 = require("./scheduler.service");
64
- const worker_connection_manager_1 = require("./worker-connection.manager");
65
- const cleanup_service_1 = require("./cleanup.service");
66
- const workspace_repository_1 = require("../database/repositories/workspace.repository");
23
+ const task_creation_service_1 = require("./task-creation.service");
24
+ const task_lifecycle_service_1 = require("./task-lifecycle.service");
67
25
  let DispatcherService = DispatcherService_1 = class DispatcherService {
68
26
  taskRepo;
69
- machineRepo;
70
- projectRepo;
71
- projectMemberRepo;
72
- workerTokenRepo;
27
+ workerRepo;
73
28
  auditLogRepo;
74
29
  developerRepo;
75
- databaseService;
76
- authService;
77
- cryptoService;
78
- redis;
79
30
  dedupService;
80
- schedulerService;
81
- workerConnectionManager;
82
- cleanupService;
83
- workspaceRepo;
31
+ taskCreationService;
32
+ taskLifecycleService;
33
+ eventEmitter;
84
34
  logger = new common_1.Logger(DispatcherService_1.name);
85
- constructor(taskRepo, machineRepo, projectRepo, projectMemberRepo, workerTokenRepo, auditLogRepo, developerRepo, databaseService, authService, cryptoService, redis, dedupService, schedulerService, workerConnectionManager, cleanupService, workspaceRepo) {
35
+ constructor(taskRepo, workerRepo, auditLogRepo, developerRepo, dedupService, taskCreationService, taskLifecycleService, eventEmitter) {
86
36
  this.taskRepo = taskRepo;
87
- this.machineRepo = machineRepo;
88
- this.projectRepo = projectRepo;
89
- this.projectMemberRepo = projectMemberRepo;
90
- this.workerTokenRepo = workerTokenRepo;
37
+ this.workerRepo = workerRepo;
91
38
  this.auditLogRepo = auditLogRepo;
92
39
  this.developerRepo = developerRepo;
93
- this.databaseService = databaseService;
94
- this.authService = authService;
95
- this.cryptoService = cryptoService;
96
- this.redis = redis;
97
40
  this.dedupService = dedupService;
98
- this.schedulerService = schedulerService;
99
- this.workerConnectionManager = workerConnectionManager;
100
- this.cleanupService = cleanupService;
101
- this.workspaceRepo = workspaceRepo;
41
+ this.taskCreationService = taskCreationService;
42
+ this.taskLifecycleService = taskLifecycleService;
43
+ this.eventEmitter = eventEmitter;
102
44
  }
103
45
  // ---------------------------------------------------------------------------
104
46
  // handleCommand — unified entry point from adapters
@@ -122,49 +64,20 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
122
64
  return this.handleConfirmCommand(command);
123
65
  case protocol_1.CommandType.LIST:
124
66
  return this.handleListCommand(command);
125
- case protocol_1.CommandType.PROGRESS: {
126
- const taskId = command.args.taskId;
127
- if (!taskId) {
128
- return { success: false, message: 'taskId is required' };
129
- }
130
- const task = this.taskRepo.findById(taskId);
131
- if (!task) {
132
- return { success: false, message: `Task #${taskId} not found` };
133
- }
134
- return { success: true, task };
135
- }
136
- case protocol_1.CommandType.LOGS: {
137
- const taskId = command.args.taskId;
138
- if (!taskId) {
139
- return { success: false, message: 'taskId is required' };
140
- }
141
- const task = this.taskRepo.findById(taskId);
142
- if (!task) {
143
- return { success: false, message: `Task #${taskId} not found` };
144
- }
145
- return { success: true, task };
146
- }
67
+ case protocol_1.CommandType.PROGRESS:
68
+ case protocol_1.CommandType.LOGS:
69
+ case protocol_1.CommandType.WORKSPACE:
70
+ return this.requireTask(command.args.taskId);
147
71
  case protocol_1.CommandType.DRAIN:
148
72
  return this.handleDrainCommand(command, true);
149
73
  case protocol_1.CommandType.UNDRAIN:
150
74
  return this.handleDrainCommand(command, false);
151
- case protocol_1.CommandType.WORKSPACE: {
152
- const taskId = command.args.taskId;
153
- if (!taskId) {
154
- return { success: false, message: 'taskId is required' };
155
- }
156
- const task = this.taskRepo.findById(taskId);
157
- if (!task) {
158
- return { success: false, message: `Task #${taskId} not found` };
159
- }
160
- return { success: true, task };
161
- }
162
75
  default:
163
76
  return { success: false, message: `Unknown command type: ${command.type}` };
164
77
  }
165
78
  }
166
79
  // ---------------------------------------------------------------------------
167
- // Command handlers
80
+ // Command handlers — thin wrappers that delegate to focused services
168
81
  // ---------------------------------------------------------------------------
169
82
  async handleDevelopCommand(command) {
170
83
  const { description, project: projectKeyOrAlias } = command.args;
@@ -174,11 +87,11 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
174
87
  const request = {
175
88
  description,
176
89
  projectKey: projectKeyOrAlias ?? '',
177
- machineId: command.args.machine,
90
+ workerId: command.args.worker,
178
91
  developerId: command.user.id,
179
92
  };
180
93
  try {
181
- const task = await this.createTask(request, command.user.id, {
94
+ const task = await this.taskCreationService.createTask(request, command.user.id, {
182
95
  platform: command.platform,
183
96
  msgId: command.source.msgId,
184
97
  chatId: command.source.chatId,
@@ -200,12 +113,22 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
200
113
  throw err;
201
114
  }
202
115
  }
116
+ requireTask(taskId) {
117
+ if (!taskId) {
118
+ return { success: false, message: 'taskId is required' };
119
+ }
120
+ const task = this.taskRepo.findById(taskId);
121
+ if (!task) {
122
+ return { success: false, message: `Task #${taskId} not found` };
123
+ }
124
+ return { success: true, task };
125
+ }
203
126
  async handleCancelCommand(command) {
204
127
  const taskId = command.args.taskId;
205
128
  if (!taskId) {
206
129
  return { success: false, message: 'taskId is required' };
207
130
  }
208
- await this.cancelTask(taskId, command.user.id);
131
+ await this.taskLifecycleService.cancelTask(taskId, command.user.id);
209
132
  const task = this.taskRepo.findById(taskId);
210
133
  return { success: true, task, message: `Task #${taskId} cancelled` };
211
134
  }
@@ -215,7 +138,7 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
215
138
  return { success: false, message: 'taskId is required' };
216
139
  }
217
140
  try {
218
- const task = await this.retryTask(taskId, command.user.id);
141
+ const task = await this.taskLifecycleService.retryTask(taskId, command.user.id);
219
142
  return { success: true, task, message: `Task #${taskId} retried` };
220
143
  }
221
144
  catch (err) {
@@ -238,18 +161,18 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
238
161
  }
239
162
  // Resolve pending confirm from Redis.
240
163
  // Priority: reply-to msgId association, fallback to #taskId lookup.
241
- const pendingData = await this.resolvePendingConfirm(taskId, command.user.id, command.source.msgId);
164
+ const pendingData = await this.taskLifecycleService.resolvePendingConfirm(taskId, command.user.id, command.source.msgId);
242
165
  if (!pendingData) {
243
166
  return { success: false, message: 'No pending confirmation found or expired' };
244
167
  }
245
168
  if (pendingData.action === 'create_task' && pendingData.request) {
246
169
  // Confirmed dedup override — create the task anyway
247
- const task = await this.createTaskInternal(pendingData.request, pendingData.createdBy, pendingData.source, true);
170
+ const task = await this.taskCreationService.createTaskInternal(pendingData.request, pendingData.createdBy, pendingData.source, true);
248
171
  return { success: true, task, message: 'Task created (dedup override)' };
249
172
  }
250
173
  if (pendingData.action === 'retry' && pendingData.taskId) {
251
174
  // Confirmed retry with existing MR
252
- const task = await this.executeRetry(pendingData.taskId, pendingData.createdBy);
175
+ const task = await this.taskLifecycleService.executeRetry(pendingData.taskId, pendingData.createdBy);
253
176
  return { success: true, task, message: `Task #${pendingData.taskId} retried (MR override)` };
254
177
  }
255
178
  return { success: false, message: 'Unknown confirm action' };
@@ -268,377 +191,36 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
268
191
  return { success: true, tasks: filtered };
269
192
  }
270
193
  handleDrainCommand(command, drain) {
271
- const machineName = command.args.machine;
272
- if (!machineName) {
273
- return { success: false, message: 'Machine name is required' };
194
+ const workerName = command.args.worker;
195
+ if (!workerName) {
196
+ return { success: false, message: 'Worker name is required' };
274
197
  }
275
- const machine = this.machineRepo.findByName(machineName);
276
- if (!machine) {
277
- return { success: false, message: `Machine '${machineName}' not found` };
198
+ const worker = this.workerRepo.findByName(workerName);
199
+ if (!worker) {
200
+ return { success: false, message: `Worker '${workerName}' not found` };
278
201
  }
279
202
  const developer = this.developerRepo.findById(command.user.id);
280
203
  if (!developer || developer.role !== protocol_1.DeveloperRole.ADMIN) {
281
- return { success: false, message: 'Only admins can drain/undrain machines' };
282
- }
283
- const newStatus = drain ? protocol_1.MachineStatus.DRAINING : protocol_1.MachineStatus.ONLINE;
284
- this.machineRepo.updateStatus(machine.id, newStatus);
285
- this.auditLogRepo.create({
286
- userId: command.user.id,
287
- action: drain ? 'machine_drain' : 'machine_undrain',
288
- resource: `machine:${machine.id}`,
289
- detail: `Machine '${machine.name}' ${drain ? 'drained' : 'undrained'}`,
290
- });
291
- return { success: true, message: `Machine '${machine.name}' ${drain ? 'drained' : 'undrained'}` };
292
- }
293
- // ---------------------------------------------------------------------------
294
- // createTask
295
- // ---------------------------------------------------------------------------
296
- async createTask(request, createdBy, source) {
297
- return this.createTaskInternal(request, createdBy, source, false);
298
- }
299
- async createTaskInternal(request, createdBy, source, skipDedup = false) {
300
- // 1. Find project by key or alias
301
- const project = this.resolveProject(request.projectKey);
302
- if (!project) {
303
- throw new common_1.NotFoundException(`Project '${request.projectKey}' not found`);
304
- }
305
- // 2. Authorization: check developer is a project member (or admin bypass)
306
- const developer = this.developerRepo.findById(createdBy);
307
- if (!developer) {
308
- throw new common_1.NotFoundException(`Developer #${createdBy} not found`);
309
- }
310
- if (developer.role !== protocol_1.DeveloperRole.ADMIN) {
311
- const membership = this.projectMemberRepo.findByProjectAndDeveloper(project.key, createdBy);
312
- if (!membership) {
313
- throw new common_1.ForbiddenException(`Developer '${developer.name}' is not a member of project '${project.key}'`);
314
- }
315
- }
316
- // 3. Compute fingerprint and check dedup
317
- const fingerprint = this.dedupService.computeFingerprint(project.key, request.description);
318
- if (!skipDedup) {
319
- const existingTask = this.taskRepo.findActiveByFingerprint(fingerprint);
320
- if (existingTask) {
321
- // Store pending confirm in Redis
322
- const confirmKey = `pending_confirm:dedup:${crypto.randomUUID()}`;
323
- const pendingData = JSON.stringify({
324
- action: 'create_task',
325
- request: { ...request, projectKey: project.key },
326
- createdBy,
327
- source,
328
- existingTaskId: existingTask.id,
329
- });
330
- await this.redis.setex(confirmKey, protocol_1.CONFIRM_PENDING_TTL_SEC, pendingData);
331
- // Store index from taskId to confirmKey for lookup during confirm
332
- await this.redis.setex(`pending_confirm_idx:task:${existingTask.id}`, protocol_1.CONFIRM_PENDING_TTL_SEC, confirmKey);
333
- throw new common_1.ConflictException({
334
- message: `A similar task already exists: #${existingTask.id} (${existingTask.status})`,
335
- confirmRequired: {
336
- reason: 'duplicate_fingerprint',
337
- message: `Task #${existingTask.id} with the same description is already ${existingTask.status}. Create anyway?`,
338
- pendingAction: 'create_task',
339
- taskId: existingTask.id,
340
- confirmKey,
341
- },
342
- });
343
- }
344
- }
345
- // 4. Snapshot config (14 fields from project)
346
- const configSnapshot = {
347
- repoUrl: project.repoUrl,
348
- sshUrl: project.sshUrl,
349
- gitPlatform: project.gitPlatform,
350
- defaultBranch: project.defaultBranch,
351
- workspaceRoot: project.workspaceRoot,
352
- setupCommands: project.setupCommands,
353
- testCommand: project.testCommand,
354
- agentType: project.agentType,
355
- agentCommand: project.agentCommand,
356
- agentEnv: project.agentEnv,
357
- allowedTools: project.allowedTools,
358
- maxTurns: project.maxTurns,
359
- skillsPath: project.skillsPath,
360
- pipeline: project.pipeline,
361
- ptyOutputFilter: project.ptyOutputFilter,
362
- };
363
- // 5. Insert task (QUEUED)
364
- let task;
365
- try {
366
- task = this.taskRepo.create({
367
- description: request.description,
368
- fingerprint,
369
- projectKey: project.key,
370
- machineId: request.machineId,
371
- developerId: request.developerId ?? createdBy,
372
- configSnapshot: JSON.stringify(configSnapshot),
373
- sourcePlatform: source?.platform,
374
- sourceMsgId: source?.msgId,
375
- sourceChatId: source?.chatId,
376
- sourceAppId: source?.appId,
377
- createdBy,
378
- });
379
- }
380
- catch (err) {
381
- // Catch UNIQUE constraint violation on fingerprint (concurrent create)
382
- const errMsg = err instanceof Error ? err.message : String(err);
383
- if (errMsg.includes('UNIQUE') || errMsg.includes('unique')) {
384
- throw new common_1.ConflictException('A task with the same fingerprint was just created concurrently');
385
- }
386
- throw err;
387
- }
388
- // 6. Enqueue to BullMQ
389
- await this.schedulerService.enqueueTask(task.id);
390
- this.logger.log(`Task #${task.id} created for project '${project.key}' by developer #${createdBy}`);
391
- this.auditLogRepo.create({
392
- userId: createdBy,
393
- action: 'task_create',
394
- resource: `task:${task.id}`,
395
- detail: `Created task for project '${project.key}': ${request.description.slice(0, 100)}`,
396
- });
397
- return task;
398
- }
399
- // ---------------------------------------------------------------------------
400
- // cancelTask
401
- // ---------------------------------------------------------------------------
402
- async cancelTask(taskId, cancelledBy) {
403
- const task = this.taskRepo.findById(taskId);
404
- if (!task) {
405
- throw new common_1.NotFoundException(`Task #${taskId} not found`);
406
- }
407
- // Validate transition
408
- state_machine_1.TaskStateMachine.assertTransition(task.status, protocol_1.TaskStatus.CANCELLED);
409
- switch (task.status) {
410
- case protocol_1.TaskStatus.QUEUED: {
411
- // Remove from BullMQ — task stays QUEUED until we set CANCELLED below
412
- this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, task.revision);
413
- break;
414
- }
415
- case protocol_1.TaskStatus.RUNNING: {
416
- // Send cancel to Worker via WS, wait ack with timeout
417
- if (task.machineId) {
418
- const acked = await this.sendCancelToWorkerWithAck(task.machineId, taskId);
419
- if (!acked) {
420
- this.logger.warn(`Cancel ack timeout for task #${taskId} on machine ${task.machineId}, forcing cancel`);
421
- }
422
- }
423
- // Reload task to get latest revision (may have changed during ack wait)
424
- const freshTask = this.taskRepo.findById(taskId);
425
- if (freshTask && freshTask.status !== protocol_1.TaskStatus.CANCELLED) {
426
- this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, freshTask.revision, { completedAt: new Date().toISOString() });
427
- }
428
- break;
429
- }
430
- case protocol_1.TaskStatus.ASSIGNED: {
431
- // Re-read to catch ASSIGNED->RUNNING race between initial read and CAS
432
- const freshAssigned = this.taskRepo.findById(taskId);
433
- if (!freshAssigned || freshAssigned.status === protocol_1.TaskStatus.CANCELLED) {
434
- break;
435
- }
436
- state_machine_1.TaskStateMachine.assertTransition(freshAssigned.status, protocol_1.TaskStatus.CANCELLED);
437
- // Send cancel to Worker if assigned, set CANCELLED
438
- if (freshAssigned.machineId) {
439
- this.sendCancelToWorker(freshAssigned.machineId, taskId);
440
- }
441
- this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, freshAssigned.revision, { completedAt: new Date().toISOString() });
442
- break;
443
- }
444
- case protocol_1.TaskStatus.SUSPENDED: {
445
- // Direct set CANCELLED — no Worker ack needed
446
- this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, task.revision, { completedAt: new Date().toISOString() });
447
- break;
448
- }
449
- default:
450
- throw new common_1.BadRequestException(`Cannot cancel task #${taskId} in status ${task.status}`);
451
- }
452
- this.auditLogRepo.create({
453
- userId: cancelledBy,
454
- action: 'task_cancel',
455
- resource: `task:${taskId}`,
456
- detail: `Cancelled task #${taskId} from status ${task.status}`,
204
+ return { success: false, message: 'Only admins can drain/undrain workers' };
205
+ }
206
+ const newStatus = drain ? protocol_1.WorkerStatus.DRAINING : protocol_1.WorkerStatus.ONLINE;
207
+ const previousStatus = worker.status;
208
+ this.workerRepo.updateStatus(worker.id, newStatus);
209
+ this.eventEmitter.emit(event_types_1.DomainEvents.WORKER_STATUS_CHANGED, {
210
+ workerId: worker.id,
211
+ status: newStatus,
212
+ previousStatus,
457
213
  });
458
- // Schedule workspace cleanup for the cancelled task
459
- if (task.machineId) {
460
- const workspace = this.workspaceRepo.findByTaskId(taskId);
461
- if (workspace) {
462
- this.cleanupService.scheduleCleanup(taskId, task.machineId, workspace.path).catch((err) => {
463
- this.logger.error(`Failed to schedule cleanup for cancelled task #${taskId}: ${err instanceof Error ? err.message : String(err)}`);
464
- });
465
- }
466
- }
467
- this.logger.log(`Task #${taskId} cancelled by developer #${cancelledBy}`);
468
- }
469
- // ---------------------------------------------------------------------------
470
- // markTaskRunning — transition ASSIGNED → RUNNING when worker starts executing
471
- // ---------------------------------------------------------------------------
472
- markTaskRunning(taskId) {
473
- const task = this.taskRepo.findById(taskId);
474
- if (!task || task.status !== protocol_1.TaskStatus.ASSIGNED)
475
- return false;
476
- const updated = this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.RUNNING, task.revision, { startedAt: new Date().toISOString() });
477
- if (updated) {
478
- this.auditLogRepo.create({
479
- action: 'task_running_from_worker',
480
- resource: `task:${taskId}`,
481
- detail: `Task marked RUNNING via worker progress frame`,
482
- });
483
- }
484
- return updated;
485
- }
486
- // ---------------------------------------------------------------------------
487
- // markTaskTerminalFromWorker — called by gateway when worker reports failure/cancel
488
- // ---------------------------------------------------------------------------
489
- markTaskTerminalFromWorker(taskId, targetStatus, errorMessage) {
490
- const task = this.taskRepo.findById(taskId);
491
- if (!task)
492
- return false;
493
- // Already in target or other terminal status — nothing to do
494
- if (state_machine_1.TaskStateMachine.isTerminal(task.status))
495
- return false;
496
- // Validate transition is legal
497
- if (!state_machine_1.TaskStateMachine.canTransition(task.status, targetStatus)) {
498
- this.logger.warn(`Cannot transition task #${taskId} from ${task.status} to ${targetStatus} via worker progress`);
499
- return false;
500
- }
501
- const extra = {
502
- completedAt: new Date().toISOString(),
503
- };
504
- if (errorMessage)
505
- extra.errorMessage = errorMessage;
506
- const updated = this.updateTaskStatusWithRetry(taskId, targetStatus, task.revision, extra);
507
- if (updated) {
508
- // Schedule workspace cleanup
509
- if (task.machineId) {
510
- const workspace = this.workspaceRepo.findByTaskId(taskId);
511
- if (workspace) {
512
- this.cleanupService
513
- .scheduleCleanup(taskId, task.machineId, workspace.path)
514
- .catch((err) => {
515
- this.logger.error(`Failed to schedule cleanup for task #${taskId}: ${err instanceof Error ? err.message : String(err)}`);
516
- });
517
- }
518
- }
519
- this.auditLogRepo.create({
520
- action: `task_${targetStatus.toLowerCase()}_from_worker`,
521
- resource: `task:${taskId}`,
522
- detail: `Task marked ${targetStatus} via worker progress frame`,
523
- });
524
- }
525
- return updated;
526
- }
527
- // ---------------------------------------------------------------------------
528
- // retryTask
529
- // ---------------------------------------------------------------------------
530
- async retryTask(taskId, retriedBy) {
531
- const task = this.taskRepo.findById(taskId);
532
- if (!task) {
533
- throw new common_1.NotFoundException(`Task #${taskId} not found`);
534
- }
535
- if (task.status !== protocol_1.TaskStatus.FAILED) {
536
- throw new common_1.BadRequestException(`Cannot retry task #${taskId}: status is ${task.status}, expected FAILED`);
537
- }
538
- // Validate transition
539
- state_machine_1.TaskStateMachine.assertTransition(task.status, protocol_1.TaskStatus.QUEUED);
540
- // Check fingerprint conflict — another active task with same fingerprint
541
- if (task.fingerprint) {
542
- const conflicting = this.taskRepo.findActiveByFingerprint(task.fingerprint);
543
- if (conflicting && conflicting.id !== taskId) {
544
- throw new common_1.ConflictException(`Cannot retry: active task #${conflicting.id} has the same fingerprint`);
545
- }
546
- }
547
- // Check for existing MR/PR on the branch (if branch exists)
548
- if (task.branch && task.configSnapshot) {
549
- const mrCheckResult = await this.checkExistingMergeRequest(task);
550
- if (mrCheckResult) {
551
- // Store pending confirm for retry with existing MR
552
- const confirmKey = `pending_confirm:retry:${crypto.randomUUID()}`;
553
- const pendingData = JSON.stringify({
554
- action: 'retry',
555
- taskId,
556
- createdBy: retriedBy,
557
- });
558
- await this.redis.setex(confirmKey, protocol_1.CONFIRM_PENDING_TTL_SEC, pendingData);
559
- // Store index from taskId to confirmKey for lookup during confirm
560
- await this.redis.setex(`pending_confirm_idx:task:${taskId}`, protocol_1.CONFIRM_PENDING_TTL_SEC, confirmKey);
561
- throw new common_1.ConflictException({
562
- message: `Task #${taskId} branch '${task.branch}' has an open MR/PR`,
563
- confirmRequired: {
564
- reason: 'existing_mr',
565
- message: `Task #${taskId} branch '${task.branch}' has an open MR/PR (${mrCheckResult}). Retry will force push to the same branch. Continue?`,
566
- pendingAction: 'retry',
567
- taskId,
568
- mrUrl: mrCheckResult,
569
- confirmKey,
570
- },
571
- });
572
- }
573
- }
574
- return this.executeRetry(taskId, retriedBy);
575
- }
576
- async executeRetry(taskId, retriedBy) {
577
- const task = this.taskRepo.findById(taskId);
578
- if (!task) {
579
- throw new common_1.NotFoundException(`Task #${taskId} not found`);
580
- }
581
- // CAS update: FAILED -> QUEUED
582
- const updated = this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.QUEUED, task.revision, {
583
- retryCount: task.retryCount + 1,
584
- errorMessage: null,
585
- machineId: null,
586
- assignedAt: null,
587
- startedAt: null,
588
- completedAt: null,
589
- currentStage: null,
590
- currentSessionId: null,
591
- });
592
- if (!updated) {
593
- throw new common_1.ConflictException(`Failed to retry task #${taskId}: concurrent modification`);
594
- }
595
- // Re-enqueue to BullMQ
596
- await this.schedulerService.enqueueTask(taskId);
597
214
  this.auditLogRepo.create({
598
- userId: retriedBy,
599
- action: 'task_retry',
600
- resource: `task:${taskId}`,
601
- detail: `Retried task #${taskId} (attempt ${task.retryCount + 1})`,
215
+ userId: command.user.id,
216
+ action: drain ? 'worker_drain' : 'worker_undrain',
217
+ resource: `worker:${worker.id}`,
218
+ detail: `Worker '${worker.name}' ${drain ? 'drained' : 'undrained'}`,
602
219
  });
603
- this.logger.log(`Task #${taskId} retried by developer #${retriedBy}`);
604
- return this.taskRepo.findById(taskId);
605
- }
606
- // ---------------------------------------------------------------------------
607
- // confirmStage — forward interactive stage confirm result to Worker
608
- // ---------------------------------------------------------------------------
609
- async confirmStage(taskId, stageIndex, result) {
610
- const task = this.taskRepo.findById(taskId);
611
- if (!task) {
612
- throw new common_1.NotFoundException(`Task #${taskId} not found`);
613
- }
614
- if (task.status !== protocol_1.TaskStatus.RUNNING &&
615
- task.status !== protocol_1.TaskStatus.SUSPENDED) {
616
- throw new common_1.BadRequestException(`Task #${taskId} is not in a confirmable state (${task.status})`);
617
- }
618
- if (!task.machineId) {
619
- throw new common_1.BadRequestException(`Task #${taskId} has no assigned machine`);
620
- }
621
- // Forward confirm response to Worker via WS
622
- const frame = {
623
- type: 'stage_confirm_response',
624
- taskId,
625
- stageIndex,
626
- result,
627
- };
628
- const ws = this.workerConnectionManager.getConnection(task.machineId);
629
- if (!ws || ws.readyState !== 1 /* WebSocket.OPEN */) {
630
- throw new common_1.BadRequestException(`Worker for task #${taskId} is not connected`);
631
- }
632
- ws.send(JSON.stringify(frame));
633
- // Set a short TTL on the stage confirm key instead of deleting immediately.
634
- // This gives a 60-second window for retry if the worker doesn't process the message,
635
- // while still ensuring eventual cleanup.
636
- const stageConfirmKey = `confirm:stage:${taskId}:${stageIndex}`;
637
- await this.redis.expire(stageConfirmKey, 60);
638
- this.logger.log(`Stage confirm forwarded to Worker for task #${taskId}, stage ${stageIndex}`);
220
+ return { success: true, message: `Worker '${worker.name}' ${drain ? 'drained' : 'undrained'}` };
639
221
  }
640
222
  // ---------------------------------------------------------------------------
641
- // getTask / listTasks
223
+ // Task query methods
642
224
  // ---------------------------------------------------------------------------
643
225
  getTask(taskId) {
644
226
  return this.taskRepo.findById(taskId);
@@ -652,477 +234,17 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
652
234
  listVisibleTasks(developerId, query) {
653
235
  return this.taskRepo.listVisible(developerId, query);
654
236
  }
655
- // ---------------------------------------------------------------------------
656
- // registerWorker
657
- // ---------------------------------------------------------------------------
658
- async registerWorker(request) {
659
- // 1. Parse token format: ovw_<id>_<secret>
660
- const { tokenId, secret } = this.parseWorkerToken(request.token);
661
- // 2. Find token by id, verify it's active and unused
662
- const token = this.workerTokenRepo.findActiveUnused(tokenId);
663
- if (!token) {
664
- throw new common_1.UnauthorizedException('Invalid or already used worker token');
665
- }
666
- // 3. Verify bcrypt(secret, hash)
667
- const secretValid = await this.cryptoService.comparePassword(secret, token.tokenHash);
668
- if (!secretValid) {
669
- throw new common_1.UnauthorizedException('Invalid worker token secret');
670
- }
671
- // 4. Validate protocol version (major must match)
672
- this.validateProtocolVersion(request.protocolVersion);
673
- // 5. Resolve machine: by explicit ID, by name, or create new
674
- let machineId = request.machineId;
675
- let machine = machineId ? this.machineRepo.findById(machineId) : null;
676
- let isFirstRegistration = !machineId;
677
- // If no explicit machineId, check if a machine with this name already exists
678
- if (!machine && !machineId) {
679
- const existingByName = this.machineRepo.findByName(request.machineName);
680
- if (existingByName) {
681
- if (existingByName.status === protocol_1.MachineStatus.DECOMMISSIONED) {
682
- // Archive the decommissioned machine by renaming it, then create fresh
683
- const archiveName = `${existingByName.name}_decom_${existingByName.id.slice(0, 8)}`;
684
- this.machineRepo.updateName(existingByName.id, archiveName);
685
- this.logger.log(`Archived decommissioned machine '${existingByName.name}' → '${archiveName}'`);
686
- }
687
- else {
688
- machine = existingByName;
689
- machineId = existingByName.id;
690
- isFirstRegistration = false;
691
- }
692
- }
693
- }
694
- if (!machineId) {
695
- machineId = crypto.randomUUID();
696
- }
697
- // Reject re-registration of decommissioned machines (by explicit machineId)
698
- if (machine && machine.status === protocol_1.MachineStatus.DECOMMISSIONED) {
699
- throw new common_1.ForbiddenException(`Machine '${machineId}' has been decommissioned and cannot re-register`);
700
- }
701
- // 6. All validations passed — now mark token as used and mutate state
702
- this.workerTokenRepo.markUsed(tokenId);
703
- if (machine) {
704
- // Update existing machine
705
- this.machineRepo.updateStatus(machineId, protocol_1.MachineStatus.ONLINE);
706
- this.machineRepo.updateTokenId(machineId, tokenId);
707
- }
708
- else {
709
- // Create new machine
710
- machine = this.machineRepo.create({
711
- id: machineId,
712
- name: request.machineName,
713
- host: request.host,
714
- port: request.port,
715
- tokenId,
716
- os: request.os,
717
- cpuModel: request.cpuModel,
718
- cpuCores: request.cpuCores,
719
- memoryGb: request.memoryGb,
720
- capabilities: request.capabilities,
721
- tags: request.tags,
722
- protocolVersion: request.protocolVersion,
723
- });
724
- }
725
- // 7. Generate recovery secret on first registration
726
- let recoverySecret;
727
- if (isFirstRegistration) {
728
- recoverySecret = crypto.randomBytes(32).toString('hex');
729
- const recoveryHash = await this.cryptoService.hashPassword(recoverySecret);
730
- this.machineRepo.updateRecoverySecretHash(machineId, recoveryHash);
731
- }
732
- // 8. Sign Worker JWT
733
- const jwt = this.authService.signWorkerJwt(machineId, tokenId);
734
- this.auditLogRepo.create({
735
- action: 'worker_register',
736
- resource: `machine:${machineId}`,
737
- detail: `Worker '${request.machineName}' registered (token #${tokenId}, first=${isFirstRegistration})`,
738
- });
739
- this.logger.log(`Worker '${request.machineName}' registered as ${machineId}`);
740
- return {
741
- jwt,
742
- machineId,
743
- recoverySecret,
744
- };
745
- }
746
- // ---------------------------------------------------------------------------
747
- // refreshWorkerToken
748
- // ---------------------------------------------------------------------------
749
- async refreshWorkerToken(request, currentJwt) {
750
- const { machineId } = request;
751
- // Verify tokenId matches the current JWT's tokenId
752
- const machine = this.machineRepo.findById(machineId);
753
- if (!machine) {
754
- throw new common_1.NotFoundException(`Machine '${machineId}' not found`);
755
- }
756
- if (machine.tokenId !== currentJwt.tokenId) {
757
- throw new common_1.UnauthorizedException('Token ID mismatch — token may have been revoked');
758
- }
759
- // Verify the token hasn't been revoked
760
- const token = this.workerTokenRepo.findById(currentJwt.tokenId);
761
- if (!token || token.status === 'revoked') {
762
- throw new common_1.UnauthorizedException('Worker token has been revoked');
763
- }
764
- // Sign new Worker JWT
765
- const jwt = this.authService.signWorkerJwt(machineId, currentJwt.tokenId);
766
- this.logger.debug(`Worker JWT refreshed for machine ${machineId}`);
767
- return { jwt };
768
- }
769
- // ---------------------------------------------------------------------------
770
- // recoverWorker
771
- // ---------------------------------------------------------------------------
772
- async recoverWorker(request) {
773
- const { machineId, machineName, recoverySecret } = request;
774
- // 1. Find machine
775
- const machine = this.machineRepo.findById(machineId);
776
- if (!machine) {
777
- throw new common_1.NotFoundException(`Machine '${machineId}' not found`);
778
- }
779
- // 2. Reject recovery of decommissioned machines
780
- if (machine.status === protocol_1.MachineStatus.DECOMMISSIONED) {
781
- throw new common_1.ForbiddenException(`Machine '${machineId}' has been decommissioned and cannot recover`);
782
- }
783
- // 3. Validate machine name matches
784
- if (machine.name !== machineName) {
785
- throw new common_1.UnauthorizedException('Machine name does not match the registered name');
786
- }
787
- // 3. Validate recovery secret hash exists
788
- if (!machine.recoverySecretHash) {
789
- throw new common_1.UnauthorizedException('No recovery secret configured for this machine');
790
- }
791
- // 4. Validate recovery secret
792
- const secretValid = await this.cryptoService.comparePassword(recoverySecret, machine.recoverySecretHash);
793
- if (!secretValid) {
794
- throw new common_1.UnauthorizedException('Invalid recovery secret');
795
- }
796
- // 5. Atomically claim the recovery token via Redis GETDEL to prevent
797
- // race conditions where two concurrent recovery requests consume
798
- // the same token.
799
- const recoveryClaimKey = `recovery_claim:${machineId}`;
800
- // Set the claim key if not already present (first recovery wins)
801
- const claimed = await this.redis.getClient().set(recoveryClaimKey, '1', 'EX', 30, 'NX');
802
- if (claimed === null) {
803
- throw new common_1.ConflictException('Recovery already in progress for this machine');
804
- }
805
- try {
806
- // 6. Wrap SQLite updates in a transaction for atomicity
807
- const db = this.databaseService.getDb();
808
- const runRecoveryTransaction = db.transaction(() => {
809
- // Update machine status to online
810
- db.prepare('UPDATE machines SET status = ? WHERE id = ?')
811
- .run(protocol_1.MachineStatus.ONLINE, machineId);
812
- // Record audit log
813
- db.prepare(`INSERT INTO audit_logs (user_id, action, resource, detail)
814
- VALUES (?, ?, ?, ?)`).run(null, 'worker_recover', `machine:${machineId}`, `Worker '${machineName}' recovered via recovery secret`);
815
- });
816
- runRecoveryTransaction();
817
- // 7. Issue new token — sign Worker JWT with existing tokenId
818
- const jwt = this.authService.signWorkerJwt(machineId, machine.tokenId);
819
- this.logger.log(`Worker '${machineName}' (${machineId}) recovered via recovery secret`);
820
- return { jwt, machineId };
821
- }
822
- finally {
823
- // Clean up the claim key
824
- await this.redis.del(recoveryClaimKey);
825
- }
826
- }
827
- // ---------------------------------------------------------------------------
828
- // issuePtyToken
829
- // ---------------------------------------------------------------------------
830
- issuePtyToken(taskId, aud) {
831
- const task = this.taskRepo.findById(taskId);
832
- if (!task) {
833
- throw new common_1.NotFoundException(`Task #${taskId} not found`);
834
- }
835
- return this.authService.signChannelToken(taskId, aud);
836
- }
837
- // ---------------------------------------------------------------------------
838
- // Helpers
839
- // ---------------------------------------------------------------------------
840
- resolveProject(keyOrAlias) {
841
- if (!keyOrAlias) {
842
- // Try default project
843
- return this.projectRepo.findDefault();
844
- }
845
- // Try by key first
846
- const byKey = this.projectRepo.findByKey(keyOrAlias);
847
- if (byKey)
848
- return byKey;
849
- // Try by alias
850
- return this.projectRepo.findByAlias(keyOrAlias);
851
- }
852
- parseWorkerToken(raw) {
853
- // Format: ovw_<id>_<secret>
854
- const match = /^ovw_(\d+)_(.+)$/.exec(raw);
855
- if (!match) {
856
- throw new common_1.UnauthorizedException('Invalid token format. Expected: ovw_<id>_<secret>');
857
- }
858
- return {
859
- tokenId: parseInt(match[1], 10),
860
- secret: match[2],
861
- };
862
- }
863
- validateProtocolVersion(workerVersion) {
864
- const [serverMajor] = protocol_1.PROTOCOL_VERSION.split('.');
865
- const [workerMajor] = workerVersion.split('.');
866
- if (serverMajor !== workerMajor) {
867
- throw new common_1.BadRequestException(`Protocol version mismatch: server=${protocol_1.PROTOCOL_VERSION}, worker=${workerVersion}. Major version must match.`);
868
- }
869
- if (workerVersion !== protocol_1.PROTOCOL_VERSION) {
870
- this.logger.warn(`Protocol minor version mismatch: server=${protocol_1.PROTOCOL_VERSION}, worker=${workerVersion}`);
871
- }
872
- }
873
- /**
874
- * Resolve a pending confirmation from Redis.
875
- * Tries reply-to msgId lookup first, then taskId-based index.
876
- * Validates that the confirming user matches the original creator.
877
- * Consumes (deletes) the pending confirm on success.
878
- */
879
- async resolvePendingConfirm(taskId, userId, _msgId) {
880
- // Look up the confirm key via taskId index (non-destructive read)
881
- const indexKey = `pending_confirm_idx:task:${taskId}`;
882
- const confirmKey = await this.redis.get(indexKey);
883
- if (!confirmKey)
884
- return null;
885
- // Atomically consume the confirm data (GETDEL prevents double-confirm race)
886
- const raw = await this.redis.getdel(confirmKey);
887
- if (!raw)
888
- return null;
889
- let data;
890
- try {
891
- data = JSON.parse(raw);
892
- }
893
- catch {
894
- return null;
895
- }
896
- // Validate user matches original creator.
897
- // If auth fails the confirm data is already consumed (intentional —
898
- // prevents an unauthorized user from probing indefinitely, and the
899
- // confirm would have expired via TTL anyway).
900
- if (data.createdBy !== userId) {
901
- this.logger.warn(`Confirm attempt by user #${userId} but pending confirm belongs to user #${data.createdBy}`);
902
- return null;
903
- }
904
- // Clean up the index key
905
- await this.redis.del(indexKey);
906
- return data;
907
- }
908
- /**
909
- * Update task status with CAS retry (up to CAS_MAX_RETRIES).
910
- * Returns true if successful, throws ConflictException on exhaustion.
911
- */
912
- updateTaskStatusWithRetry(taskId, status, initialRevision, extra) {
913
- let revision = initialRevision;
914
- for (let attempt = 0; attempt < protocol_1.CAS_MAX_RETRIES; attempt++) {
915
- const success = this.taskRepo.updateStatus(taskId, status, revision, extra);
916
- if (success)
917
- return true;
918
- // Reload task to get fresh revision
919
- const freshTask = this.taskRepo.findById(taskId);
920
- if (!freshTask) {
921
- throw new common_1.NotFoundException(`Task #${taskId} not found during CAS retry`);
922
- }
923
- // If task already reached target status, that's fine
924
- if (freshTask.status === status)
925
- return true;
926
- revision = freshTask.revision;
927
- }
928
- throw new common_1.ConflictException(`CAS retry exhausted for task #${taskId} after ${protocol_1.CAS_MAX_RETRIES} attempts`);
929
- }
930
- /**
931
- * Send cancel frame to Worker and wait for ack within CANCEL_ACK_TIMEOUT_MS.
932
- */
933
- async sendCancelToWorkerWithAck(machineId, taskId) {
934
- const msgId = crypto.randomUUID();
935
- try {
936
- await this.workerConnectionManager
937
- .sendWithAck(machineId, { type: 'cancel', msgId, taskId }, protocol_1.CANCEL_ACK_TIMEOUT_MS);
938
- return true;
939
- }
940
- catch {
941
- return false;
942
- }
943
- }
944
- /**
945
- * Send cancel frame to Worker (fire and forget).
946
- */
947
- sendCancelToWorker(machineId, taskId) {
948
- const msgId = crypto.randomUUID();
949
- this.workerConnectionManager.send(machineId, {
950
- type: 'cancel',
951
- msgId,
952
- taskId,
953
- });
954
- }
955
- /**
956
- * Check if the task's branch has an existing open MR/PR on the Git platform.
957
- * Returns the MR/PR URL if found, null otherwise.
958
- * On timeout or error, returns null (allows retry to proceed).
959
- *
960
- * Supports GitLab and GitHub APIs. Extracts the project path from the repo URL
961
- * and queries the platform API for open merge/pull requests on the branch.
962
- */
963
- async checkExistingMergeRequest(task) {
964
- if (!task.branch || !task.configSnapshot)
965
- return null;
966
- try {
967
- const config = JSON.parse(task.configSnapshot);
968
- const { repoUrl, gitPlatform } = config;
969
- if (!repoUrl)
970
- return null;
971
- // Extract the project path from the repo URL.
972
- const projectPath = this.extractProjectPath(repoUrl);
973
- if (!projectPath) {
974
- this.logger.warn(`Cannot extract project path from repo URL '${repoUrl}' for task #${task.id}`);
975
- return task.mrUrl ?? null;
976
- }
977
- // Extract the base URL for API calls
978
- const baseUrl = this.extractGitBaseUrl(repoUrl);
979
- if (!baseUrl) {
980
- this.logger.warn(`Cannot extract base URL from repo URL '${repoUrl}' for task #${task.id}`);
981
- return task.mrUrl ?? null;
982
- }
983
- // Look up the git token. Since the DB schema does not currently include
984
- // git_token_encrypted, use the GIT_TOKEN environment variable as fallback.
985
- const gitToken = process.env.GIT_TOKEN || '';
986
- if (!gitToken) {
987
- this.logger.debug(`No GIT_TOKEN configured; skipping MR/PR API check for task #${task.id}`);
988
- return task.mrUrl ?? null;
989
- }
990
- const abortController = new AbortController();
991
- const timeout = setTimeout(() => abortController.abort(), 10_000);
992
- try {
993
- let mrUrl = null;
994
- if (gitPlatform === 'gitlab') {
995
- mrUrl = await this.checkGitLabMergeRequest(baseUrl, projectPath, task.branch, gitToken, abortController.signal);
996
- }
997
- else if (gitPlatform === 'github') {
998
- mrUrl = await this.checkGitHubPullRequest(baseUrl, projectPath, task.branch, gitToken, abortController.signal);
999
- }
1000
- else {
1001
- this.logger.debug(`Unsupported git platform '${gitPlatform}' for MR/PR check on task #${task.id}`);
1002
- return task.mrUrl ?? null;
1003
- }
1004
- return mrUrl;
1005
- }
1006
- finally {
1007
- clearTimeout(timeout);
1008
- }
1009
- }
1010
- catch (err) {
1011
- this.logger.warn(`MR/PR check failed for task #${task.id}: ${err instanceof Error ? err.message : String(err)}`);
1012
- // On error, allow retry to proceed
1013
- return null;
1014
- }
1015
- }
1016
- /**
1017
- * Query GitLab API for open merge requests on a branch.
1018
- * GET /api/v4/projects/:id/merge_requests?source_branch=...&state=opened
1019
- */
1020
- async checkGitLabMergeRequest(baseUrl, projectPath, branch, token, signal) {
1021
- const encodedPath = encodeURIComponent(projectPath);
1022
- const url = `${baseUrl}/api/v4/projects/${encodedPath}/merge_requests?source_branch=${encodeURIComponent(branch)}&state=opened&per_page=1`;
1023
- const response = await fetch(url, {
1024
- headers: {
1025
- 'PRIVATE-TOKEN': token,
1026
- 'Accept': 'application/json',
1027
- },
1028
- signal,
1029
- });
1030
- if (!response.ok) {
1031
- this.logger.warn(`GitLab MR check returned ${response.status} for project '${projectPath}', branch '${branch}'`);
1032
- return null;
1033
- }
1034
- const data = (await response.json());
1035
- if (Array.isArray(data) && data.length > 0 && data[0].web_url) {
1036
- this.logger.debug(`Found open GitLab MR for branch '${branch}': ${data[0].web_url}`);
1037
- return data[0].web_url;
1038
- }
1039
- return null;
1040
- }
1041
- /**
1042
- * Query GitHub API for open pull requests on a branch.
1043
- * GET /repos/:owner/:repo/pulls?head=owner:branch&state=open
1044
- */
1045
- async checkGitHubPullRequest(baseUrl, projectPath, branch, token, signal) {
1046
- const owner = projectPath.split('/')[0];
1047
- const apiBase = baseUrl === 'https://github.com'
1048
- ? 'https://api.github.com'
1049
- : `${baseUrl}/api/v3`;
1050
- const url = `${apiBase}/repos/${projectPath}/pulls?head=${encodeURIComponent(`${owner}:${branch}`)}&state=open&per_page=1`;
1051
- const response = await fetch(url, {
1052
- headers: {
1053
- 'Authorization': `Bearer ${token}`,
1054
- 'Accept': 'application/vnd.github+json',
1055
- 'X-GitHub-Api-Version': '2022-11-28',
1056
- },
1057
- signal,
1058
- });
1059
- if (!response.ok) {
1060
- this.logger.warn(`GitHub PR check returned ${response.status} for project '${projectPath}', branch '${branch}'`);
1061
- return null;
1062
- }
1063
- const data = (await response.json());
1064
- if (Array.isArray(data) && data.length > 0 && data[0].html_url) {
1065
- this.logger.debug(`Found open GitHub PR for branch '${branch}': ${data[0].html_url}`);
1066
- return data[0].html_url;
1067
- }
1068
- return null;
1069
- }
1070
- /**
1071
- * Extract the project path (e.g. "group/project") from a git repo URL.
1072
- * Supports HTTPS and SSH formats.
1073
- */
1074
- extractProjectPath(repoUrl) {
1075
- // SSH format: git@host:path.git
1076
- const sshMatch = /^git@[^:]+:(.+?)(?:\.git)?$/.exec(repoUrl);
1077
- if (sshMatch)
1078
- return sshMatch[1];
1079
- // HTTPS format: https://host/path.git or https://host/path
1080
- try {
1081
- const parsed = new URL(repoUrl);
1082
- let path = parsed.pathname;
1083
- path = path.replace(/^\//, '').replace(/\.git$/, '');
1084
- return path || null;
1085
- }
1086
- catch {
1087
- return null;
1088
- }
1089
- }
1090
- /**
1091
- * Extract the base URL (scheme + host) from a git repo URL.
1092
- */
1093
- extractGitBaseUrl(repoUrl) {
1094
- // SSH format: git@host:path -> https://host
1095
- const sshMatch = /^git@([^:]+):/.exec(repoUrl);
1096
- if (sshMatch)
1097
- return `https://${sshMatch[1]}`;
1098
- // HTTPS format
1099
- try {
1100
- const parsed = new URL(repoUrl);
1101
- return `${parsed.protocol}//${parsed.host}`;
1102
- }
1103
- catch {
1104
- return null;
1105
- }
1106
- }
1107
237
  };
1108
238
  exports.DispatcherService = DispatcherService;
1109
239
  exports.DispatcherService = DispatcherService = DispatcherService_1 = __decorate([
1110
240
  (0, common_1.Injectable)(),
1111
241
  __metadata("design:paramtypes", [task_repository_1.TaskRepository,
1112
- machine_repository_1.MachineRepository,
1113
- project_repository_1.ProjectRepository,
1114
- project_member_repository_1.ProjectMemberRepository,
1115
- worker_token_repository_1.WorkerTokenRepository,
242
+ worker_repository_1.WorkerRepository,
1116
243
  audit_log_repository_1.AuditLogRepository,
1117
244
  developer_repository_1.DeveloperRepository,
1118
- database_service_1.DatabaseService,
1119
- auth_service_1.AuthService,
1120
- crypto_service_1.CryptoService,
1121
- redis_service_1.RedisService,
1122
245
  dedup_service_1.DedupService,
1123
- scheduler_service_1.SchedulerService,
1124
- worker_connection_manager_1.WorkerConnectionManager,
1125
- cleanup_service_1.CleanupService,
1126
- workspace_repository_1.WorkspaceRepository])
246
+ task_creation_service_1.TaskCreationService,
247
+ task_lifecycle_service_1.TaskLifecycleService,
248
+ event_emitter_1.EventEmitter2])
1127
249
  ], DispatcherService);
1128
250
  //# sourceMappingURL=dispatcher.service.js.map