crewly 1.20.132 → 1.20.134

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/constants.ts +49 -0
  2. package/config/index.ts +2 -0
  3. package/dist/backend/backend/src/constants.d.ts +82 -0
  4. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  5. package/dist/backend/backend/src/constants.js +87 -0
  6. package/dist/backend/backend/src/constants.js.map +1 -1
  7. package/dist/backend/backend/src/controllers/bundle/bundle.controller.d.ts +44 -0
  8. package/dist/backend/backend/src/controllers/bundle/bundle.controller.d.ts.map +1 -0
  9. package/dist/backend/backend/src/controllers/bundle/bundle.controller.js +122 -0
  10. package/dist/backend/backend/src/controllers/bundle/bundle.controller.js.map +1 -0
  11. package/dist/backend/backend/src/controllers/bundle/bundle.routes.d.ts +25 -0
  12. package/dist/backend/backend/src/controllers/bundle/bundle.routes.d.ts.map +1 -0
  13. package/dist/backend/backend/src/controllers/bundle/bundle.routes.js +33 -0
  14. package/dist/backend/backend/src/controllers/bundle/bundle.routes.js.map +1 -0
  15. package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.d.ts +56 -0
  16. package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.d.ts.map +1 -0
  17. package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.js +114 -0
  18. package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.js.map +1 -0
  19. package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts +25 -0
  20. package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
  21. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +73 -55
  22. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
  23. package/dist/backend/backend/src/controllers/cloud/cloud.routes.d.ts +3 -0
  24. package/dist/backend/backend/src/controllers/cloud/cloud.routes.d.ts.map +1 -1
  25. package/dist/backend/backend/src/controllers/cloud/cloud.routes.js +9 -0
  26. package/dist/backend/backend/src/controllers/cloud/cloud.routes.js.map +1 -1
  27. package/dist/backend/backend/src/index.d.ts.map +1 -1
  28. package/dist/backend/backend/src/index.js +14 -0
  29. package/dist/backend/backend/src/index.js.map +1 -1
  30. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  31. package/dist/backend/backend/src/routes/api.routes.js +4 -0
  32. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  33. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.d.ts +62 -0
  34. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.d.ts.map +1 -0
  35. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.js +173 -0
  36. package/dist/backend/backend/src/services/bundle/bundle-apply.factory.js.map +1 -0
  37. package/dist/backend/backend/src/services/bundle/bundle-apply.service.d.ts +359 -0
  38. package/dist/backend/backend/src/services/bundle/bundle-apply.service.d.ts.map +1 -0
  39. package/dist/backend/backend/src/services/bundle/bundle-apply.service.js +888 -0
  40. package/dist/backend/backend/src/services/bundle/bundle-apply.service.js.map +1 -0
  41. package/dist/backend/backend/src/services/bundle/bundle-catalog.d.ts +167 -0
  42. package/dist/backend/backend/src/services/bundle/bundle-catalog.d.ts.map +1 -0
  43. package/dist/backend/backend/src/services/bundle/bundle-catalog.js +248 -0
  44. package/dist/backend/backend/src/services/bundle/bundle-catalog.js.map +1 -0
  45. package/dist/backend/backend/src/services/bundle/bundle-connectors.d.ts +43 -0
  46. package/dist/backend/backend/src/services/bundle/bundle-connectors.d.ts.map +1 -0
  47. package/dist/backend/backend/src/services/bundle/bundle-connectors.js +57 -0
  48. package/dist/backend/backend/src/services/bundle/bundle-connectors.js.map +1 -0
  49. package/dist/backend/backend/src/services/bundle/bundle-deps.d.ts +37 -0
  50. package/dist/backend/backend/src/services/bundle/bundle-deps.d.ts.map +1 -0
  51. package/dist/backend/backend/src/services/bundle/bundle-deps.js +60 -0
  52. package/dist/backend/backend/src/services/bundle/bundle-deps.js.map +1 -0
  53. package/dist/backend/backend/src/services/bundle/bundle-manifest.d.ts +93 -0
  54. package/dist/backend/backend/src/services/bundle/bundle-manifest.d.ts.map +1 -0
  55. package/dist/backend/backend/src/services/bundle/bundle-manifest.js +532 -0
  56. package/dist/backend/backend/src/services/bundle/bundle-manifest.js.map +1 -0
  57. package/dist/backend/backend/src/services/bundle/bundle-placeholders.d.ts +100 -0
  58. package/dist/backend/backend/src/services/bundle/bundle-placeholders.d.ts.map +1 -0
  59. package/dist/backend/backend/src/services/bundle/bundle-placeholders.js +183 -0
  60. package/dist/backend/backend/src/services/bundle/bundle-placeholders.js.map +1 -0
  61. package/dist/backend/backend/src/services/bundle/bundle-runtime.d.ts +36 -0
  62. package/dist/backend/backend/src/services/bundle/bundle-runtime.d.ts.map +1 -0
  63. package/dist/backend/backend/src/services/bundle/bundle-runtime.js +38 -0
  64. package/dist/backend/backend/src/services/bundle/bundle-runtime.js.map +1 -0
  65. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.d.ts +105 -0
  66. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.d.ts.map +1 -0
  67. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.js +144 -0
  68. package/dist/backend/backend/src/services/bundle/bundle-skill-installer.js.map +1 -0
  69. package/dist/backend/backend/src/services/bundle/bundle-state.store.d.ts +48 -0
  70. package/dist/backend/backend/src/services/bundle/bundle-state.store.d.ts.map +1 -0
  71. package/dist/backend/backend/src/services/bundle/bundle-state.store.js +90 -0
  72. package/dist/backend/backend/src/services/bundle/bundle-state.store.js.map +1 -0
  73. package/dist/backend/backend/src/services/bundle/bundle-team.builder.d.ts +81 -0
  74. package/dist/backend/backend/src/services/bundle/bundle-team.builder.d.ts.map +1 -0
  75. package/dist/backend/backend/src/services/bundle/bundle-team.builder.js +172 -0
  76. package/dist/backend/backend/src/services/bundle/bundle-team.builder.js.map +1 -0
  77. package/dist/backend/backend/src/services/bundle/bundle-time.d.ts +59 -0
  78. package/dist/backend/backend/src/services/bundle/bundle-time.d.ts.map +1 -0
  79. package/dist/backend/backend/src/services/bundle/bundle-time.js +98 -0
  80. package/dist/backend/backend/src/services/bundle/bundle-time.js.map +1 -0
  81. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.d.ts +132 -0
  82. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.d.ts.map +1 -0
  83. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.js +228 -0
  84. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.js.map +1 -0
  85. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.d.ts +115 -0
  86. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.d.ts.map +1 -0
  87. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.js +160 -0
  88. package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.js.map +1 -0
  89. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  90. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +13 -0
  91. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  92. package/dist/backend/backend/src/services/core/storage.service.js +1 -1
  93. package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
  94. package/dist/backend/backend/src/services/core/teams-backup.service.d.ts.map +1 -1
  95. package/dist/backend/backend/src/services/core/teams-backup.service.js +4 -2
  96. package/dist/backend/backend/src/services/core/teams-backup.service.js.map +1 -1
  97. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.d.ts.map +1 -1
  98. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js +2 -0
  99. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js.map +1 -1
  100. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts +14 -2
  101. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -1
  102. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js +37 -1
  103. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -1
  104. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +21 -0
  105. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  106. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +85 -0
  107. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  108. package/dist/backend/backend/src/services/template/template.service.d.ts.map +1 -1
  109. package/dist/backend/backend/src/services/template/template.service.js +5 -2
  110. package/dist/backend/backend/src/services/template/template.service.js.map +1 -1
  111. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
  112. package/dist/backend/backend/src/services/workflow/cron-task.service.js +4 -2
  113. package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
  114. package/dist/backend/backend/src/types/solution-bundle.types.d.ts +367 -0
  115. package/dist/backend/backend/src/types/solution-bundle.types.d.ts.map +1 -0
  116. package/dist/backend/backend/src/types/solution-bundle.types.js +17 -0
  117. package/dist/backend/backend/src/types/solution-bundle.types.js.map +1 -0
  118. package/dist/backend/backend/src/types/team-template.types.d.ts +8 -0
  119. package/dist/backend/backend/src/types/team-template.types.d.ts.map +1 -1
  120. package/dist/backend/backend/src/types/team-template.types.js.map +1 -1
  121. package/dist/backend/build-info.json +2 -2
  122. package/dist/backend/config/constants.d.ts +48 -0
  123. package/dist/backend/config/constants.d.ts.map +1 -1
  124. package/dist/backend/config/constants.js +48 -0
  125. package/dist/backend/config/constants.js.map +1 -1
  126. package/dist/backend/config/index.d.ts +2 -2
  127. package/dist/backend/config/index.d.ts.map +1 -1
  128. package/dist/backend/config/index.js +2 -2
  129. package/dist/backend/config/index.js.map +1 -1
  130. package/dist/cli/backend/src/constants.d.ts +82 -0
  131. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  132. package/dist/cli/backend/src/constants.js +87 -0
  133. package/dist/cli/backend/src/constants.js.map +1 -1
  134. package/dist/cli/backend/src/services/bundle/bundle-apply.service.d.ts +359 -0
  135. package/dist/cli/backend/src/services/bundle/bundle-apply.service.d.ts.map +1 -0
  136. package/dist/cli/backend/src/services/bundle/bundle-apply.service.js +888 -0
  137. package/dist/cli/backend/src/services/bundle/bundle-apply.service.js.map +1 -0
  138. package/dist/cli/backend/src/services/bundle/bundle-catalog.d.ts +167 -0
  139. package/dist/cli/backend/src/services/bundle/bundle-catalog.d.ts.map +1 -0
  140. package/dist/cli/backend/src/services/bundle/bundle-catalog.js +248 -0
  141. package/dist/cli/backend/src/services/bundle/bundle-catalog.js.map +1 -0
  142. package/dist/cli/backend/src/services/bundle/bundle-deps.d.ts +37 -0
  143. package/dist/cli/backend/src/services/bundle/bundle-deps.d.ts.map +1 -0
  144. package/dist/cli/backend/src/services/bundle/bundle-deps.js +60 -0
  145. package/dist/cli/backend/src/services/bundle/bundle-deps.js.map +1 -0
  146. package/dist/cli/backend/src/services/bundle/bundle-manifest.d.ts +93 -0
  147. package/dist/cli/backend/src/services/bundle/bundle-manifest.d.ts.map +1 -0
  148. package/dist/cli/backend/src/services/bundle/bundle-manifest.js +532 -0
  149. package/dist/cli/backend/src/services/bundle/bundle-manifest.js.map +1 -0
  150. package/dist/cli/backend/src/services/bundle/bundle-placeholders.d.ts +100 -0
  151. package/dist/cli/backend/src/services/bundle/bundle-placeholders.d.ts.map +1 -0
  152. package/dist/cli/backend/src/services/bundle/bundle-placeholders.js +183 -0
  153. package/dist/cli/backend/src/services/bundle/bundle-placeholders.js.map +1 -0
  154. package/dist/cli/backend/src/services/bundle/bundle-runtime.d.ts +36 -0
  155. package/dist/cli/backend/src/services/bundle/bundle-runtime.d.ts.map +1 -0
  156. package/dist/cli/backend/src/services/bundle/bundle-runtime.js +38 -0
  157. package/dist/cli/backend/src/services/bundle/bundle-runtime.js.map +1 -0
  158. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.d.ts +105 -0
  159. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.d.ts.map +1 -0
  160. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.js +144 -0
  161. package/dist/cli/backend/src/services/bundle/bundle-skill-installer.js.map +1 -0
  162. package/dist/cli/backend/src/services/bundle/bundle-state.store.d.ts +48 -0
  163. package/dist/cli/backend/src/services/bundle/bundle-state.store.d.ts.map +1 -0
  164. package/dist/cli/backend/src/services/bundle/bundle-state.store.js +90 -0
  165. package/dist/cli/backend/src/services/bundle/bundle-state.store.js.map +1 -0
  166. package/dist/cli/backend/src/services/bundle/bundle-team.builder.d.ts +81 -0
  167. package/dist/cli/backend/src/services/bundle/bundle-team.builder.d.ts.map +1 -0
  168. package/dist/cli/backend/src/services/bundle/bundle-team.builder.js +172 -0
  169. package/dist/cli/backend/src/services/bundle/bundle-team.builder.js.map +1 -0
  170. package/dist/cli/backend/src/services/bundle/bundle-time.d.ts +59 -0
  171. package/dist/cli/backend/src/services/bundle/bundle-time.d.ts.map +1 -0
  172. package/dist/cli/backend/src/services/bundle/bundle-time.js +98 -0
  173. package/dist/cli/backend/src/services/bundle/bundle-time.js.map +1 -0
  174. package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.d.ts +132 -0
  175. package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.d.ts.map +1 -0
  176. package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.js +228 -0
  177. package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.js.map +1 -0
  178. package/dist/cli/backend/src/services/core/storage.service.js +1 -1
  179. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
  180. package/dist/cli/backend/src/services/core/teams-backup.service.d.ts.map +1 -1
  181. package/dist/cli/backend/src/services/core/teams-backup.service.js +4 -2
  182. package/dist/cli/backend/src/services/core/teams-backup.service.js.map +1 -1
  183. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.d.ts +72 -0
  184. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.d.ts.map +1 -0
  185. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.js +389 -0
  186. package/dist/cli/backend/src/services/marketplace/marketplace-installer.service.js.map +1 -0
  187. package/dist/cli/backend/src/services/messaging/index.d.ts +15 -0
  188. package/dist/cli/backend/src/services/messaging/index.d.ts.map +1 -0
  189. package/dist/cli/backend/src/services/messaging/index.js +20 -0
  190. package/dist/cli/backend/src/services/messaging/index.js.map +1 -0
  191. package/dist/cli/backend/src/services/messaging/queue-processor.service.d.ts +224 -0
  192. package/dist/cli/backend/src/services/messaging/queue-processor.service.d.ts.map +1 -0
  193. package/dist/cli/backend/src/services/messaging/queue-processor.service.js +969 -0
  194. package/dist/cli/backend/src/services/messaging/queue-processor.service.js.map +1 -0
  195. package/dist/cli/backend/src/services/messaging/response-router.service.d.ts +110 -0
  196. package/dist/cli/backend/src/services/messaging/response-router.service.d.ts.map +1 -0
  197. package/dist/cli/backend/src/services/messaging/response-router.service.js +268 -0
  198. package/dist/cli/backend/src/services/messaging/response-router.service.js.map +1 -0
  199. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts +14 -2
  200. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -1
  201. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js +37 -1
  202. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -1
  203. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.d.ts +133 -0
  204. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.d.ts.map +1 -0
  205. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.js +232 -0
  206. package/dist/cli/backend/src/services/orchestrator/commitment-approval-guard.js.map +1 -0
  207. package/dist/cli/backend/src/services/skill/skill-catalog.service.d.ts +277 -0
  208. package/dist/cli/backend/src/services/skill/skill-catalog.service.d.ts.map +1 -0
  209. package/dist/cli/backend/src/services/skill/skill-catalog.service.js +708 -0
  210. package/dist/cli/backend/src/services/skill/skill-catalog.service.js.map +1 -0
  211. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.d.ts +243 -0
  212. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.d.ts.map +1 -0
  213. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.js +439 -0
  214. package/dist/cli/backend/src/services/skill-setup/skill-install-job.service.js.map +1 -0
  215. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +21 -0
  216. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  217. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +85 -0
  218. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  219. package/dist/cli/backend/src/services/template/template.service.d.ts.map +1 -1
  220. package/dist/cli/backend/src/services/template/template.service.js +5 -2
  221. package/dist/cli/backend/src/services/template/template.service.js.map +1 -1
  222. package/dist/cli/backend/src/services/workflow/cron-task.service.d.ts +316 -0
  223. package/dist/cli/backend/src/services/workflow/cron-task.service.d.ts.map +1 -0
  224. package/dist/cli/backend/src/services/workflow/cron-task.service.js +1043 -0
  225. package/dist/cli/backend/src/services/workflow/cron-task.service.js.map +1 -0
  226. package/dist/cli/backend/src/types/cron-task.types.d.ts +95 -0
  227. package/dist/cli/backend/src/types/cron-task.types.d.ts.map +1 -0
  228. package/dist/cli/backend/src/types/cron-task.types.js +10 -0
  229. package/dist/cli/backend/src/types/cron-task.types.js.map +1 -0
  230. package/dist/cli/backend/src/types/solution-bundle.types.d.ts +367 -0
  231. package/dist/cli/backend/src/types/solution-bundle.types.d.ts.map +1 -0
  232. package/dist/cli/backend/src/types/solution-bundle.types.js +17 -0
  233. package/dist/cli/backend/src/types/solution-bundle.types.js.map +1 -0
  234. package/dist/cli/backend/src/types/team-template.types.d.ts +8 -0
  235. package/dist/cli/backend/src/types/team-template.types.d.ts.map +1 -1
  236. package/dist/cli/backend/src/types/team-template.types.js.map +1 -1
  237. package/dist/cli/cli/src/commands/cloud.d.ts +45 -25
  238. package/dist/cli/cli/src/commands/cloud.d.ts.map +1 -1
  239. package/dist/cli/cli/src/commands/cloud.js +153 -47
  240. package/dist/cli/cli/src/commands/cloud.js.map +1 -1
  241. package/dist/cli/cli/src/commands/deploy-bundle.d.ts +160 -0
  242. package/dist/cli/cli/src/commands/deploy-bundle.d.ts.map +1 -0
  243. package/dist/cli/cli/src/commands/deploy-bundle.js +386 -0
  244. package/dist/cli/cli/src/commands/deploy-bundle.js.map +1 -0
  245. package/dist/cli/cli/src/commands/onboard-checklist.js +3 -3
  246. package/dist/cli/cli/src/commands/onboard-checklist.js.map +1 -1
  247. package/dist/cli/cli/src/commands/onboard.d.ts +32 -1
  248. package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
  249. package/dist/cli/cli/src/commands/onboard.js +80 -3
  250. package/dist/cli/cli/src/commands/onboard.js.map +1 -1
  251. package/dist/cli/cli/src/constants.d.ts +5 -0
  252. package/dist/cli/cli/src/constants.d.ts.map +1 -1
  253. package/dist/cli/cli/src/constants.js +5 -0
  254. package/dist/cli/cli/src/constants.js.map +1 -1
  255. package/dist/cli/cli/src/index.js +21 -4
  256. package/dist/cli/cli/src/index.js.map +1 -1
  257. package/dist/cli/cli/src/utils/cloud-submit.d.ts.map +1 -1
  258. package/dist/cli/cli/src/utils/cloud-submit.js +14 -5
  259. package/dist/cli/cli/src/utils/cloud-submit.js.map +1 -1
  260. package/dist/cli/config/constants.d.ts +48 -0
  261. package/dist/cli/config/constants.d.ts.map +1 -1
  262. package/dist/cli/config/constants.js +48 -0
  263. package/dist/cli/config/constants.js.map +1 -1
  264. package/dist/cli/config/index.d.ts +2 -2
  265. package/dist/cli/config/index.d.ts.map +1 -1
  266. package/dist/cli/config/index.js +2 -2
  267. package/dist/cli/config/index.js.map +1 -1
  268. package/frontend/dist/assets/{index-5b98bf77.js → index-97bb17d4.js} +274 -274
  269. package/frontend/dist/index.html +1 -1
  270. package/package.json +1 -1
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Solution bundle controller — REST handlers for `/api/bundles`
3
+ * (specs/solution-bundles.md).
4
+ *
5
+ * Every response is `{ success: true, data }` or
6
+ * `{ success: false, error, code?, missing?, invalid? }`.
7
+ *
8
+ * Owner-only: applying a bundle and reading an apply job refuse a request
9
+ * that carries `X-Agent-Session` (403), like `/api/harness` and the setup
10
+ * checklist — an agent must not deploy teams, schedules and channels in the
11
+ * owner's name. The catalog reads stay open.
12
+ *
13
+ * @module controllers/bundle/bundle.controller
14
+ */
15
+ import { refuseAgent } from '../harness/harness.controller.js';
16
+ import { BundleError } from '../../services/bundle/bundle-apply.service.js';
17
+ import { toBundleDetail, toBundleSummary } from '../../services/bundle/bundle-catalog.js';
18
+ /** HTTP status per engine error code. */
19
+ const ERROR_STATUS = {
20
+ unknown_bundle: 404,
21
+ job_not_found: 404,
22
+ bundle_not_ready: 409,
23
+ invalid_runtime: 400,
24
+ invalid_answers: 400,
25
+ };
26
+ /**
27
+ * Send an error response.
28
+ *
29
+ * @param res - Response
30
+ * @param error - Thrown value
31
+ */
32
+ export function sendBundleError(res, error) {
33
+ if (error instanceof BundleError) {
34
+ res.status(ERROR_STATUS[error.code] ?? 400).json({
35
+ success: false,
36
+ error: error.message,
37
+ code: error.code,
38
+ ...(error.details ? { missing: error.details.missing, invalid: error.details.invalid } : {}),
39
+ });
40
+ return;
41
+ }
42
+ res.status(500).json({ success: false, error: error instanceof Error ? error.message : String(error) });
43
+ }
44
+ /**
45
+ * A route parameter as a string.
46
+ *
47
+ * @param req - Request
48
+ * @param name - Parameter
49
+ * @returns Value ('' when absent)
50
+ */
51
+ function param(req, name) {
52
+ const value = req.params[name];
53
+ return typeof value === 'string' ? value : '';
54
+ }
55
+ /**
56
+ * Build the handlers.
57
+ *
58
+ * @param deps - Engine and catalog accessors (tests inject fakes)
59
+ * @returns Handlers
60
+ */
61
+ export function createBundleController(deps) {
62
+ return {
63
+ /** GET / — ready bundles (`?drafts=1` includes drafts) */
64
+ async list(req, res) {
65
+ try {
66
+ const includeDrafts = req.query.drafts === '1' || req.query.drafts === 'true';
67
+ const bundles = deps.catalog().list({ includeDrafts }).map((b) => toBundleSummary(b.template));
68
+ res.json({ success: true, data: { bundles } });
69
+ }
70
+ catch (error) {
71
+ sendBundleError(res, error);
72
+ }
73
+ },
74
+ /** GET /:templateId — questions, members, what gets set up, and this machine's deployment */
75
+ async detail(req, res) {
76
+ try {
77
+ const id = param(req, 'templateId');
78
+ const entry = deps.catalog().get(id);
79
+ if (!entry)
80
+ throw new BundleError('unknown_bundle', `没有找到方案「${id}」`);
81
+ const deployment = await deps.service().getDeployment(id);
82
+ res.json({ success: true, data: { bundle: toBundleDetail(entry.template), deployment } });
83
+ }
84
+ catch (error) {
85
+ sendBundleError(res, error);
86
+ }
87
+ },
88
+ /** POST /apply { templateId, answers, runtime?, allowDraft? } — start (or join) an apply job; 202 */
89
+ async apply(req, res) {
90
+ if (refuseAgent(req, res, 'deploy a solution bundle'))
91
+ return;
92
+ try {
93
+ const body = (req.body ?? {});
94
+ const templateId = typeof body.templateId === 'string' ? body.templateId.trim() : '';
95
+ if (!templateId)
96
+ throw new BundleError('unknown_bundle', 'templateId is required');
97
+ const deployment = await deps.service().start({
98
+ templateId,
99
+ answers: body.answers,
100
+ runtime: typeof body.runtime === 'string' && body.runtime ? body.runtime : undefined,
101
+ allowDraft: body.allowDraft === true,
102
+ });
103
+ res.status(202).json({ success: true, data: { jobId: deployment.jobId, deployment } });
104
+ }
105
+ catch (error) {
106
+ sendBundleError(res, error);
107
+ }
108
+ },
109
+ /** GET /apply/:jobId — progress of an apply job */
110
+ async getJob(req, res) {
111
+ if (refuseAgent(req, res, 'read a bundle deployment'))
112
+ return;
113
+ try {
114
+ res.json({ success: true, data: await deps.service().getJob(param(req, 'jobId')) });
115
+ }
116
+ catch (error) {
117
+ sendBundleError(res, error);
118
+ }
119
+ },
120
+ };
121
+ }
122
+ //# sourceMappingURL=bundle.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.controller.js","sourceRoot":"","sources":["../../../../../../backend/src/controllers/bundle/bundle.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Solution bundle routes — mounted at `/api/bundles`.
3
+ *
4
+ * GET and POST only, so the phone / portal relay can carry them
5
+ * (`MobileApiRelayService` allowlists `GET /bundles` and `POST /bundles/apply`).
6
+ *
7
+ * - GET / — ready bundles
8
+ * - GET /apply/:jobId — apply progress (owner-only)
9
+ * - POST /apply — deploy { templateId, answers, runtime?, allowDraft? } (owner-only)
10
+ * - GET /:templateId — questions, members, deployment on this machine
11
+ *
12
+ * @module controllers/bundle/bundle.routes
13
+ */
14
+ import { Router } from 'express';
15
+ import type { BundleApplyService } from '../../services/bundle/bundle-apply.service.js';
16
+ import type { BundleCatalog } from '../../services/bundle/bundle-catalog.js';
17
+ /**
18
+ * Create the bundle router.
19
+ *
20
+ * @param getService - Engine accessor (tests inject a fake)
21
+ * @param getCatalog - Catalog accessor (tests inject a fake)
22
+ * @returns Express router
23
+ */
24
+ export declare function createBundleRouter(getService?: () => BundleApplyService, getCatalog?: () => Pick<BundleCatalog, 'list' | 'get'>): Router;
25
+ //# sourceMappingURL=bundle.routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.routes.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/bundle/bundle.routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+CAA+C,CAAC;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAI7E;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,GAAE,MAAM,kBAA0C,EAC5D,UAAU,GAAE,MAAM,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,KAAK,CAAoB,GACvE,MAAM,CAQR"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Solution bundle routes — mounted at `/api/bundles`.
3
+ *
4
+ * GET and POST only, so the phone / portal relay can carry them
5
+ * (`MobileApiRelayService` allowlists `GET /bundles` and `POST /bundles/apply`).
6
+ *
7
+ * - GET / — ready bundles
8
+ * - GET /apply/:jobId — apply progress (owner-only)
9
+ * - POST /apply — deploy { templateId, answers, runtime?, allowDraft? } (owner-only)
10
+ * - GET /:templateId — questions, members, deployment on this machine
11
+ *
12
+ * @module controllers/bundle/bundle.routes
13
+ */
14
+ import { Router } from 'express';
15
+ import { getBundleApplyService, getBundleCatalog } from '../../services/bundle/bundle-apply.factory.js';
16
+ import { createBundleController } from './bundle.controller.js';
17
+ /**
18
+ * Create the bundle router.
19
+ *
20
+ * @param getService - Engine accessor (tests inject a fake)
21
+ * @param getCatalog - Catalog accessor (tests inject a fake)
22
+ * @returns Express router
23
+ */
24
+ export function createBundleRouter(getService = getBundleApplyService, getCatalog = getBundleCatalog) {
25
+ const router = Router();
26
+ const controller = createBundleController({ service: getService, catalog: getCatalog });
27
+ router.get('/', controller.list);
28
+ router.get('/apply/:jobId', controller.getJob);
29
+ router.post('/apply', controller.apply);
30
+ router.get('/:templateId', controller.detail);
31
+ return router;
32
+ }
33
+ //# sourceMappingURL=bundle.routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.routes.js","sourceRoot":"","sources":["../../../../../../backend/src/controllers/bundle/bundle.routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACxG,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,aAAuC,qBAAqB,EAC5D,aAAwD,gBAAgB;IAExE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,MAAM,UAAU,GAAG,sBAAsB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;IACxF,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Cloud device pairing controller — `/api/cloud/device/*` on this backend.
3
+ *
4
+ * The web UI (`/setup` Cloud step, Settings → Cloud) and the phone app (over
5
+ * the relay allowlist) connect this machine to Crewly Cloud without handling
6
+ * tokens:
7
+ *
8
+ * - `POST /api/cloud/device/start` → `{ state: 'pending', userCode, verificationUrl, expiresAt, … }`
9
+ * (starts a pairing with crewly-auth, or returns the pending one).
10
+ * - `GET /api/cloud/device/status` → the same shape; `state` moves to
11
+ * `connected` once the owner approves and the backend has connected itself.
12
+ * - `POST /api/cloud/device/cancel` → stop waiting.
13
+ *
14
+ * Owner-only: every route refuses a request carrying `X-Agent-Session` (403)
15
+ * — an agent must not sign the machine in to an account, same rule as
16
+ * `/api/harness`. Responses never contain the device code or tokens.
17
+ *
18
+ * @module controllers/cloud/cloud-device-pairing.controller
19
+ */
20
+ import type { Request, Response } from 'express';
21
+ import { CloudDevicePairingService } from '../../services/cloud/cloud-device-pairing.service.js';
22
+ /**
23
+ * The process-wide pairing service, wired to the real Cloud URL, device
24
+ * identity and connect path.
25
+ *
26
+ * @returns CloudDevicePairingService
27
+ */
28
+ export declare function getCloudDevicePairingService(): CloudDevicePairingService;
29
+ /**
30
+ * Replace the service (tests).
31
+ *
32
+ * @param next - Service to use, or null to rebuild the real one
33
+ */
34
+ export declare function setCloudDevicePairingServiceForTests(next: CloudDevicePairingService | null): void;
35
+ /**
36
+ * POST /api/cloud/device/start — begin (or resume) a pairing.
37
+ *
38
+ * @param req - Body `{ deviceName? }`
39
+ * @param res - `{ success, data: CloudDevicePairingStatus }`
40
+ */
41
+ export declare function startCloudDevicePairing(req: Request, res: Response): Promise<void>;
42
+ /**
43
+ * GET /api/cloud/device/status — where the pairing stands.
44
+ *
45
+ * @param req - Express request
46
+ * @param res - `{ success, data: CloudDevicePairingStatus }`
47
+ */
48
+ export declare function getCloudDevicePairingStatus(req: Request, res: Response): void;
49
+ /**
50
+ * POST /api/cloud/device/cancel — stop waiting for the owner.
51
+ *
52
+ * @param req - Express request
53
+ * @param res - `{ success, data: CloudDevicePairingStatus }`
54
+ */
55
+ export declare function cancelCloudDevicePairing(req: Request, res: Response): void;
56
+ //# sourceMappingURL=cloud-device-pairing.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-device-pairing.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/cloud/cloud-device-pairing.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sDAAsD,CAAC;AAcjG;;;;;GAKG;AACH,wBAAgB,4BAA4B,IAAI,yBAAyB,CAUxE;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,yBAAyB,GAAG,IAAI,GAAG,IAAI,CAEjG;AAeD;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAaxF;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAI7E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAG1E"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Cloud device pairing controller — `/api/cloud/device/*` on this backend.
3
+ *
4
+ * The web UI (`/setup` Cloud step, Settings → Cloud) and the phone app (over
5
+ * the relay allowlist) connect this machine to Crewly Cloud without handling
6
+ * tokens:
7
+ *
8
+ * - `POST /api/cloud/device/start` → `{ state: 'pending', userCode, verificationUrl, expiresAt, … }`
9
+ * (starts a pairing with crewly-auth, or returns the pending one).
10
+ * - `GET /api/cloud/device/status` → the same shape; `state` moves to
11
+ * `connected` once the owner approves and the backend has connected itself.
12
+ * - `POST /api/cloud/device/cancel` → stop waiting.
13
+ *
14
+ * Owner-only: every route refuses a request carrying `X-Agent-Session` (403)
15
+ * — an agent must not sign the machine in to an account, same rule as
16
+ * `/api/harness`. Responses never contain the device code or tokens.
17
+ *
18
+ * @module controllers/cloud/cloud-device-pairing.controller
19
+ */
20
+ import { readAgentSessionHeader } from '../../utils/agent-caller.utils.js';
21
+ import { CLOUD_CONSTANTS } from '../../constants.js';
22
+ import { DeviceIdentityService } from '../../services/cloud/device-identity.service.js';
23
+ import { CloudDevicePairingService } from '../../services/cloud/cloud-device-pairing.service.js';
24
+ import { performCloudConnect } from './cloud.controller.js';
25
+ /** HTTP 403. */
26
+ const HTTP_FORBIDDEN = 403;
27
+ /** HTTP 502 — crewly-auth refused or was unreachable. */
28
+ const HTTP_BAD_GATEWAY = 502;
29
+ /** Longest device name accepted from the UI. */
30
+ const MAX_DEVICE_NAME_LENGTH = 80;
31
+ let service = null;
32
+ /**
33
+ * The process-wide pairing service, wired to the real Cloud URL, device
34
+ * identity and connect path.
35
+ *
36
+ * @returns CloudDevicePairingService
37
+ */
38
+ export function getCloudDevicePairingService() {
39
+ if (!service) {
40
+ service = new CloudDevicePairingService({
41
+ cloudUrl: () => CLOUD_CONSTANTS.DEFAULT_CLOUD_URL,
42
+ identity: () => DeviceIdentityService.getInstance().getOrCreateIdentity(),
43
+ connect: (credentials) => performCloudConnect({ token: credentials.token, refreshToken: credentials.refreshToken }),
44
+ });
45
+ }
46
+ return service;
47
+ }
48
+ /**
49
+ * Replace the service (tests).
50
+ *
51
+ * @param next - Service to use, or null to rebuild the real one
52
+ */
53
+ export function setCloudDevicePairingServiceForTests(next) {
54
+ service = next;
55
+ }
56
+ /**
57
+ * Refuse a request made by an agent session.
58
+ *
59
+ * @param req - Express request
60
+ * @param res - Express response
61
+ * @returns True when refused (response sent)
62
+ */
63
+ function refuseAgent(req, res) {
64
+ if (!readAgentSessionHeader(req))
65
+ return false;
66
+ res.status(HTTP_FORBIDDEN).json({ success: false, error: 'Only the owner can connect this machine to Crewly Cloud' });
67
+ return true;
68
+ }
69
+ /**
70
+ * POST /api/cloud/device/start — begin (or resume) a pairing.
71
+ *
72
+ * @param req - Body `{ deviceName? }`
73
+ * @param res - `{ success, data: CloudDevicePairingStatus }`
74
+ */
75
+ export async function startCloudDevicePairing(req, res) {
76
+ if (refuseAgent(req, res))
77
+ return;
78
+ const raw = req.body?.deviceName;
79
+ const deviceName = typeof raw === 'string' ? raw.slice(0, MAX_DEVICE_NAME_LENGTH) : undefined;
80
+ try {
81
+ const status = await getCloudDevicePairingService().start({ deviceName });
82
+ res.json({ success: true, data: status });
83
+ }
84
+ catch (error) {
85
+ res.status(HTTP_BAD_GATEWAY).json({
86
+ success: false,
87
+ error: error instanceof Error ? error.message : 'Could not reach Crewly Cloud',
88
+ });
89
+ }
90
+ }
91
+ /**
92
+ * GET /api/cloud/device/status — where the pairing stands.
93
+ *
94
+ * @param req - Express request
95
+ * @param res - `{ success, data: CloudDevicePairingStatus }`
96
+ */
97
+ export function getCloudDevicePairingStatus(req, res) {
98
+ if (refuseAgent(req, res))
99
+ return;
100
+ res.setHeader('Cache-Control', 'no-store');
101
+ res.json({ success: true, data: getCloudDevicePairingService().getStatus() });
102
+ }
103
+ /**
104
+ * POST /api/cloud/device/cancel — stop waiting for the owner.
105
+ *
106
+ * @param req - Express request
107
+ * @param res - `{ success, data: CloudDevicePairingStatus }`
108
+ */
109
+ export function cancelCloudDevicePairing(req, res) {
110
+ if (refuseAgent(req, res))
111
+ return;
112
+ res.json({ success: true, data: getCloudDevicePairingService().cancel() });
113
+ }
114
+ //# sourceMappingURL=cloud-device-pairing.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-device-pairing.controller.js","sourceRoot":"","sources":["../../../../../../backend/src/controllers/cloud/cloud-device-pairing.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iDAAiD,CAAC;AACxF,OAAO,EAAE,yBAAyB,EAAE,MAAM,sDAAsD,CAAC;AACjG,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,gBAAgB;AAChB,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,yDAAyD;AACzD,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,gDAAgD;AAChD,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAElC,IAAI,OAAO,GAAqC,IAAI,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B;IAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,yBAAyB,CAAC;YACtC,QAAQ,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,iBAAiB;YACjD,QAAQ,EAAE,GAAG,EAAE,CAAC,qBAAqB,CAAC,WAAW,EAAE,CAAC,mBAAmB,EAAE;YACzE,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CACvB,mBAAmB,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,CAAC,YAAY,EAAE,CAAC;SAC5F,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oCAAoC,CAAC,IAAsC;IACzF,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,GAAY,EAAE,GAAa;IAC9C,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,yDAAyD,EAAE,CAAC,CAAC;IACtH,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,GAAY,EAAE,GAAa;IACvE,IAAI,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC;QAAE,OAAO;IAClC,MAAM,GAAG,GAAI,GAAG,CAAC,IAA6C,EAAE,UAAU,CAAC;IAC3E,MAAM,UAAU,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,4BAA4B,EAAE,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1E,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC;YAChC,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B;SAC/E,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAAY,EAAE,GAAa;IACrE,IAAI,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC;QAAE,OAAO;IAClC,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC3C,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,4BAA4B,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAY,EAAE,GAAa;IAClE,IAAI,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC;QAAE,OAAO;IAClC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,4BAA4B,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC"}
@@ -26,6 +26,31 @@ export interface CloudHandshakePayload {
26
26
  /** ISO timestamp of this handshake */
27
27
  timestamp: string;
28
28
  }
29
+ /** Input for {@link performCloudConnect}. */
30
+ export interface CloudConnectInput {
31
+ /** Cloud API base URL (defaults to CLOUD_CONSTANTS.DEFAULT_CLOUD_URL) */
32
+ cloudUrl?: string;
33
+ /** Cloud access token (JWT) */
34
+ token: string;
35
+ /** Cloud refresh token, for auto-renewal */
36
+ refreshToken?: string;
37
+ }
38
+ /**
39
+ * Connect this instance to CrewlyAI Cloud with a token pair.
40
+ *
41
+ * The single connect path shared by `POST /api/cloud/connect` (CLI, pasted
42
+ * token, Google redirect) and the device-code pairing that completes in the
43
+ * background ({@link CloudDevicePairingService}): verify the token, persist
44
+ * the credentials, send the handshake, and start Cloud Sync, the message
45
+ * router and the Slack config sync.
46
+ *
47
+ * @param input - Cloud URL and token pair
48
+ * @returns The subscription tier
49
+ * @throws Error when the Cloud rejects the token (message contains "authentication failed")
50
+ */
51
+ export declare function performCloudConnect(input: CloudConnectInput): Promise<{
52
+ tier: string;
53
+ }>;
29
54
  /**
30
55
  * POST /api/cloud/connect
31
56
  *
@@ -1 +1 @@
1
- {"version":3,"file":"cloud.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/cloud/cloud.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAyB/D,oEAAoE;AACpE,MAAM,WAAW,qBAAqB;IACpC,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB;AA+ED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+EnG;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBxG;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBlG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DvG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhG;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDtG;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBtG;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CrG;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CxG;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0ChG"}
1
+ {"version":3,"file":"cloud.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/cloud/cloud.controller.ts"],"names":[],"mappings":"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"}
@@ -98,6 +98,78 @@ async function sendCloudHandshake(cloudUrl, token) {
98
98
  });
99
99
  }
100
100
  }
101
+ /**
102
+ * Connect this instance to CrewlyAI Cloud with a token pair.
103
+ *
104
+ * The single connect path shared by `POST /api/cloud/connect` (CLI, pasted
105
+ * token, Google redirect) and the device-code pairing that completes in the
106
+ * background ({@link CloudDevicePairingService}): verify the token, persist
107
+ * the credentials, send the handshake, and start Cloud Sync, the message
108
+ * router and the Slack config sync.
109
+ *
110
+ * @param input - Cloud URL and token pair
111
+ * @returns The subscription tier
112
+ * @throws Error when the Cloud rejects the token (message contains "authentication failed")
113
+ */
114
+ export async function performCloudConnect(input) {
115
+ const { cloudUrl, token, refreshToken } = input;
116
+ const resolvedUrl = cloudUrl || CLOUD_CONSTANTS.DEFAULT_CLOUD_URL;
117
+ const client = CloudClientService.getInstance();
118
+ // Try local JWT verification first — this works when OSS and Cloud share
119
+ // the same JWT secret (CREWLY_JWT_SECRET), or when the token was issued
120
+ // by this same instance (e.g. local Google OAuth flow).
121
+ const localPayload = verifyJwt(token);
122
+ let result;
123
+ if (localPayload) {
124
+ // JWT verified locally — connect without calling cloud API
125
+ const tier = localPayload.plan || 'free';
126
+ client.connectLocal(resolvedUrl, token, tier, refreshToken);
127
+ result = { success: true, tier };
128
+ logger.info('Connected to CrewlyAI Cloud (local JWT verification)', { tier });
129
+ }
130
+ else {
131
+ // Local verification failed (different JWT secret) — call cloud API
132
+ result = await client.connect(resolvedUrl, token, refreshToken);
133
+ logger.info('Connected to CrewlyAI Cloud (remote verification)', { tier: result.tier });
134
+ }
135
+ // Safety net: ensure refreshToken is persisted even if connect/connectLocal
136
+ // did not receive it (e.g. race condition or future refactor).
137
+ // Both connect() and connectLocal() already store refreshToken, but
138
+ // setRefreshToken() also re-persists config to disk as an extra guarantee.
139
+ if (refreshToken) {
140
+ client.setRefreshToken(refreshToken);
141
+ }
142
+ // Send device metadata + active teams to Cloud (best-effort, non-blocking)
143
+ sendCloudHandshake(resolvedUrl, token).catch(() => { });
144
+ // Start Cloud Sync for device discovery and message polling (best-effort)
145
+ try {
146
+ const identityService = DeviceIdentityService.getInstance();
147
+ const identity = await identityService.getOrCreateIdentity();
148
+ CloudSyncService.getInstance().start({
149
+ cloudUrl: resolvedUrl,
150
+ token,
151
+ deviceId: identity.deviceId,
152
+ deviceName: identity.deviceName,
153
+ });
154
+ logger.info('CloudSyncService started after cloud connect', { deviceId: identity.deviceId });
155
+ // Start MessageRouterService for cross-device agent communication
156
+ try {
157
+ const { startMessageRouter, startSlackCloudSync } = await import('../../services/cloud/cloud-initializer.js');
158
+ startMessageRouter();
159
+ // Slack v3: fetch the Cloud-owned Slack config right after login
160
+ startSlackCloudSync();
161
+ }
162
+ catch {
163
+ logger.debug('MessageRouterService start deferred (non-fatal)');
164
+ }
165
+ }
166
+ catch (syncErr) {
167
+ logger.warn('CloudSyncService start failed (non-fatal)', {
168
+ error: syncErr instanceof Error ? syncErr.message : String(syncErr),
169
+ });
170
+ }
171
+ return { tier: result.tier };
172
+ }
101
173
  /**
102
174
  * POST /api/cloud/connect
103
175
  *
@@ -116,61 +188,7 @@ export async function connectToCloud(req, res, next) {
116
188
  res.status(400).json({ success: false, error: 'Missing required parameter: token' });
117
189
  return;
118
190
  }
119
- const resolvedUrl = cloudUrl || CLOUD_CONSTANTS.DEFAULT_CLOUD_URL;
120
- const client = CloudClientService.getInstance();
121
- // Try local JWT verification first — this works when OSS and Cloud share
122
- // the same JWT secret (CREWLY_JWT_SECRET), or when the token was issued
123
- // by this same instance (e.g. local Google OAuth flow).
124
- const localPayload = verifyJwt(token);
125
- let result;
126
- if (localPayload) {
127
- // JWT verified locally — connect without calling cloud API
128
- const tier = localPayload.plan || 'free';
129
- client.connectLocal(resolvedUrl, token, tier, refreshToken);
130
- result = { success: true, tier };
131
- logger.info('Connected to CrewlyAI Cloud (local JWT verification)', { tier });
132
- }
133
- else {
134
- // Local verification failed (different JWT secret) — call cloud API
135
- result = await client.connect(resolvedUrl, token, refreshToken);
136
- logger.info('Connected to CrewlyAI Cloud (remote verification)', { tier: result.tier });
137
- }
138
- // Safety net: ensure refreshToken is persisted even if connect/connectLocal
139
- // did not receive it (e.g. race condition or future refactor).
140
- // Both connect() and connectLocal() already store refreshToken, but
141
- // setRefreshToken() also re-persists config to disk as an extra guarantee.
142
- if (refreshToken) {
143
- client.setRefreshToken(refreshToken);
144
- }
145
- // Send device metadata + active teams to Cloud (best-effort, non-blocking)
146
- sendCloudHandshake(resolvedUrl, token).catch(() => { });
147
- // Start Cloud Sync for device discovery and message polling (best-effort)
148
- try {
149
- const identityService = DeviceIdentityService.getInstance();
150
- const identity = await identityService.getOrCreateIdentity();
151
- CloudSyncService.getInstance().start({
152
- cloudUrl: resolvedUrl,
153
- token,
154
- deviceId: identity.deviceId,
155
- deviceName: identity.deviceName,
156
- });
157
- logger.info('CloudSyncService started after cloud connect', { deviceId: identity.deviceId });
158
- // Start MessageRouterService for cross-device agent communication
159
- try {
160
- const { startMessageRouter, startSlackCloudSync } = await import('../../services/cloud/cloud-initializer.js');
161
- startMessageRouter();
162
- // Slack v3: fetch the Cloud-owned Slack config right after login
163
- startSlackCloudSync();
164
- }
165
- catch {
166
- logger.debug('MessageRouterService start deferred (non-fatal)');
167
- }
168
- }
169
- catch (syncErr) {
170
- logger.warn('CloudSyncService start failed (non-fatal)', {
171
- error: syncErr instanceof Error ? syncErr.message : String(syncErr),
172
- });
173
- }
191
+ const result = await performCloudConnect({ cloudUrl, token, refreshToken });
174
192
  res.json({ success: true, data: { tier: result.tier } });
175
193
  }
176
194
  catch (error) {