crewly 1.20.133 → 1.20.135

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 (270) hide show
  1. package/config/skills/agent/core/block-task/SKILL.md +16 -3
  2. package/dist/backend/backend/src/constants.d.ts +82 -0
  3. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  4. package/dist/backend/backend/src/constants.js +87 -0
  5. package/dist/backend/backend/src/constants.js.map +1 -1
  6. package/dist/backend/backend/src/controllers/bundle/bundle.controller.d.ts +44 -0
  7. package/dist/backend/backend/src/controllers/bundle/bundle.controller.d.ts.map +1 -0
  8. package/dist/backend/backend/src/controllers/bundle/bundle.controller.js +122 -0
  9. package/dist/backend/backend/src/controllers/bundle/bundle.controller.js.map +1 -0
  10. package/dist/backend/backend/src/controllers/bundle/bundle.routes.d.ts +25 -0
  11. package/dist/backend/backend/src/controllers/bundle/bundle.routes.d.ts.map +1 -0
  12. package/dist/backend/backend/src/controllers/bundle/bundle.routes.js +33 -0
  13. package/dist/backend/backend/src/controllers/bundle/bundle.routes.js.map +1 -0
  14. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +6 -1
  15. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
  16. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +9 -2
  17. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  18. package/dist/backend/backend/src/index.d.ts.map +1 -1
  19. package/dist/backend/backend/src/index.js +28 -0
  20. package/dist/backend/backend/src/index.js.map +1 -1
  21. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  22. package/dist/backend/backend/src/routes/api.routes.js +4 -0
  23. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  24. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +43 -7
  25. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  26. package/dist/backend/backend/src/services/agent/agent-registration.service.js +144 -51
  27. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  28. package/dist/backend/backend/src/services/agent/registration-flow-registry.d.ts +92 -0
  29. package/dist/backend/backend/src/services/agent/registration-flow-registry.d.ts.map +1 -0
  30. package/dist/backend/backend/src/services/agent/registration-flow-registry.js +121 -0
  31. package/dist/backend/backend/src/services/agent/registration-flow-registry.js.map +1 -0
  32. package/dist/backend/backend/src/services/agent/resumed-kickoff.d.ts +66 -0
  33. package/dist/backend/backend/src/services/agent/resumed-kickoff.d.ts.map +1 -0
  34. package/dist/backend/backend/src/services/agent/resumed-kickoff.js +72 -0
  35. package/dist/backend/backend/src/services/agent/resumed-kickoff.js.map +1 -0
  36. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.d.ts +62 -0
  37. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.d.ts.map +1 -0
  38. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.js +173 -0
  39. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.js.map +1 -0
  40. package/dist/backend/backend/src/services/bundle/bundle-apply.service.d.ts +359 -0
  41. package/dist/backend/backend/src/services/bundle/bundle-apply.service.d.ts.map +1 -0
  42. package/dist/backend/backend/src/services/bundle/bundle-apply.service.js +888 -0
  43. package/dist/backend/backend/src/services/bundle/bundle-apply.service.js.map +1 -0
  44. package/dist/backend/backend/src/services/bundle/bundle-catalog.d.ts +167 -0
  45. package/dist/backend/backend/src/services/bundle/bundle-catalog.d.ts.map +1 -0
  46. package/dist/backend/backend/src/services/bundle/bundle-catalog.js +248 -0
  47. package/dist/backend/backend/src/services/bundle/bundle-catalog.js.map +1 -0
  48. package/dist/backend/backend/src/services/bundle/bundle-connectors.d.ts +43 -0
  49. package/dist/backend/backend/src/services/bundle/bundle-connectors.d.ts.map +1 -0
  50. package/dist/backend/backend/src/services/bundle/bundle-connectors.js +57 -0
  51. package/dist/backend/backend/src/services/bundle/bundle-connectors.js.map +1 -0
  52. package/dist/backend/backend/src/services/bundle/bundle-deps.d.ts +37 -0
  53. package/dist/backend/backend/src/services/bundle/bundle-deps.d.ts.map +1 -0
  54. package/dist/backend/backend/src/services/bundle/bundle-deps.js +60 -0
  55. package/dist/backend/backend/src/services/bundle/bundle-deps.js.map +1 -0
  56. package/dist/backend/backend/src/services/bundle/bundle-manifest.d.ts +93 -0
  57. package/dist/backend/backend/src/services/bundle/bundle-manifest.d.ts.map +1 -0
  58. package/dist/backend/backend/src/services/bundle/bundle-manifest.js +532 -0
  59. package/dist/backend/backend/src/services/bundle/bundle-manifest.js.map +1 -0
  60. package/dist/backend/backend/src/services/bundle/bundle-placeholders.d.ts +100 -0
  61. package/dist/backend/backend/src/services/bundle/bundle-placeholders.d.ts.map +1 -0
  62. package/dist/backend/backend/src/services/bundle/bundle-placeholders.js +183 -0
  63. package/dist/backend/backend/src/services/bundle/bundle-placeholders.js.map +1 -0
  64. package/dist/backend/backend/src/services/bundle/bundle-runtime.d.ts +36 -0
  65. package/dist/backend/backend/src/services/bundle/bundle-runtime.d.ts.map +1 -0
  66. package/dist/backend/backend/src/services/bundle/bundle-runtime.js +38 -0
  67. package/dist/backend/backend/src/services/bundle/bundle-runtime.js.map +1 -0
  68. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.d.ts +105 -0
  69. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.d.ts.map +1 -0
  70. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.js +144 -0
  71. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.js.map +1 -0
  72. package/dist/backend/backend/src/services/bundle/bundle-state.store.d.ts +48 -0
  73. package/dist/backend/backend/src/services/bundle/bundle-state.store.d.ts.map +1 -0
  74. package/dist/backend/backend/src/services/bundle/bundle-state.store.js +90 -0
  75. package/dist/backend/backend/src/services/bundle/bundle-state.store.js.map +1 -0
  76. package/dist/backend/backend/src/services/bundle/bundle-team.builder.d.ts +81 -0
  77. package/dist/backend/backend/src/services/bundle/bundle-team.builder.d.ts.map +1 -0
  78. package/dist/backend/backend/src/services/bundle/bundle-team.builder.js +172 -0
  79. package/dist/backend/backend/src/services/bundle/bundle-team.builder.js.map +1 -0
  80. package/dist/backend/backend/src/services/bundle/bundle-time.d.ts +59 -0
  81. package/dist/backend/backend/src/services/bundle/bundle-time.d.ts.map +1 -0
  82. package/dist/backend/backend/src/services/bundle/bundle-time.js +98 -0
  83. package/dist/backend/backend/src/services/bundle/bundle-time.js.map +1 -0
  84. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  85. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +5 -0
  86. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  87. package/dist/backend/backend/src/services/core/storage.service.js +1 -1
  88. package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
  89. package/dist/backend/backend/src/services/core/teams-backup.service.d.ts.map +1 -1
  90. package/dist/backend/backend/src/services/core/teams-backup.service.js +4 -2
  91. package/dist/backend/backend/src/services/core/teams-backup.service.js.map +1 -1
  92. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.d.ts.map +1 -1
  93. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js +2 -0
  94. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js.map +1 -1
  95. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts +14 -2
  96. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -1
  97. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js +37 -1
  98. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -1
  99. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
  100. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +9 -1
  101. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
  102. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  103. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +8 -0
  104. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  105. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +21 -0
  106. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  107. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +85 -0
  108. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  109. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +32 -0
  110. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  111. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +60 -1
  112. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  113. package/dist/backend/backend/src/services/template/template.service.d.ts.map +1 -1
  114. package/dist/backend/backend/src/services/template/template.service.js +5 -2
  115. package/dist/backend/backend/src/services/template/template.service.js.map +1 -1
  116. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
  117. package/dist/backend/backend/src/services/workflow/cron-task.service.js +4 -2
  118. package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
  119. package/dist/backend/backend/src/types/solution-bundle.types.d.ts +367 -0
  120. package/dist/backend/backend/src/types/solution-bundle.types.d.ts.map +1 -0
  121. package/dist/backend/backend/src/types/solution-bundle.types.js +17 -0
  122. package/dist/backend/backend/src/types/solution-bundle.types.js.map +1 -0
  123. package/dist/backend/backend/src/types/team-template.types.d.ts +8 -0
  124. package/dist/backend/backend/src/types/team-template.types.d.ts.map +1 -1
  125. package/dist/backend/backend/src/types/team-template.types.js.map +1 -1
  126. package/dist/backend/backend/src/types/v2/work-item.types.d.ts +37 -0
  127. package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  128. package/dist/backend/backend/src/types/v2/work-item.types.js +33 -0
  129. package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
  130. package/dist/backend/build-info.json +2 -2
  131. package/dist/cli/backend/src/constants.d.ts +82 -0
  132. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  133. package/dist/cli/backend/src/constants.js +87 -0
  134. package/dist/cli/backend/src/constants.js.map +1 -1
  135. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +43 -7
  136. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  137. package/dist/cli/backend/src/services/agent/agent-registration.service.js +144 -51
  138. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  139. package/dist/cli/backend/src/services/agent/registration-flow-registry.d.ts +92 -0
  140. package/dist/cli/backend/src/services/agent/registration-flow-registry.d.ts.map +1 -0
  141. package/dist/cli/backend/src/services/agent/registration-flow-registry.js +121 -0
  142. package/dist/cli/backend/src/services/agent/registration-flow-registry.js.map +1 -0
  143. package/dist/cli/backend/src/services/agent/resumed-kickoff.d.ts +66 -0
  144. package/dist/cli/backend/src/services/agent/resumed-kickoff.d.ts.map +1 -0
  145. package/dist/cli/backend/src/services/agent/resumed-kickoff.js +72 -0
  146. package/dist/cli/backend/src/services/agent/resumed-kickoff.js.map +1 -0
  147. package/dist/cli/backend/src/services/bundle/bundle-apply.service.d.ts +359 -0
  148. package/dist/cli/backend/src/services/bundle/bundle-apply.service.d.ts.map +1 -0
  149. package/dist/cli/backend/src/services/bundle/bundle-apply.service.js +888 -0
  150. package/dist/cli/backend/src/services/bundle/bundle-apply.service.js.map +1 -0
  151. package/dist/cli/backend/src/services/bundle/bundle-catalog.d.ts +167 -0
  152. package/dist/cli/backend/src/services/bundle/bundle-catalog.d.ts.map +1 -0
  153. package/dist/cli/backend/src/services/bundle/bundle-catalog.js +248 -0
  154. package/dist/cli/backend/src/services/bundle/bundle-catalog.js.map +1 -0
  155. package/dist/cli/backend/src/services/bundle/bundle-deps.d.ts +37 -0
  156. package/dist/cli/backend/src/services/bundle/bundle-deps.d.ts.map +1 -0
  157. package/dist/cli/backend/src/services/bundle/bundle-deps.js +60 -0
  158. package/dist/cli/backend/src/services/bundle/bundle-deps.js.map +1 -0
  159. package/dist/cli/backend/src/services/bundle/bundle-manifest.d.ts +93 -0
  160. package/dist/cli/backend/src/services/bundle/bundle-manifest.d.ts.map +1 -0
  161. package/dist/cli/backend/src/services/bundle/bundle-manifest.js +532 -0
  162. package/dist/cli/backend/src/services/bundle/bundle-manifest.js.map +1 -0
  163. package/dist/cli/backend/src/services/bundle/bundle-placeholders.d.ts +100 -0
  164. package/dist/cli/backend/src/services/bundle/bundle-placeholders.d.ts.map +1 -0
  165. package/dist/cli/backend/src/services/bundle/bundle-placeholders.js +183 -0
  166. package/dist/cli/backend/src/services/bundle/bundle-placeholders.js.map +1 -0
  167. package/dist/cli/backend/src/services/bundle/bundle-runtime.d.ts +36 -0
  168. package/dist/cli/backend/src/services/bundle/bundle-runtime.d.ts.map +1 -0
  169. package/dist/cli/backend/src/services/bundle/bundle-runtime.js +38 -0
  170. package/dist/cli/backend/src/services/bundle/bundle-runtime.js.map +1 -0
  171. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.d.ts +105 -0
  172. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.d.ts.map +1 -0
  173. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.js +144 -0
  174. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.js.map +1 -0
  175. package/dist/cli/backend/src/services/bundle/bundle-state.store.d.ts +48 -0
  176. package/dist/cli/backend/src/services/bundle/bundle-state.store.d.ts.map +1 -0
  177. package/dist/cli/backend/src/services/bundle/bundle-state.store.js +90 -0
  178. package/dist/cli/backend/src/services/bundle/bundle-state.store.js.map +1 -0
  179. package/dist/cli/backend/src/services/bundle/bundle-team.builder.d.ts +81 -0
  180. package/dist/cli/backend/src/services/bundle/bundle-team.builder.d.ts.map +1 -0
  181. package/dist/cli/backend/src/services/bundle/bundle-team.builder.js +172 -0
  182. package/dist/cli/backend/src/services/bundle/bundle-team.builder.js.map +1 -0
  183. package/dist/cli/backend/src/services/bundle/bundle-time.d.ts +59 -0
  184. package/dist/cli/backend/src/services/bundle/bundle-time.d.ts.map +1 -0
  185. package/dist/cli/backend/src/services/bundle/bundle-time.js +98 -0
  186. package/dist/cli/backend/src/services/bundle/bundle-time.js.map +1 -0
  187. package/dist/cli/backend/src/services/core/storage.service.js +1 -1
  188. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
  189. package/dist/cli/backend/src/services/core/teams-backup.service.d.ts.map +1 -1
  190. package/dist/cli/backend/src/services/core/teams-backup.service.js +4 -2
  191. package/dist/cli/backend/src/services/core/teams-backup.service.js.map +1 -1
  192. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.d.ts +72 -0
  193. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.d.ts.map +1 -0
  194. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.js +389 -0
  195. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.js.map +1 -0
  196. package/dist/cli/backend/src/services/messaging/index.d.ts +15 -0
  197. package/dist/cli/backend/src/services/messaging/index.d.ts.map +1 -0
  198. package/dist/cli/backend/src/services/messaging/index.js +20 -0
  199. package/dist/cli/backend/src/services/messaging/index.js.map +1 -0
  200. package/dist/cli/backend/src/services/messaging/queue-processor.service.d.ts +224 -0
  201. package/dist/cli/backend/src/services/messaging/queue-processor.service.d.ts.map +1 -0
  202. package/dist/cli/backend/src/services/messaging/queue-processor.service.js +969 -0
  203. package/dist/cli/backend/src/services/messaging/queue-processor.service.js.map +1 -0
  204. package/dist/cli/backend/src/services/messaging/response-router.service.d.ts +110 -0
  205. package/dist/cli/backend/src/services/messaging/response-router.service.d.ts.map +1 -0
  206. package/dist/cli/backend/src/services/messaging/response-router.service.js +268 -0
  207. package/dist/cli/backend/src/services/messaging/response-router.service.js.map +1 -0
  208. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts +14 -2
  209. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -1
  210. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js +37 -1
  211. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -1
  212. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.d.ts +133 -0
  213. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.d.ts.map +1 -0
  214. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.js +232 -0
  215. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.js.map +1 -0
  216. package/dist/cli/backend/src/services/skill/skill-catalog.service.d.ts +277 -0
  217. package/dist/cli/backend/src/services/skill/skill-catalog.service.d.ts.map +1 -0
  218. package/dist/cli/backend/src/services/skill/skill-catalog.service.js +708 -0
  219. package/dist/cli/backend/src/services/skill/skill-catalog.service.js.map +1 -0
  220. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.d.ts +243 -0
  221. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.d.ts.map +1 -0
  222. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.js +439 -0
  223. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.js.map +1 -0
  224. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +21 -0
  225. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  226. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +85 -0
  227. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  228. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +32 -0
  229. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  230. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +60 -1
  231. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  232. package/dist/cli/backend/src/services/template/template.service.d.ts.map +1 -1
  233. package/dist/cli/backend/src/services/template/template.service.js +5 -2
  234. package/dist/cli/backend/src/services/template/template.service.js.map +1 -1
  235. package/dist/cli/backend/src/services/workflow/cron-task.service.d.ts +316 -0
  236. package/dist/cli/backend/src/services/workflow/cron-task.service.d.ts.map +1 -0
  237. package/dist/cli/backend/src/services/workflow/cron-task.service.js +1043 -0
  238. package/dist/cli/backend/src/services/workflow/cron-task.service.js.map +1 -0
  239. package/dist/cli/backend/src/types/cron-task.types.d.ts +95 -0
  240. package/dist/cli/backend/src/types/cron-task.types.d.ts.map +1 -0
  241. package/dist/cli/backend/src/types/cron-task.types.js +10 -0
  242. package/dist/cli/backend/src/types/cron-task.types.js.map +1 -0
  243. package/dist/cli/backend/src/types/solution-bundle.types.d.ts +367 -0
  244. package/dist/cli/backend/src/types/solution-bundle.types.d.ts.map +1 -0
  245. package/dist/cli/backend/src/types/solution-bundle.types.js +17 -0
  246. package/dist/cli/backend/src/types/solution-bundle.types.js.map +1 -0
  247. package/dist/cli/backend/src/types/team-template.types.d.ts +8 -0
  248. package/dist/cli/backend/src/types/team-template.types.d.ts.map +1 -1
  249. package/dist/cli/backend/src/types/team-template.types.js.map +1 -1
  250. package/dist/cli/backend/src/types/v2/work-item.types.d.ts +37 -0
  251. package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  252. package/dist/cli/backend/src/types/v2/work-item.types.js +33 -0
  253. package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
  254. package/dist/cli/cli/src/commands/deploy-bundle.d.ts +160 -0
  255. package/dist/cli/cli/src/commands/deploy-bundle.d.ts.map +1 -0
  256. package/dist/cli/cli/src/commands/deploy-bundle.js +386 -0
  257. package/dist/cli/cli/src/commands/deploy-bundle.js.map +1 -0
  258. package/dist/cli/cli/src/commands/onboard.d.ts +32 -1
  259. package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
  260. package/dist/cli/cli/src/commands/onboard.js +80 -3
  261. package/dist/cli/cli/src/commands/onboard.js.map +1 -1
  262. package/dist/cli/cli/src/constants.d.ts +5 -0
  263. package/dist/cli/cli/src/constants.d.ts.map +1 -1
  264. package/dist/cli/cli/src/constants.js +5 -0
  265. package/dist/cli/cli/src/constants.js.map +1 -1
  266. package/dist/cli/cli/src/index.js +15 -0
  267. package/dist/cli/cli/src/index.js.map +1 -1
  268. package/frontend/dist/assets/{index-e6c403b6.js → index-2c7ee4a6.js} +276 -276
  269. package/frontend/dist/index.html +1 -1
  270. package/package.json +1 -1
@@ -0,0 +1,888 @@
1
+ /**
2
+ * Solution bundle apply engine.
3
+ *
4
+ * Deploys a bundle template in one step, with progress:
5
+ *
6
+ * 1. `team` — create the team(s) with filled prompts (StorageService)
7
+ * 2. `norms` — write norms, review points and SOPs where the team's
8
+ * norms and SOPs live (`teams/<id>/norms`, `teams/<id>/sops`)
9
+ * 3. `skills` — install missing skills (marketplace, behind an interface)
10
+ * 4. `connectors` — report services not yet connected, with /connections links
11
+ * 5. `slack` — team channels + extra channels (SlackTeamChannelService),
12
+ * only when Slack is connected; otherwise pending
13
+ * 6. `schedules` — recurring tasks (CronTaskService)
14
+ * 7. `first_week` — day-0 tasks go to the orchestrator now (the chat path the
15
+ * onboarding first task uses); later days are delivered by
16
+ * {@link BundleApplyService.deliverDue}
17
+ *
18
+ * Idempotent and resumable: the deployment is persisted after every step;
19
+ * a re-run skips steps that are done, and each step skips items it already
20
+ * did (existing teams are kept, sent tasks are not re-sent, schedules and
21
+ * channels are recorded). One failing step never stops the others, except
22
+ * that everything needs the team.
23
+ *
24
+ * All collaborators are injected (`bundle-apply.factory.ts` wires the
25
+ * backend's; the CLI wires an offline set), so the engine is testable with
26
+ * plain fakes and works with or without a running backend.
27
+ *
28
+ * @module services/bundle/bundle-apply.service
29
+ */
30
+ import { randomUUID } from 'crypto';
31
+ import { promises as fs } from 'fs';
32
+ import * as path from 'path';
33
+ import { BUNDLE_CONSTANTS, RUNTIME_TYPES } from '../../constants.js';
34
+ import { atomicWriteFile } from '../../utils/file-io.utils.js';
35
+ import { bundleTeams, parseMemberRef } from './bundle-manifest.js';
36
+ import { BundleAnswersError, fillPlaceholders, resolveAnswers, } from './bundle-placeholders.js';
37
+ import { buildBundleTeam, bundleTeamId, memberForRole, memberSession, teamPlaceholderValues } from './bundle-team.builder.js';
38
+ import { firstWeekDueAt, isValidTimezone } from './bundle-time.js';
39
+ /** A request the engine refuses before anything is written. */
40
+ export class BundleError extends Error {
41
+ code;
42
+ details;
43
+ /**
44
+ * @param code - Machine-readable code (mapped to an HTTP status by the controller)
45
+ * @param message - Owner-facing message
46
+ * @param details - Missing / invalid answers for `invalid_answers`
47
+ */
48
+ constructor(code, message, details) {
49
+ super(message);
50
+ this.code = code;
51
+ this.details = details;
52
+ this.name = 'BundleError';
53
+ }
54
+ }
55
+ // =============================================================================
56
+ // Helpers
57
+ // =============================================================================
58
+ /**
59
+ * Error text.
60
+ *
61
+ * @param error - Thrown value
62
+ * @returns Message
63
+ */
64
+ function errorText(error) {
65
+ return error instanceof Error ? error.message : String(error);
66
+ }
67
+ /**
68
+ * A fresh step list (all queued).
69
+ *
70
+ * @returns Steps in run order
71
+ */
72
+ export function initialSteps() {
73
+ return BUNDLE_CONSTANTS.STEP_ORDER.map((id) => ({
74
+ id,
75
+ label: BUNDLE_CONSTANTS.STEP_LABELS[id] ?? id,
76
+ status: 'queued',
77
+ }));
78
+ }
79
+ /**
80
+ * The overall status from the steps.
81
+ *
82
+ * @param steps - Steps
83
+ * @returns `done` when everything is done (or had nothing to do), `failed`
84
+ * when the team could not be created, else `partial`
85
+ */
86
+ export function overallStatus(steps) {
87
+ const team = steps.find((s) => s.id === BUNDLE_CONSTANTS.STEP_IDS.TEAM);
88
+ if (team?.status === 'failed')
89
+ return 'failed';
90
+ const finished = steps.every((s) => s.status === 'done' || (s.status === 'skipped' && s.reason === BUNDLE_CONSTANTS.SKIP_REASONS.NOTHING_TO_DO));
91
+ return finished ? 'done' : 'partial';
92
+ }
93
+ /**
94
+ * Markdown with YAML frontmatter, the shape get-team-norms / get-sops read.
95
+ *
96
+ * @param fields - Frontmatter fields (undefined values are dropped)
97
+ * @param body - Markdown body
98
+ * @returns File content
99
+ */
100
+ export function withFrontmatter(fields, body) {
101
+ const lines = Object.entries(fields)
102
+ .filter((e) => typeof e[1] === 'string' && e[1] !== '')
103
+ .map(([k, v]) => `${k}: ${v.replace(/\r?\n/g, ' ')}`);
104
+ return `---\n${lines.join('\n')}\n---\n\n${body.trim()}\n`;
105
+ }
106
+ /**
107
+ * The message a first-week task reaches the orchestrator as.
108
+ *
109
+ * @param task - Task state
110
+ * @param team - Team it is for
111
+ * @param memberName - Member it is for
112
+ * @param body - Filled task text
113
+ * @returns Message content
114
+ */
115
+ export function buildFirstWeekMessage(task, team, memberName, body) {
116
+ return [
117
+ `${BUNDLE_CONSTANTS.FIRST_WEEK_HEADER} 第 ${task.day + 1} 天 · ${task.title}`,
118
+ `请交给团队「${team.name}」(team id: ${team.id}) 的 ${memberName} 来做;团队还没启动的话先启动它。`,
119
+ '',
120
+ body.trim(),
121
+ ].join('\n');
122
+ }
123
+ // =============================================================================
124
+ // Service
125
+ // =============================================================================
126
+ /**
127
+ * The apply engine. See the module docs.
128
+ */
129
+ export class BundleApplyService {
130
+ deps;
131
+ /** Deployments being applied right now, by template id */
132
+ running = new Map();
133
+ /** Starts still validating / preparing, by template id (so a double tap joins them) */
134
+ starting = new Map();
135
+ /** Serialises writes per template */
136
+ writes = new Map();
137
+ /**
138
+ * @param deps - Collaborators
139
+ */
140
+ constructor(deps) {
141
+ this.deps = deps;
142
+ }
143
+ /**
144
+ * Validate a request and start applying it in the background.
145
+ *
146
+ * A second call while the same template is being applied returns that job
147
+ * (a double tap on a phone must not deploy twice). Answers left out reuse
148
+ * the ones stored by an earlier run.
149
+ *
150
+ * @param request - Template id, answers, runtime
151
+ * @returns The deployment snapshot with its `jobId`
152
+ * @throws BundleError `unknown_bundle` | `bundle_not_ready` | `invalid_runtime` | `invalid_answers`
153
+ */
154
+ async start(request) {
155
+ return (await this.begin(request)).deployment;
156
+ }
157
+ /**
158
+ * Apply and wait for the result (CLI, tests).
159
+ *
160
+ * @param request - Template id, answers, runtime
161
+ * @returns The finished deployment
162
+ * @throws BundleError like {@link start}
163
+ */
164
+ async applyAndWait(request) {
165
+ return (await this.begin(request)).done;
166
+ }
167
+ /**
168
+ * Progress of a job: the live deployment while it runs, else the stored one.
169
+ *
170
+ * @param jobId - Job id from {@link start}
171
+ * @returns The deployment
172
+ * @throws BundleError `job_not_found`
173
+ */
174
+ async getJob(jobId) {
175
+ for (const run of this.running.values()) {
176
+ if (run.deployment.jobId === jobId)
177
+ return run.deployment;
178
+ }
179
+ const stored = (await this.deps.store.list()).find((d) => d.jobId === jobId);
180
+ if (!stored)
181
+ throw new BundleError('job_not_found', `Job "${jobId}" not found`);
182
+ return stored;
183
+ }
184
+ /**
185
+ * The deployment of a template on this machine.
186
+ *
187
+ * @param templateId - Template id
188
+ * @returns The deployment, or null when it was never applied
189
+ */
190
+ async getDeployment(templateId) {
191
+ return this.running.get(templateId)?.deployment ?? (await this.deps.store.read(templateId));
192
+ }
193
+ /**
194
+ * Deliver first-week tasks that are due (the backend calls this on a timer).
195
+ *
196
+ * @returns Number of tasks sent
197
+ */
198
+ async deliverDue() {
199
+ const orchestrator = this.deps.orchestrator;
200
+ if (!orchestrator)
201
+ return 0;
202
+ let sent = 0;
203
+ for (const stored of await this.deps.store.list()) {
204
+ if (this.running.has(stored.templateId))
205
+ continue;
206
+ const now = this.deps.now();
207
+ const due = stored.firstWeek.filter((t) => t.status === 'scheduled' && Date.parse(t.dueAt) <= now.getTime());
208
+ if (due.length === 0)
209
+ continue;
210
+ const entry = this.deps.catalog.get(stored.templateId);
211
+ if (!entry)
212
+ continue;
213
+ const ctx = this.contextFor(entry, stored, stored.answers);
214
+ for (const task of due) {
215
+ if (await this.sendFirstWeekTask(ctx, task))
216
+ sent += 1;
217
+ }
218
+ const step = stored.steps.find((s) => s.id === BUNDLE_CONSTANTS.STEP_IDS.FIRST_WEEK);
219
+ if (step)
220
+ Object.assign(step, this.firstWeekOutcome(stored));
221
+ stored.status = overallStatus(stored.steps);
222
+ stored.updatedAt = this.deps.now().toISOString();
223
+ await this.persist(stored);
224
+ }
225
+ return sent;
226
+ }
227
+ /**
228
+ * Re-run deployments that were waiting on something that is now there:
229
+ * the backend (after a CLI deploy without one) or Slack.
230
+ *
231
+ * @returns Template ids that were restarted
232
+ */
233
+ async resumeWaiting() {
234
+ const restarted = [];
235
+ for (const stored of await this.deps.store.list()) {
236
+ if (stored.status === 'done' || this.running.has(stored.templateId))
237
+ continue;
238
+ const resumable = stored.steps.some((s) => s.status === 'pending' &&
239
+ (s.reason === BUNDLE_CONSTANTS.PENDING_REASONS.BACKEND_NOT_RUNNING ||
240
+ (s.reason === BUNDLE_CONSTANTS.PENDING_REASONS.SLACK_NOT_CONNECTED && !!this.deps.slack?.isConnected())));
241
+ if (!resumable || !this.deps.catalog.get(stored.templateId))
242
+ continue;
243
+ try {
244
+ await this.start({ templateId: stored.templateId, allowDraft: true });
245
+ restarted.push(stored.templateId);
246
+ }
247
+ catch {
248
+ // A template that no longer validates stays as it is.
249
+ }
250
+ }
251
+ return restarted;
252
+ }
253
+ // ---------------------------------------------------------------------------
254
+ // Run
255
+ // ---------------------------------------------------------------------------
256
+ /**
257
+ * Validate, prepare the deployment and start the run.
258
+ *
259
+ * @param request - Request
260
+ * @returns The deployment and a promise of the finished one
261
+ */
262
+ async begin(request) {
263
+ const inflight = this.running.get(request.templateId);
264
+ if (inflight)
265
+ return inflight;
266
+ const pending = this.starting.get(request.templateId);
267
+ if (pending)
268
+ return pending;
269
+ const prepared = this.prepare(request).finally(() => {
270
+ this.starting.delete(request.templateId);
271
+ });
272
+ this.starting.set(request.templateId, prepared);
273
+ return prepared;
274
+ }
275
+ /**
276
+ * Validate the request, build and persist the deployment, start the run.
277
+ *
278
+ * @param request - Request
279
+ * @returns The deployment and a promise of the finished one
280
+ * @throws BundleError for a refused request
281
+ */
282
+ async prepare(request) {
283
+ const entry = this.deps.catalog.get(request.templateId);
284
+ if (!entry)
285
+ throw new BundleError('unknown_bundle', `没有找到方案「${request.templateId}」`);
286
+ const { template } = entry;
287
+ const draft = (template.bundle.status ?? BUNDLE_CONSTANTS.STATUS.READY) !== BUNDLE_CONSTANTS.STATUS.READY;
288
+ if (draft && !request.allowDraft) {
289
+ throw new BundleError('bundle_not_ready', `方案「${template.bundle.label}」还在准备中,暂时不能部署`);
290
+ }
291
+ const runtimes = Object.values(RUNTIME_TYPES);
292
+ if (request.runtime !== undefined && !runtimes.includes(request.runtime)) {
293
+ throw new BundleError('invalid_runtime', `Unknown runtime "${request.runtime}" (use ${runtimes.join(', ')})`);
294
+ }
295
+ const previous = await this.deps.store.read(template.id);
296
+ const rawAnswers = request.answers ?? previous?.answers ?? {};
297
+ let answers;
298
+ try {
299
+ answers = resolveAnswers(template.bundle.questions ?? [], rawAnswers);
300
+ }
301
+ catch (error) {
302
+ if (error instanceof BundleAnswersError) {
303
+ throw new BundleError('invalid_answers', error.message, { missing: error.missing, invalid: error.invalid });
304
+ }
305
+ throw error;
306
+ }
307
+ const now = this.deps.now().toISOString();
308
+ const teamDone = previous?.steps.some((s) => s.id === BUNDLE_CONSTANTS.STEP_IDS.TEAM && s.status === 'done') ?? false;
309
+ const runtime = teamDone && previous
310
+ ? previous.runtime
311
+ : await this.deps.resolveRuntime(template.bundle.runtime.recommended, request.runtime);
312
+ const jobId = this.deps.newJobId?.() ?? `bundle-${randomUUID()}`;
313
+ const steps = initialSteps().map((fresh) => {
314
+ const old = previous?.steps.find((s) => s.id === fresh.id);
315
+ return old && old.status === 'done' ? old : fresh;
316
+ });
317
+ const deployment = {
318
+ templateId: template.id,
319
+ templateVersion: template.version ?? '0.0.0',
320
+ jobId,
321
+ status: 'running',
322
+ runtime,
323
+ answers: teamDone && previous ? previous.answers : answers,
324
+ startedAt: now,
325
+ updatedAt: now,
326
+ teams: previous?.teams ?? [],
327
+ steps,
328
+ connectors: previous?.connectors ?? [],
329
+ firstWeek: previous?.firstWeek ?? [],
330
+ schedules: previous?.schedules ?? {},
331
+ channels: previous?.channels ?? {},
332
+ };
333
+ await this.persist(deployment);
334
+ const ctx = this.contextFor(entry, deployment, deployment.answers);
335
+ const done = this.run(ctx).finally(() => {
336
+ this.running.delete(template.id);
337
+ });
338
+ const handle = { deployment, done };
339
+ this.running.set(template.id, handle);
340
+ return handle;
341
+ }
342
+ /**
343
+ * Shared context for a deployment.
344
+ *
345
+ * @param entry - Loaded bundle
346
+ * @param deployment - Deployment
347
+ * @param answers - Answers
348
+ * @returns Context
349
+ */
350
+ contextFor(entry, deployment, answers) {
351
+ return { entry, template: entry.template, teams: bundleTeams(entry.template), deployment, answers };
352
+ }
353
+ /**
354
+ * Run every step that is not done, persisting after each.
355
+ *
356
+ * @param ctx - Context
357
+ * @returns The finished deployment
358
+ */
359
+ async run(ctx) {
360
+ const { deployment } = ctx;
361
+ const bodies = {
362
+ [BUNDLE_CONSTANTS.STEP_IDS.TEAM]: (c) => this.stepTeam(c),
363
+ [BUNDLE_CONSTANTS.STEP_IDS.NORMS]: (c) => this.stepNorms(c),
364
+ [BUNDLE_CONSTANTS.STEP_IDS.SKILLS]: (c) => this.stepSkills(c),
365
+ [BUNDLE_CONSTANTS.STEP_IDS.CONNECTORS]: (c) => this.stepConnectors(c),
366
+ [BUNDLE_CONSTANTS.STEP_IDS.SLACK]: (c) => this.stepSlack(c),
367
+ [BUNDLE_CONSTANTS.STEP_IDS.SCHEDULES]: (c) => this.stepSchedules(c),
368
+ [BUNDLE_CONSTANTS.STEP_IDS.FIRST_WEEK]: (c) => this.stepFirstWeek(c),
369
+ };
370
+ const needsTeam = new Set([
371
+ BUNDLE_CONSTANTS.STEP_IDS.NORMS,
372
+ BUNDLE_CONSTANTS.STEP_IDS.SLACK,
373
+ BUNDLE_CONSTANTS.STEP_IDS.SCHEDULES,
374
+ BUNDLE_CONSTANTS.STEP_IDS.FIRST_WEEK,
375
+ ]);
376
+ for (const step of deployment.steps) {
377
+ if (step.status === 'done')
378
+ continue;
379
+ const teamStep = deployment.steps.find((s) => s.id === BUNDLE_CONSTANTS.STEP_IDS.TEAM);
380
+ if (needsTeam.has(step.id) && teamStep?.status !== 'done') {
381
+ this.finishStep(step, { status: 'skipped', reason: BUNDLE_CONSTANTS.SKIP_REASONS.TEAM_FAILED, message: '团队没建好,这一步先跳过' });
382
+ await this.touch(deployment);
383
+ continue;
384
+ }
385
+ step.status = 'running';
386
+ step.startedAt = this.deps.now().toISOString();
387
+ delete step.error;
388
+ delete step.reason;
389
+ await this.touch(deployment);
390
+ let outcome;
391
+ try {
392
+ outcome = await bodies[step.id](ctx);
393
+ }
394
+ catch (error) {
395
+ outcome = { status: 'failed', error: errorText(error), message: '这一步出错了,重新部署会从这里接着做' };
396
+ }
397
+ this.finishStep(step, outcome);
398
+ await this.touch(deployment);
399
+ }
400
+ deployment.status = overallStatus(deployment.steps);
401
+ deployment.finishedAt = this.deps.now().toISOString();
402
+ await this.touch(deployment);
403
+ return deployment;
404
+ }
405
+ /**
406
+ * Apply an outcome to a step.
407
+ *
408
+ * @param step - Step
409
+ * @param outcome - Outcome
410
+ */
411
+ finishStep(step, outcome) {
412
+ step.status = outcome.status;
413
+ step.finishedAt = this.deps.now().toISOString();
414
+ if (outcome.reason)
415
+ step.reason = outcome.reason;
416
+ else
417
+ delete step.reason;
418
+ if (outcome.message)
419
+ step.message = outcome.message;
420
+ else
421
+ delete step.message;
422
+ if (outcome.error)
423
+ step.error = outcome.error;
424
+ else
425
+ delete step.error;
426
+ if (outcome.items)
427
+ step.items = outcome.items;
428
+ }
429
+ /**
430
+ * Stamp and persist.
431
+ *
432
+ * @param deployment - Deployment
433
+ */
434
+ async touch(deployment) {
435
+ deployment.updatedAt = this.deps.now().toISOString();
436
+ await this.persist(deployment);
437
+ }
438
+ /**
439
+ * Persist a deployment; writes of one template never overlap.
440
+ *
441
+ * @param deployment - Deployment
442
+ */
443
+ async persist(deployment) {
444
+ const prev = this.writes.get(deployment.templateId) ?? Promise.resolve();
445
+ const snapshot = JSON.parse(JSON.stringify(deployment));
446
+ const next = prev.catch(() => undefined).then(() => this.deps.store.write(snapshot));
447
+ this.writes.set(deployment.templateId, next);
448
+ await next;
449
+ }
450
+ // ---------------------------------------------------------------------------
451
+ // Team lookups shared by the steps
452
+ // ---------------------------------------------------------------------------
453
+ /**
454
+ * The saved team for a bundle team key.
455
+ *
456
+ * @param ctx - Context
457
+ * @param key - Team key
458
+ * @returns The team
459
+ * @throws Error when it is missing (deleted since the team step)
460
+ */
461
+ async savedTeam(ctx, key) {
462
+ const id = ctx.deployment.teams.find((t) => t.key === key)?.teamId ?? bundleTeamId(ctx.template.id, key);
463
+ const team = await this.deps.teams.get(id);
464
+ if (!team)
465
+ throw new Error(`Team "${id}" no longer exists; delete ${this.deps.store.fileFor(ctx.template.id)} to deploy again`);
466
+ return team;
467
+ }
468
+ /**
469
+ * Placeholder values of a saved team.
470
+ *
471
+ * @param ctx - Context
472
+ * @param team - Saved team
473
+ * @returns Values (answers + team_name + lead_name)
474
+ */
475
+ valuesFor(ctx, team) {
476
+ const lead = team.members.find((m) => m.id === team.leaderId) ?? team.members[0];
477
+ return teamPlaceholderValues(ctx.answers, team.name, lead?.name ?? '');
478
+ }
479
+ /**
480
+ * Resolve a member ref (default: the main team's lead).
481
+ *
482
+ * @param ctx - Context
483
+ * @param ref - `role` / `team/role`, or undefined
484
+ * @returns Team and member session / name
485
+ */
486
+ async resolveRef(ctx, ref) {
487
+ const main = ctx.teams[0];
488
+ const parsed = ref ? parseMemberRef(ref) : { teamKey: main.key, role: main.leadRole };
489
+ const team = await this.savedTeam(ctx, parsed.teamKey);
490
+ const member = memberForRole(team, parsed.role);
491
+ if (!member)
492
+ throw new Error(`No member with role "${parsed.role}" in team "${team.name}"`);
493
+ return { team, session: memberSession(member), memberName: member.name };
494
+ }
495
+ // ---------------------------------------------------------------------------
496
+ // Steps
497
+ // ---------------------------------------------------------------------------
498
+ /** Step `team`: create every team that does not exist yet. */
499
+ async stepTeam(ctx) {
500
+ const items = [];
501
+ const deployed = [];
502
+ for (const spec of ctx.teams) {
503
+ const id = bundleTeamId(ctx.template.id, spec.key);
504
+ const existing = await this.deps.teams.get(id);
505
+ if (existing) {
506
+ deployed.push({ key: spec.key, teamId: id, name: existing.name });
507
+ items.push({ id: spec.key, label: existing.name, status: 'done', message: '已存在,保留原样' });
508
+ continue;
509
+ }
510
+ const team = buildBundleTeam({ template: ctx.template, team: spec, answers: ctx.answers, runtime: ctx.deployment.runtime, now: this.deps.now() });
511
+ await this.deps.teams.save(team);
512
+ deployed.push({ key: spec.key, teamId: team.id, name: team.name });
513
+ items.push({ id: spec.key, label: team.name, status: 'done', message: `${team.members.length} 位成员` });
514
+ }
515
+ ctx.deployment.teams = deployed;
516
+ const names = deployed.map((t) => `「${t.name}」`).join('、');
517
+ return { status: 'done', items, message: `团队 ${names} 已就绪` };
518
+ }
519
+ /** Step `norms`: norms, review points and SOPs into the teams' folders. */
520
+ async stepNorms(ctx) {
521
+ const b = ctx.template.bundle;
522
+ const norms = b.norms ?? [];
523
+ const sops = b.sops ?? [];
524
+ const reviews = b.reviewPoints ?? [];
525
+ if (norms.length + sops.length + reviews.length === 0) {
526
+ return { status: 'skipped', reason: BUNDLE_CONSTANTS.SKIP_REASONS.NOTHING_TO_DO, message: '这个方案没有额外规范' };
527
+ }
528
+ const items = [];
529
+ const main = BUNDLE_CONSTANTS.MAIN_TEAM_KEY;
530
+ const updatedBy = `bundle:${ctx.template.id}`;
531
+ const updatedAt = this.deps.now().toISOString();
532
+ for (const spec of ctx.teams) {
533
+ const team = await this.savedTeam(ctx, spec.key);
534
+ const values = this.valuesFor(ctx, team);
535
+ const teamDir = path.join(this.deps.crewlyHome, 'teams', team.id);
536
+ for (const norm of norms.filter((n) => (n.team ?? main) === spec.key)) {
537
+ const body = fillPlaceholders(await this.docBody(ctx, norm), values, `norm ${norm.id}`);
538
+ const file = path.join(teamDir, BUNDLE_CONSTANTS.NORMS_DIR, `${norm.id}.md`);
539
+ await this.writeDoc(file, withFrontmatter({ title: fillPlaceholders(norm.title, values, `norm ${norm.id} title`), trigger: norm.trigger, updatedBy, updatedAt }, body));
540
+ items.push({ id: `norm:${spec.key}/${norm.id}`, label: norm.title, status: 'done' });
541
+ }
542
+ const teamReviews = reviews.filter((r) => (r.team ?? main) === spec.key);
543
+ if (teamReviews.length > 0) {
544
+ const rows = teamReviews.map((r) => {
545
+ const who = r.approver === 'owner' ? '老板' : `负责人 ${values.lead_name}`;
546
+ const how = r.how ? `:${fillPlaceholders(r.how, values, `review ${r.id}`)}` : '';
547
+ return `- **${fillPlaceholders(r.what, values, `review ${r.id}`)}** → 先拿到${who}的同意${how}`;
548
+ });
549
+ const body = [
550
+ '这些事必须先拿到同意才能做。没得到明确的「可以」之前,只准备、不执行。',
551
+ '',
552
+ ...rows,
553
+ '',
554
+ '拿不准算不算的时候,按「需要同意」处理。',
555
+ ].join('\n');
556
+ const file = path.join(teamDir, BUNDLE_CONSTANTS.NORMS_DIR, `${BUNDLE_CONSTANTS.REVIEW_POINTS_NORM_ID}.md`);
557
+ await this.writeDoc(file, withFrontmatter({ title: '需要老板点头的事', trigger: 'before_publish,approval,owner_review,external', updatedBy, updatedAt }, body));
558
+ items.push({ id: `norm:${spec.key}/${BUNDLE_CONSTANTS.REVIEW_POINTS_NORM_ID}`, label: '需要老板点头的事', status: 'done' });
559
+ }
560
+ for (const sop of sops.filter((s) => (s.team ?? main) === spec.key)) {
561
+ const category = sop.category ?? BUNDLE_CONSTANTS.DEFAULT_SOP_CATEGORY;
562
+ const body = fillPlaceholders(await this.docBody(ctx, sop), values, `sop ${sop.id}`);
563
+ const file = path.join(teamDir, BUNDLE_CONSTANTS.SOPS_DIR, category, `${sop.id}.md`);
564
+ await this.writeDoc(file, withFrontmatter({ title: fillPlaceholders(sop.title, values, `sop ${sop.id} title`), category, updatedBy, updatedAt }, body));
565
+ items.push({ id: `sop:${spec.key}/${sop.id}`, label: sop.title, status: 'done' });
566
+ }
567
+ }
568
+ return { status: 'done', items, message: `写好 ${items.length} 份规范和 SOP` };
569
+ }
570
+ /**
571
+ * The body of a norm / SOP: inline content or the referenced file.
572
+ *
573
+ * @param ctx - Context
574
+ * @param doc - Norm or SOP
575
+ * @returns Markdown body (unfilled)
576
+ */
577
+ async docBody(ctx, doc) {
578
+ if (doc.content !== undefined)
579
+ return doc.content;
580
+ return fs.readFile(path.join(ctx.entry.dir, doc.file), 'utf-8');
581
+ }
582
+ /**
583
+ * Write a file atomically, creating its folder.
584
+ *
585
+ * @param file - Absolute path
586
+ * @param content - Content
587
+ */
588
+ async writeDoc(file, content) {
589
+ await fs.mkdir(path.dirname(file), { recursive: true });
590
+ await atomicWriteFile(file, content);
591
+ }
592
+ /** Step `skills`: install what is missing. */
593
+ async stepSkills(ctx) {
594
+ const required = ctx.template.bundle.skills?.required ?? [];
595
+ const optional = ctx.template.bundle.skills?.optional ?? [];
596
+ if (required.length + optional.length === 0) {
597
+ return { status: 'skipped', reason: BUNDLE_CONSTANTS.SKIP_REASONS.NOTHING_TO_DO, message: '不需要额外技能' };
598
+ }
599
+ const items = [];
600
+ let requiredFailed = 0;
601
+ for (const [skillId, isRequired] of [...required.map((s) => [s, true]), ...optional.map((s) => [s, false])]) {
602
+ try {
603
+ if (await this.deps.skills.isAvailable(skillId)) {
604
+ items.push({ id: skillId, label: skillId, status: 'done', message: '已有' });
605
+ continue;
606
+ }
607
+ const result = await this.deps.skills.install(skillId);
608
+ if (result.ok)
609
+ items.push({ id: skillId, label: skillId, status: 'done', message: result.message });
610
+ else {
611
+ items.push({ id: skillId, label: skillId, status: 'failed', message: result.message });
612
+ if (isRequired)
613
+ requiredFailed += 1;
614
+ }
615
+ }
616
+ catch (error) {
617
+ items.push({ id: skillId, label: skillId, status: 'failed', message: errorText(error) });
618
+ if (isRequired)
619
+ requiredFailed += 1;
620
+ }
621
+ }
622
+ if (requiredFailed > 0) {
623
+ return { status: 'failed', items, error: `${requiredFailed} 个必需技能没装上`, message: '有技能没装上,重新部署会再试' };
624
+ }
625
+ const optionalFailed = items.filter((i) => i.status === 'failed').length;
626
+ return { status: 'done', items, message: optionalFailed > 0 ? `技能已就绪(${optionalFailed} 个可选技能没装上)` : '技能已就绪' };
627
+ }
628
+ /** Step `connectors`: which services still need connecting. */
629
+ async stepConnectors(ctx) {
630
+ const specs = ctx.template.bundle.connectors ?? [];
631
+ if (specs.length === 0) {
632
+ return { status: 'skipped', reason: BUNDLE_CONSTANTS.SKIP_REASONS.NOTHING_TO_DO, message: '不需要接别的服务' };
633
+ }
634
+ const states = [];
635
+ for (const spec of specs) {
636
+ let status = 'unknown';
637
+ if (this.deps.connectors) {
638
+ try {
639
+ status = await this.deps.connectors.check(spec);
640
+ }
641
+ catch {
642
+ status = 'unknown';
643
+ }
644
+ }
645
+ states.push({
646
+ id: spec.id,
647
+ products: spec.products ?? [],
648
+ required: spec.required,
649
+ why: spec.why,
650
+ status,
651
+ connectPath: `${BUNDLE_CONSTANTS.CONNECTIONS_PATH}?platform=${encodeURIComponent(spec.id)}`,
652
+ });
653
+ }
654
+ ctx.deployment.connectors = states;
655
+ const items = states.map((s) => ({
656
+ id: s.id,
657
+ label: s.products.length > 0 ? `${s.id} (${s.products.join(', ')})` : s.id,
658
+ status: s.status === 'connected' ? 'done' : 'pending',
659
+ message: s.status === 'connected' ? '已连接' : `${s.required ? '需要连接' : '建议连接'}:${s.why} → ${s.connectPath}`,
660
+ }));
661
+ if (!this.deps.connectors) {
662
+ return { status: 'pending', reason: BUNDLE_CONSTANTS.PENDING_REASONS.BACKEND_NOT_RUNNING, items, message: 'Crewly 启动后再检查;先在 /connections 连好这些服务' };
663
+ }
664
+ const missing = states.filter((s) => s.required && s.status !== 'connected');
665
+ if (missing.length > 0) {
666
+ return {
667
+ status: 'pending',
668
+ reason: BUNDLE_CONSTANTS.PENDING_REASONS.CONNECTORS_MISSING,
669
+ items,
670
+ message: `还要连接:${missing.map((m) => m.id).join('、')}(在「连接」页面,手机上也能点)`,
671
+ };
672
+ }
673
+ return { status: 'done', items, message: '要用的服务都连好了' };
674
+ }
675
+ /** Step `slack`: team channels and extra channels. */
676
+ async stepSlack(ctx) {
677
+ const layout = ctx.template.bundle.slack ?? {};
678
+ const teamChannels = layout.teamChannels !== false;
679
+ const extra = layout.channels ?? [];
680
+ if (!teamChannels && extra.length === 0) {
681
+ return { status: 'skipped', reason: BUNDLE_CONSTANTS.SKIP_REASONS.NOTHING_TO_DO, message: '这个方案不用 Slack 频道' };
682
+ }
683
+ const slack = this.deps.slack;
684
+ if (!slack) {
685
+ return { status: 'pending', reason: BUNDLE_CONSTANTS.PENDING_REASONS.BACKEND_NOT_RUNNING, message: 'Crewly 启动并连上 Slack 后自动建频道' };
686
+ }
687
+ if (!slack.isConnected()) {
688
+ return { status: 'pending', reason: BUNDLE_CONSTANTS.PENDING_REASONS.SLACK_NOT_CONNECTED, message: '还没连 Slack;连上后会自动建频道、拉成员进来' };
689
+ }
690
+ const items = [];
691
+ let failed = 0;
692
+ const allTeams = [];
693
+ for (const spec of ctx.teams)
694
+ allTeams.push(await this.savedTeam(ctx, spec.key));
695
+ if (teamChannels) {
696
+ for (const team of allTeams) {
697
+ try {
698
+ const ch = await slack.ensureTeamChannel(team);
699
+ items.push({ id: `team:${team.id}`, label: `#${ch.slackChannelName}`, status: 'done', message: `团队「${team.name}」的频道` });
700
+ }
701
+ catch (error) {
702
+ failed += 1;
703
+ items.push({ id: `team:${team.id}`, label: team.name, status: 'failed', message: errorText(error) });
704
+ }
705
+ }
706
+ }
707
+ const mainValues = this.valuesFor(ctx, allTeams[0]);
708
+ for (const channel of extra) {
709
+ try {
710
+ const sessions = new Set();
711
+ for (const ref of channel.members) {
712
+ if (ref === '*') {
713
+ allTeams.forEach((t) => t.members.forEach((m) => sessions.add(memberSession(m))));
714
+ }
715
+ else {
716
+ sessions.add((await this.resolveRef(ctx, ref)).session);
717
+ }
718
+ }
719
+ const ch = await slack.ensureAgentChannel({
720
+ name: fillPlaceholders(channel.name, mainValues, `channel ${channel.key}`),
721
+ purpose: channel.purpose ? fillPlaceholders(channel.purpose, mainValues, `channel ${channel.key} purpose`) : '',
722
+ memberSessions: [...sessions],
723
+ ...(ctx.deployment.channels[channel.key] ? { existingChannelId: ctx.deployment.channels[channel.key] } : {}),
724
+ });
725
+ ctx.deployment.channels[channel.key] = ch.slackChannelId;
726
+ items.push({ id: `channel:${channel.key}`, label: `#${ch.slackChannelName}`, status: 'done', message: `${sessions.size} 位成员` });
727
+ }
728
+ catch (error) {
729
+ failed += 1;
730
+ items.push({ id: `channel:${channel.key}`, label: channel.name, status: 'failed', message: errorText(error) });
731
+ }
732
+ }
733
+ if (failed > 0)
734
+ return { status: 'failed', items, error: `${failed} 个频道没建好`, message: '有频道没建好,重新部署会再试' };
735
+ return { status: 'done', items, message: `建好 ${items.length} 个 Slack 频道` };
736
+ }
737
+ /** Step `schedules`: recurring tasks through the cron-task scheduler. */
738
+ async stepSchedules(ctx) {
739
+ const schedules = ctx.template.bundle.schedules ?? [];
740
+ if (schedules.length === 0) {
741
+ return { status: 'skipped', reason: BUNDLE_CONSTANTS.SKIP_REASONS.NOTHING_TO_DO, message: '这个方案没有定时任务' };
742
+ }
743
+ const api = this.deps.schedules;
744
+ if (!api) {
745
+ return { status: 'pending', reason: BUNDLE_CONSTANTS.PENDING_REASONS.BACKEND_NOT_RUNNING, message: 'Crewly 启动后自动排定时任务' };
746
+ }
747
+ const items = [];
748
+ let failed = 0;
749
+ for (const schedule of schedules) {
750
+ if (ctx.deployment.schedules[schedule.id]) {
751
+ items.push({ id: schedule.id, label: schedule.title, status: 'done', message: '已排好' });
752
+ continue;
753
+ }
754
+ try {
755
+ const { team, session, memberName } = await this.resolveRef(ctx, schedule.target);
756
+ const values = this.valuesFor(ctx, team);
757
+ const timezone = this.timezoneFor(ctx, values, schedule.timezone);
758
+ const task = await api.create({
759
+ cronExpression: schedule.cron,
760
+ timezone,
761
+ targetAgent: session,
762
+ targetTeamId: team.id,
763
+ taskDescription: `【${schedule.title}】\n${fillPlaceholders(schedule.task, values, `schedule ${schedule.id}`)}`,
764
+ createdBy: 'user',
765
+ });
766
+ ctx.deployment.schedules[schedule.id] = task.id;
767
+ items.push({ id: schedule.id, label: schedule.title, status: 'done', message: `${schedule.cron}(${timezone})→ ${memberName}` });
768
+ }
769
+ catch (error) {
770
+ failed += 1;
771
+ items.push({ id: schedule.id, label: schedule.title, status: 'failed', message: errorText(error) });
772
+ }
773
+ }
774
+ if (failed > 0)
775
+ return { status: 'failed', items, error: `${failed} 个定时任务没排上`, message: '有定时任务没排上,重新部署会再试' };
776
+ return { status: 'done', items, message: `排好 ${items.length} 个定时任务` };
777
+ }
778
+ /**
779
+ * A valid timezone from a (placeholder) value, the bundle's, or the default.
780
+ *
781
+ * @param ctx - Context
782
+ * @param values - Placeholder values
783
+ * @param override - Schedule's own timezone
784
+ * @returns IANA timezone
785
+ * @throws Error when the filled timezone is not valid
786
+ */
787
+ timezoneFor(ctx, values, override) {
788
+ const raw = override ?? ctx.template.bundle.timezone ?? BUNDLE_CONSTANTS.DEFAULT_TIMEZONE;
789
+ const tz = fillPlaceholders(raw, values, 'timezone').trim() || BUNDLE_CONSTANTS.DEFAULT_TIMEZONE;
790
+ if (!isValidTimezone(tz))
791
+ throw new Error(`Unknown timezone "${tz}"`);
792
+ return tz;
793
+ }
794
+ /** Step `first_week`: send today's tasks, schedule the rest. */
795
+ async stepFirstWeek(ctx) {
796
+ const tasks = ctx.template.bundle.firstWeek ?? [];
797
+ if (tasks.length === 0) {
798
+ return { status: 'skipped', reason: BUNDLE_CONSTANTS.SKIP_REASONS.NOTHING_TO_DO, message: '这个方案没有第一周任务' };
799
+ }
800
+ const deployedAt = new Date(ctx.deployment.startedAt);
801
+ for (const task of tasks) {
802
+ if (ctx.deployment.firstWeek.some((t) => t.id === task.id))
803
+ continue;
804
+ const { team, session } = await this.resolveRef(ctx, task.target);
805
+ const tz = this.timezoneFor(ctx, this.valuesFor(ctx, team));
806
+ ctx.deployment.firstWeek.push({
807
+ id: task.id,
808
+ title: task.title,
809
+ day: task.day,
810
+ dueAt: firstWeekDueAt(deployedAt, task.day, task.time, tz),
811
+ teamId: team.id,
812
+ target: session,
813
+ status: 'scheduled',
814
+ });
815
+ }
816
+ const now = this.deps.now().getTime();
817
+ for (const state of ctx.deployment.firstWeek) {
818
+ if ((state.status === 'scheduled' || state.status === 'failed') && Date.parse(state.dueAt) <= now && this.deps.orchestrator) {
819
+ await this.sendFirstWeekTask(ctx, state);
820
+ }
821
+ }
822
+ return this.firstWeekOutcome(ctx.deployment);
823
+ }
824
+ /**
825
+ * Send one first-week task through the orchestrator and record the result.
826
+ *
827
+ * @param ctx - Context
828
+ * @param state - Task state (updated in place)
829
+ * @returns True when it was handed over
830
+ */
831
+ async sendFirstWeekTask(ctx, state) {
832
+ const orchestrator = this.deps.orchestrator;
833
+ const spec = ctx.template.bundle.firstWeek?.find((t) => t.id === state.id);
834
+ if (!orchestrator || !spec)
835
+ return false;
836
+ try {
837
+ const team = await this.deps.teams.get(state.teamId);
838
+ if (!team)
839
+ throw new Error(`Team "${state.teamId}" no longer exists`);
840
+ const member = team.members.find((m) => memberSession(m) === state.target);
841
+ const body = fillPlaceholders(spec.task, this.valuesFor(ctx, team), `first-week ${spec.id}`);
842
+ const result = await orchestrator.send(buildFirstWeekMessage(state, team, member?.name ?? state.target, body), {
843
+ source: BUNDLE_CONSTANTS.FIRST_WEEK_SOURCE,
844
+ templateId: ctx.template.id,
845
+ teamId: team.id,
846
+ taskId: state.id,
847
+ });
848
+ if (!result.forwarded)
849
+ throw new Error(result.error ?? 'The orchestrator did not take it');
850
+ state.status = 'sent';
851
+ state.sentAt = this.deps.now().toISOString();
852
+ state.conversationId = result.conversationId;
853
+ delete state.error;
854
+ return true;
855
+ }
856
+ catch (error) {
857
+ state.status = 'failed';
858
+ state.error = errorText(error);
859
+ return false;
860
+ }
861
+ }
862
+ /**
863
+ * The first-week step's state from its tasks.
864
+ *
865
+ * @param deployment - Deployment
866
+ * @returns Outcome
867
+ */
868
+ firstWeekOutcome(deployment) {
869
+ const items = deployment.firstWeek.map((t) => ({
870
+ id: t.id,
871
+ label: `第 ${t.day + 1} 天 · ${t.title}`,
872
+ status: t.status === 'sent' ? 'done' : t.status === 'failed' ? 'failed' : 'pending',
873
+ message: t.status === 'sent' ? '已交给团队' : t.status === 'failed' ? t.error : `排在 ${t.dueAt}`,
874
+ }));
875
+ const failed = deployment.firstWeek.filter((t) => t.status === 'failed').length;
876
+ const now = this.deps.now().getTime();
877
+ const overdue = deployment.firstWeek.filter((t) => t.status === 'scheduled' && Date.parse(t.dueAt) <= now).length;
878
+ const sent = deployment.firstWeek.filter((t) => t.status === 'sent').length;
879
+ const later = deployment.firstWeek.length - sent - failed - overdue;
880
+ if (failed > 0)
881
+ return { status: 'failed', items, error: `${failed} 件没交出去`, message: '有任务没交给团队,重新部署会再试' };
882
+ if (overdue > 0) {
883
+ return { status: 'pending', reason: BUNDLE_CONSTANTS.PENDING_REASONS.BACKEND_NOT_RUNNING, items, message: 'Crewly 启动后马上把今天的任务交给团队' };
884
+ }
885
+ return { status: 'done', items, message: `已交给团队 ${sent} 件${later > 0 ? `,另外 ${later} 件按天排好` : ''}` };
886
+ }
887
+ }
888
+ //# sourceMappingURL=bundle-apply.service.js.map