@taskforcehq/taskforce 0.3.305 → 0.3.306

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 (337) hide show
  1. package/dist/Taskforce.module.css +295 -89
  2. package/dist/TaskforceCore.js +215 -43
  3. package/dist/TaskforceCore.test.js +433 -28
  4. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  5. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  8. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  10. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  11. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  12. package/dist/compat/workspaceSyncCompat.js +46 -2
  13. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  14. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  15. package/dist/components/context/ContextAttachmentManager.js +549 -0
  16. package/dist/components/features/AgentsModule.d.ts +10 -1
  17. package/dist/components/features/AgentsModule.js +260 -15
  18. package/dist/components/features/AgentsModule.test.js +255 -5
  19. package/dist/components/features/DocumentViewer.d.ts +5 -1
  20. package/dist/components/features/DocumentViewer.js +60 -3
  21. package/dist/components/features/DocumentWorkspace.js +1 -1
  22. package/dist/components/features/DocumentWorkspace.test.js +33 -0
  23. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  24. package/dist/components/task/TaskCard.js +19 -28
  25. package/dist/components/task/TaskCard.test.js +38 -3
  26. package/dist/components/task/TaskContextUpload.js +4 -526
  27. package/dist/components/task/TaskForm.js +85 -90
  28. package/dist/components/task/TaskForm.test.js +110 -8
  29. package/dist/components/task/TaskKanban.d.ts +2 -1
  30. package/dist/components/task/TaskKanban.js +94 -13
  31. package/dist/components/task/TaskKanban.test.js +36 -0
  32. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  33. package/dist/components/ui/EditableAvatarButton.js +18 -0
  34. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  35. package/dist/components/views/PlanComparisonPage.js +70 -14
  36. package/dist/components/views/PlanComparisonPage.test.js +238 -0
  37. package/dist/components/views/PlansPage.js +127 -36
  38. package/dist/components/views/PlansPage.test.js +294 -12
  39. package/dist/components/views/StandaloneLayout.js +283 -533
  40. package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
  41. package/dist/components/views/panels/PlanningDrawer.js +18 -11
  42. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  43. package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
  44. package/dist/components/views/planningScope.d.ts +20 -0
  45. package/dist/components/views/planningScope.js +25 -0
  46. package/dist/components/views/planningScope.test.d.ts +1 -0
  47. package/dist/components/views/planningScope.test.js +60 -0
  48. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  49. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  50. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  51. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  52. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  53. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  54. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  55. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  56. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  57. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  58. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  59. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  60. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  61. package/dist/core/AiProfileService.d.ts +39 -2
  62. package/dist/core/AiProfileService.js +639 -73
  63. package/dist/core/AiProfileService.test.js +19 -1
  64. package/dist/core/AiProfiles.test.js +566 -7
  65. package/dist/core/AttachmentLinkService.js +57 -15
  66. package/dist/core/AuthIdentityService.d.ts +107 -0
  67. package/dist/core/AuthIdentityService.js +284 -0
  68. package/dist/core/AuthTokenService.d.ts +4 -0
  69. package/dist/core/AuthTokenService.js +34 -6
  70. package/dist/core/AuthTokenService.test.js +3 -3
  71. package/dist/core/EntitlementsPolicy.test.js +2 -2
  72. package/dist/core/GlobalSettingsService.js +115 -6
  73. package/dist/core/PlanEntitlementService.d.ts +23 -2
  74. package/dist/core/PlanEntitlementService.js +343 -57
  75. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  76. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  77. package/dist/core/PlanVersionPolicy.test.js +35 -2
  78. package/dist/core/PlanningEntities.test.js +52 -0
  79. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  80. package/dist/core/SyncReconciliationService.js +2 -65
  81. package/dist/core/SystemAdmin.test.js +42 -42
  82. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  83. package/dist/core/Taskforce.d.ts +225 -14
  84. package/dist/core/Taskforce.js +765 -192
  85. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  86. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  87. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  88. package/dist/core/WorkspaceLifecycleService.js +0 -5
  89. package/dist/core/WorkspacePermissions.test.js +25 -1
  90. package/dist/core/WorkspacePlanMode.test.js +117 -16
  91. package/dist/core/shared.d.ts +1 -1
  92. package/dist/core/shared.js +5 -1
  93. package/dist/core/types.d.ts +68 -2
  94. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
  95. package/dist/hooks/sync/bootstrap.js +2 -0
  96. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  97. package/dist/hooks/sync/controlPlane.js +78 -0
  98. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  99. package/dist/hooks/sync/controlPlane.test.js +121 -0
  100. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  101. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  102. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  103. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  104. package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
  105. package/dist/hooks/sync/orchestratorShared.js +68 -47
  106. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  107. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  108. package/dist/hooks/sync/pullLifecycle.js +24 -0
  109. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  110. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  111. package/dist/hooks/sync/recovery.d.ts +16 -2
  112. package/dist/hooks/sync/recovery.js +171 -53
  113. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  114. package/dist/hooks/sync/recovery.test.js +292 -0
  115. package/dist/hooks/sync/transfers.d.ts +15 -3
  116. package/dist/hooks/sync/transfers.js +179 -62
  117. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  118. package/dist/hooks/sync/transfers.test.js +396 -0
  119. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  120. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  121. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  122. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  123. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  124. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  125. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  126. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  127. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  128. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  129. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  130. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  131. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  132. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  133. package/dist/hooks/useSyncOrchestrator.js +359 -178
  134. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  135. package/dist/hooks/useTaskData.js +8 -3
  136. package/dist/hooks/useTaskData.test.js +45 -0
  137. package/dist/hooks/useTaskforce.d.ts +60 -2
  138. package/dist/hooks/useTaskforce.js +549 -240
  139. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  140. package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
  141. package/dist/hooks/useTaskforce.sync-behavior.test.js +2380 -212
  142. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  143. package/dist/hooks/useWorkspaceSyncController.js +6 -1
  144. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  145. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
  146. package/dist/localization/locales/en-US.d.ts +0 -1
  147. package/dist/localization/locales/en-US.js +0 -1
  148. package/dist/localization/locales/es-419.js +0 -1
  149. package/dist/localization/locales/pt-BR.js +0 -1
  150. package/dist/mcp/documentAssetRegistrar.js +3 -3
  151. package/dist/mcp/runtime.d.ts +2 -0
  152. package/dist/mcp/runtime.js +131 -64
  153. package/dist/mcp/runtime.test.js +291 -12
  154. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  155. package/dist/migrations/taskSchemaMigrations.js +171 -61
  156. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  157. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  158. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  159. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  160. package/dist/resources/templates/workflows/plan.yaml +1 -1
  161. package/dist/resources/templates/workflows/review.yaml +2 -2
  162. package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
  163. package/dist/server/auth/providers/apple.d.ts +33 -0
  164. package/dist/server/auth/providers/apple.js +82 -0
  165. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  166. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  167. package/dist/server/auth/providers/github.d.ts +26 -0
  168. package/dist/server/auth/providers/github.js +86 -0
  169. package/dist/server/auth/providers/google.d.ts +21 -0
  170. package/dist/server/auth/providers/google.js +56 -0
  171. package/dist/server/auth/providers/types.d.ts +21 -0
  172. package/dist/server/auth/providers/types.js +1 -0
  173. package/dist/server/auth.d.ts +2 -0
  174. package/dist/server/auth.js +6 -3
  175. package/dist/server/documentReviewRoutes.test.js +36 -3
  176. package/dist/server/index.cookieProxy.test.js +1 -0
  177. package/dist/server/index.d.ts +12 -0
  178. package/dist/server/index.js +120 -9
  179. package/dist/server/index.rateLimit.test.js +3 -0
  180. package/dist/server/index.test.js +106 -3
  181. package/dist/server/routes/admin.d.ts +3 -0
  182. package/dist/server/routes/admin.js +272 -19
  183. package/dist/server/routes/auth.d.ts +15 -2
  184. package/dist/server/routes/auth.js +994 -57
  185. package/dist/server/routes/authSupport.d.ts +1 -1
  186. package/dist/server/routes/authSupport.js +1 -1
  187. package/dist/server/routes/billing.js +361 -35
  188. package/dist/server/routes/billing.test.js +592 -12
  189. package/dist/server/routes/documents.d.ts +1 -0
  190. package/dist/server/routes/documents.js +172 -70
  191. package/dist/server/routes/shared.d.ts +28 -2
  192. package/dist/server/routes/shared.js +69 -4
  193. package/dist/server/routes/sync.integration.test.js +437 -39
  194. package/dist/server/routes/sync.js +61 -605
  195. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  196. package/dist/server/routes/syncAuxRoutes.js +91 -0
  197. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  198. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  199. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  200. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  201. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  202. package/dist/server/routes/syncPushRoutes.js +212 -0
  203. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  204. package/dist/server/routes/syncRouteGuards.js +67 -0
  205. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  206. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  207. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  208. package/dist/server/routes/syncRouteTypes.js +1 -0
  209. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  210. package/dist/server/routes/syncSnapshotRoutes.js +180 -0
  211. package/dist/server/routes/tasks.js +43 -1
  212. package/dist/server/routes/workspaces.js +36 -5
  213. package/dist/server/routes.js +60 -3
  214. package/dist/server/routes.test.js +1824 -171
  215. package/dist/services/workflowExportSnapshots.test.js +2 -0
  216. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  217. package/dist/shared/aiProfileSeatScope.js +21 -0
  218. package/dist/shared/passwordPolicy.d.ts +13 -0
  219. package/dist/shared/passwordPolicy.js +84 -0
  220. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  221. package/dist/shared/passwordPolicy.test.js +34 -0
  222. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  223. package/dist/storage/workspaceAssetStore.js +132 -44
  224. package/dist/storage/workspaceAssetStore.test.js +93 -6
  225. package/dist/styles/fonts.css +70 -0
  226. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  227. package/dist/sync/collaborationSyncPayload.js +17 -11
  228. package/dist/sync/collaborationSyncPayload.test.js +13 -1
  229. package/dist/sync/contentSyncState.d.ts +4 -8
  230. package/dist/sync/contentSyncState.js +17 -70
  231. package/dist/sync/syncApplyHandlers.d.ts +7 -0
  232. package/dist/sync/syncApplyHandlers.js +28 -13
  233. package/dist/sync/syncApplyHandlers.test.js +70 -3
  234. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  235. package/dist/sync/syncFieldSemantics.js +55 -0
  236. package/dist/sync/syncResourceAdapters.d.ts +44 -0
  237. package/dist/sync/syncResourceAdapters.js +77 -0
  238. package/dist/sync/syncService.d.ts +5 -0
  239. package/dist/sync/syncService.js +52 -0
  240. package/dist/sync/syncService.test.js +57 -3
  241. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  242. package/dist/sync/taskSyncChangeKey.js +143 -0
  243. package/dist/sync/taskSyncPayload.js +3 -8
  244. package/dist/sync/workspacePullFeed.js +4 -3
  245. package/dist/sync/workspacePullFeed.test.js +43 -0
  246. package/dist/sync/workspaceRepair.js +5 -2
  247. package/dist/sync/workspaceSyncModel.d.ts +45 -0
  248. package/dist/sync/workspaceSyncModel.js +183 -28
  249. package/dist/sync/workspaceSyncModel.test.js +291 -12
  250. package/dist/sync/workspaceSyncState.d.ts +17 -0
  251. package/dist/sync/workspaceSyncState.js +2 -1
  252. package/dist/sync/workspaceSyncSurface.js +3 -8
  253. package/dist/types.d.ts +17 -0
  254. package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
  255. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BG6P_GVW.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
  256. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  257. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  258. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  259. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  260. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
  261. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
  262. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-9E6ParrY.js} +1 -1
  263. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  264. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  265. package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
  266. package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
  267. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  268. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  269. package/dist/ui/assets/{TaskSettings-ln0aF7xc.js → TaskSettings-DZX4jk7e.js} +1 -1
  270. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-BA7jcEpH.js} +1 -1
  271. package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
  272. package/dist/ui/assets/index-C6k75jr8.js +6 -0
  273. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-DuEd_65c.js} +1 -1
  274. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  275. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  276. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  277. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  278. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  279. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  280. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  281. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  282. package/dist/ui/fonts/Noir_medium.otf +0 -0
  283. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  284. package/dist/ui/fonts/Noir_regular.otf +0 -0
  285. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  286. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  287. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  288. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  289. package/dist/ui/index.html +3 -3
  290. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  291. package/dist/utils/assignees.d.ts +2 -0
  292. package/dist/utils/assignees.js +2 -0
  293. package/dist/utils/avatarUpload.d.ts +27 -0
  294. package/dist/utils/avatarUpload.js +132 -0
  295. package/dist/utils/avatarUpload.test.d.ts +1 -0
  296. package/dist/utils/avatarUpload.test.js +40 -0
  297. package/dist/utils/bootstrapTrace.d.ts +7 -0
  298. package/dist/utils/bootstrapTrace.js +29 -0
  299. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  300. package/dist/utils/contextAssetEvents.d.ts +2 -0
  301. package/dist/utils/syncEventDetails.d.ts +3 -0
  302. package/dist/utils/syncEventDetails.js +64 -0
  303. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  304. package/dist/utils/syncEventDetails.test.js +23 -0
  305. package/dist/utils/syncEventPresentation.d.ts +15 -0
  306. package/dist/utils/syncEventPresentation.js +129 -0
  307. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  308. package/dist/utils/syncEventPresentation.test.js +64 -0
  309. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  310. package/dist/utils/syncStatusPresentation.js +217 -0
  311. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  312. package/dist/utils/syncStatusPresentation.test.js +164 -0
  313. package/dist/utils/taskActivity.d.ts +2 -0
  314. package/dist/utils/taskActivity.js +228 -22
  315. package/dist/utils/taskActivity.test.js +119 -8
  316. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  317. package/dist/utils/workspaceSyncPresentation.js +68 -2
  318. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  319. package/package.json +3 -2
  320. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  321. package/scripts/run-e2e-realtime.sh +17 -3
  322. package/scripts/run-smoke.sh +17 -5
  323. package/scripts/run-soak.sh +17 -5
  324. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  325. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  326. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  327. package/src/resources/templates/workflows/plan.yaml +1 -1
  328. package/src/resources/templates/workflows/review.yaml +2 -2
  329. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  330. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  331. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  332. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  333. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  334. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  335. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  336. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  337. package/scripts/migrate-planning-model.ts +0 -233
@@ -1,6 +1,42 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
- import { ensureBillingWebhookAuthExclusion, ensurePublicSiteConfigAuthExclusion, isBillingWebhookPath, rewriteProxySetCookieForLocalHost, resolveEffectiveRequestUserId, resolveAuthorizedWorkspaceAccess, shouldRewriteProxySetCookieDomain, shouldApplyAuthRateLimitForPath, shouldProxyCloudPath, writeJsonErrorIfPossible } from './index.js';
2
+ import { ensureBillingWebhookAuthExclusion, ensurePublicSiteConfigAuthExclusion, configureHttpServerTimeouts, isBillingWebhookPath, rewriteProxySetCookieForLocalHost, resolveHttpServerTimeoutSettings, resolveEffectiveRequestUserId, resolveAuthorizedWorkspaceAccess, resolveDisabledAccessError, shouldApplyReadOnlyMutationGuard, shouldRewriteProxySetCookieDomain, shouldApplyAuthRateLimitForPath, shouldProxyCloudPath, writeJsonErrorIfPossible } from './index.js';
3
3
  describe('server index helper policies', () => {
4
+ it('uses proxy-friendly HTTP server timeouts by default', () => {
5
+ const settings = resolveHttpServerTimeoutSettings({});
6
+ expect(settings.keepAliveTimeoutMs).toBe(65_000);
7
+ expect(settings.headersTimeoutMs).toBe(70_000);
8
+ expect(settings.requestTimeoutMs).toBe(120_000);
9
+ });
10
+ it('keeps headers timeout above keep-alive timeout for reverse proxies', () => {
11
+ const settings = resolveHttpServerTimeoutSettings({
12
+ TASKFORCE_HTTP_KEEP_ALIVE_TIMEOUT_MS: '80000',
13
+ TASKFORCE_HTTP_HEADERS_TIMEOUT_MS: '1000',
14
+ TASKFORCE_HTTP_REQUEST_TIMEOUT_MS: '150000'
15
+ });
16
+ expect(settings.keepAliveTimeoutMs).toBe(80_000);
17
+ expect(settings.headersTimeoutMs).toBe(85_000);
18
+ expect(settings.requestTimeoutMs).toBe(150_000);
19
+ });
20
+ it('applies resolved HTTP server timeouts to the Node server', () => {
21
+ const server = {
22
+ keepAliveTimeout: 0,
23
+ headersTimeout: 0,
24
+ requestTimeout: 0
25
+ };
26
+ const settings = configureHttpServerTimeouts(server, {
27
+ TASKFORCE_HTTP_KEEP_ALIVE_TIMEOUT_MS: '61000',
28
+ TASKFORCE_HTTP_HEADERS_TIMEOUT_MS: '66000',
29
+ TASKFORCE_HTTP_REQUEST_TIMEOUT_MS: '90000'
30
+ });
31
+ expect(settings).toEqual({
32
+ keepAliveTimeoutMs: 61_000,
33
+ headersTimeoutMs: 66_000,
34
+ requestTimeoutMs: 90_000
35
+ });
36
+ expect(server.keepAliveTimeout).toBe(61_000);
37
+ expect(server.headersTimeout).toBe(66_000);
38
+ expect(server.requestTimeout).toBe(90_000);
39
+ });
4
40
  it('adds webhook path to auth exclusions without duplicates', () => {
5
41
  const base = ['/api/taskforce/health', '/api/taskforce/auth'];
6
42
  const next = ensureBillingWebhookAuthExclusion(base);
@@ -82,6 +118,12 @@ describe('shouldProxyCloudPath', () => {
82
118
  expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/pull')).toBe(true);
83
119
  expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/push')).toBe(true);
84
120
  });
121
+ it('proxies cloud-owned user avatar context paths only', () => {
122
+ expect(shouldProxyCloudPath('/api/taskforce/context/users%2Fuser-1%2Fprofile%2Favatar%2Favatar.webp')).toBe(true);
123
+ expect(shouldProxyCloudPath('/api/taskforce/context/users/user-1/profile/avatar/avatar.webp')).toBe(true);
124
+ expect(shouldProxyCloudPath('/api/taskforce/context/workspaces%2Fworkspace-1%2Fassets%2Fimages%2Fasset.png')).toBe(false);
125
+ expect(shouldProxyCloudPath('/api/taskforce/context/users%2Fuser-1%2Fexports%2Favatar.webp')).toBe(false);
126
+ });
85
127
  it('proxies admin, billing, and auth routes', () => {
86
128
  expect(shouldProxyCloudPath('/api/taskforce/admin/users')).toBe(true);
87
129
  expect(shouldProxyCloudPath('/api/taskforce/billing/plans')).toBe(true);
@@ -91,6 +133,11 @@ describe('shouldProxyCloudPath', () => {
91
133
  expect(shouldProxyCloudPath('/api/taskforce/public/site-config')).toBe(true);
92
134
  expect(shouldProxyCloudPath('/api/taskforce/public/site-config-script')).toBe(true);
93
135
  });
136
+ it('keeps AI profile workspace mutations local so workspace sync owns replication', () => {
137
+ expect(shouldProxyCloudPath('/api/taskforce/workspace/ai-profiles/avatar')).toBe(false);
138
+ expect(shouldProxyCloudPath('/api/taskforce/workspace/ai-profiles/archive')).toBe(false);
139
+ expect(shouldProxyCloudPath('/api/taskforce/workspace/ai-profiles/merge')).toBe(false);
140
+ });
94
141
  });
95
142
  describe('proxy cookie helpers', () => {
96
143
  it('rewrites mismatched cookie domains for non-loopback local hosts', () => {
@@ -126,7 +173,8 @@ describe('resolveAuthorizedWorkspaceAccess', () => {
126
173
  userId: 'user-1',
127
174
  workspaceId: 'workspace-personal',
128
175
  role: 'member',
129
- systemRole: 'user'
176
+ systemRole: 'user',
177
+ identityId: null
130
178
  }, 'workspace-team');
131
179
  expect(core.resolveUserAccess).toHaveBeenCalledWith('user-1', 'workspace-team', 'member');
132
180
  expect(next.workspaceId).toBe('workspace-team');
@@ -154,12 +202,67 @@ describe('resolveAuthorizedWorkspaceAccess', () => {
154
202
  userId: 'user-1',
155
203
  workspaceId: 'workspace-personal',
156
204
  role: 'member',
157
- systemRole: 'user'
205
+ systemRole: 'user',
206
+ identityId: null
158
207
  }, 'workspace-team');
159
208
  expect(core.resolveUserAccess).toHaveBeenNthCalledWith(1, 'user-1', 'workspace-team', 'member');
160
209
  expect(core.resolveUserAccess).toHaveBeenNthCalledWith(2, 'user-1', 'workspace-personal', 'member');
161
210
  expect(next.workspaceId).toBe('workspace-personal');
162
211
  });
212
+ it('preserves account and workspace disabled scopes separately', () => {
213
+ const core = {
214
+ resolveUserAccess: vi.fn((userId, workspaceId, role) => ({
215
+ userId,
216
+ workspaceId,
217
+ role,
218
+ permissionMode: 'read-write',
219
+ disabled: true,
220
+ accountDisabled: false,
221
+ workspaceDisabled: true,
222
+ betaAccess: true
223
+ })),
224
+ getSystemRole: vi.fn(() => 'user')
225
+ };
226
+ const next = resolveAuthorizedWorkspaceAccess(core, {
227
+ authenticated: true,
228
+ sessionId: 'session-1',
229
+ userId: 'user-1',
230
+ workspaceId: 'workspace-shared',
231
+ role: 'member',
232
+ systemRole: 'user',
233
+ identityId: null
234
+ }, 'workspace-shared');
235
+ expect(next.disabled).toBe(true);
236
+ expect(next.accountDisabled).toBe(false);
237
+ expect(next.workspaceDisabled).toBe(true);
238
+ });
239
+ });
240
+ describe('resolveDisabledAccessError', () => {
241
+ it('blocks globally disabled accounts outside auth routes', () => {
242
+ expect(resolveDisabledAccessError({ disabled: true, accountDisabled: true }, '/api/taskforce/workspaces')).toBe('Forbidden: user account is disabled.');
243
+ expect(resolveDisabledAccessError({ disabled: true, accountDisabled: true }, '/api/taskforce/auth/session')).toBeNull();
244
+ });
245
+ it('allows workspace-disabled sessions to reach recovery routes only', () => {
246
+ const access = { disabled: true, accountDisabled: false, workspaceDisabled: true };
247
+ expect(resolveDisabledAccessError(access, '/api/taskforce/workspaces')).toBeNull();
248
+ expect(resolveDisabledAccessError(access, '/api/taskforce/session/workspace')).toBeNull();
249
+ expect(resolveDisabledAccessError(access, '/api/taskforce/tasks')).toBe('Forbidden: workspace access is not active for this user.');
250
+ });
251
+ });
252
+ describe('shouldApplyReadOnlyMutationGuard', () => {
253
+ it('guards ordinary Taskforce mutations', () => {
254
+ expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/tasks')).toBe(true);
255
+ expect(shouldApplyReadOnlyMutationGuard('PATCH', '/api/taskforce/task/task-1')).toBe(true);
256
+ expect(shouldApplyReadOnlyMutationGuard('DELETE', '/api/taskforce/task/task-1')).toBe(true);
257
+ });
258
+ it('allows session workspace switching through route-level access validation', () => {
259
+ expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/session/workspace')).toBe(false);
260
+ });
261
+ it('does not guard non-mutations, auth routes, or health checks', () => {
262
+ expect(shouldApplyReadOnlyMutationGuard('GET', '/api/taskforce/tasks')).toBe(false);
263
+ expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/auth/login')).toBe(false);
264
+ expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/health')).toBe(false);
265
+ });
163
266
  });
164
267
  describe('resolveEffectiveRequestUserId', () => {
165
268
  it('prefers the authenticated access user when present', () => {
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * Routes registered here:
9
9
  * GET/POST /api/platform/plans
10
+ * GET /api/platform/onboarding-plan-options
10
11
  * GET/POST /api/platform/plans/:planId/versions
11
12
  * GET /api/platform/features
12
13
  * GET/PATCH /api/platform/plans/:planId/versions/:versionId/features
@@ -23,6 +24,8 @@
23
24
  * GET/POST /api/taskforce/admin/email-settings
24
25
  * GET /api/taskforce/admin/email-settings/health
25
26
  * POST /api/taskforce/admin/email-settings/test
27
+ * GET/POST /api/taskforce/admin/auth-provider-settings
28
+ * GET /api/taskforce/admin/auth-provider-settings/health
26
29
  * GET/POST /api/taskforce/admin/system-settings
27
30
  * GET/POST /api/taskforce/admin/object-storage-settings
28
31
  * GET /api/taskforce/admin/object-storage-telemetry
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * Routes registered here:
9
9
  * GET/POST /api/platform/plans
10
+ * GET /api/platform/onboarding-plan-options
10
11
  * GET/POST /api/platform/plans/:planId/versions
11
12
  * GET /api/platform/features
12
13
  * GET/PATCH /api/platform/plans/:planId/versions/:versionId/features
@@ -23,6 +24,8 @@
23
24
  * GET/POST /api/taskforce/admin/email-settings
24
25
  * GET /api/taskforce/admin/email-settings/health
25
26
  * POST /api/taskforce/admin/email-settings/test
27
+ * GET/POST /api/taskforce/admin/auth-provider-settings
28
+ * GET /api/taskforce/admin/auth-provider-settings/health
26
29
  * GET/POST /api/taskforce/admin/system-settings
27
30
  * GET/POST /api/taskforce/admin/object-storage-settings
28
31
  * GET /api/taskforce/admin/object-storage-telemetry
@@ -68,6 +71,7 @@ import { getTaskforceCloudEnvironmentConfig, resolveTaskforceCloudEnvironmentFro
68
71
  import { buildCloudMcpRuntimeAuth, resolveErrorStatusCode } from './shared.js';
69
72
  import { buildAuthBehavior, buildSiteBehavior, getRouteEmailConfig, resolvePublicAuthBaseUrl, resolveRuntimeConfigBaseUrl, } from './authSupport.js';
70
73
  import { applyWorkspaceRepair, scanWorkspaceRepair } from '../../sync/workspaceRepair.js';
74
+ import { testMintAppleClientSecret } from '../auth/providers/appleClientSecret.js';
71
75
  // === SECTION: Registration ===
72
76
  export function registerAdminRoutes(deps) {
73
77
  const { addRoute, core, context, requestWorkspaceId, requestUserId, requestRole, requestHeader, requestRuntimeMode, ensureAppAccess, ensureAdminRole, ensureCloudPlanData, requireCloudRuntime, resolveAuthenticatedUserId, resolveAdminRequestActor, auditAdminAction, resolveEffectiveObjectStorageConfig, getObjectStorageClient, taskAssetStore, normalizeDocumentPathInput, ensureWithinDir, getAssetMetadata, markAssetDeleted, clearAssetDeleted, removeAssetMetadata, getStorageTelemetry, appendStorageTelemetry, getStorageIntegrityHistory, appendStorageIntegrityHistory, getDocumentPurgeHistory, appendDocumentPurgeHistory, getCollaborationTelemetry, isCollaborationPolicyEnabled, normalizeMcpOauthScopeString, MCP_TOKEN_SCOPE_VALUES, MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS, MCP_WRITE_RATE_LIMIT_ENABLED, MCP_WRITE_RATE_LIMIT_ACTOR_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WORKSPACE_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WINDOW_MS, } = deps;
@@ -571,6 +575,18 @@ export function registerAdminRoutes(deps) {
571
575
  res.writeHead(200, { 'Content-Type': 'application/json' });
572
576
  res.end(JSON.stringify({ success: true, plans }));
573
577
  });
578
+ addRoute('GET', '/api/platform/onboarding-plan-options', async (req, res) => {
579
+ if (!ensureCloudPlanData(req, res))
580
+ return;
581
+ if (!ensureAdminRole(req)) {
582
+ res.writeHead(403, { 'Content-Type': 'application/json' });
583
+ res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
584
+ return;
585
+ }
586
+ const options = core.listOnboardingPlanOptions();
587
+ res.writeHead(200, { 'Content-Type': 'application/json' });
588
+ res.end(JSON.stringify({ success: true, options }));
589
+ });
574
590
  addRoute('POST', '/api/platform/plans', async (req, res) => {
575
591
  if (!ensureCloudPlanData(req, res))
576
592
  return;
@@ -580,10 +596,19 @@ export function registerAdminRoutes(deps) {
580
596
  return;
581
597
  }
582
598
  const body = await parseJsonBody(req);
599
+ const description = body?.description === undefined || body?.description === null
600
+ ? body?.description
601
+ : String(body.description);
602
+ if (typeof description === 'string' && description.trim().length > 240) {
603
+ res.writeHead(400, { 'Content-Type': 'application/json' });
604
+ res.end(JSON.stringify({ success: false, error: 'Plan description must be 240 characters or fewer.', code: 'PLAN_DESCRIPTION_TOO_LONG' }));
605
+ return;
606
+ }
583
607
  try {
584
608
  const plan = core.upsertPlanCatalog({
585
609
  planId: body?.planId,
586
610
  displayName: body?.displayName,
611
+ description,
587
612
  status: body?.status,
588
613
  initialMode: body?.initialMode,
589
614
  initialTrialDays: body?.initialTrialDays === undefined ? undefined : Number(body?.initialTrialDays),
@@ -639,6 +664,62 @@ export function registerAdminRoutes(deps) {
639
664
  res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to list feature catalog'), code: error?.code }));
640
665
  }
641
666
  });
667
+ addRoute('PATCH', '/api/platform/features/:featureKey', async (req, res, params) => {
668
+ if (!ensureCloudPlanData(req, res))
669
+ return;
670
+ if (!ensureAdminRole(req)) {
671
+ res.writeHead(403, { 'Content-Type': 'application/json' });
672
+ res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
673
+ return;
674
+ }
675
+ const body = await parseJsonBody(req);
676
+ const featureKey = String(params.featureKey || '').trim();
677
+ const publicLabel = body?.publicLabel === undefined || body?.publicLabel === null
678
+ ? body?.publicLabel
679
+ : String(body.publicLabel);
680
+ const publicDescription = body?.publicDescription === undefined || body?.publicDescription === null
681
+ ? body?.publicDescription
682
+ : String(body.publicDescription);
683
+ const publicDisplayOrder = body?.publicDisplayOrder === undefined || body?.publicDisplayOrder === null || body?.publicDisplayOrder === ''
684
+ ? null
685
+ : Math.max(0, Math.floor(Number(body.publicDisplayOrder)));
686
+ if (typeof publicLabel === 'string' && publicLabel.trim().length > 120) {
687
+ res.writeHead(400, { 'Content-Type': 'application/json' });
688
+ res.end(JSON.stringify({ success: false, error: 'Public label must be 120 characters or fewer.', code: 'PLAN_FEATURE_PUBLIC_LABEL_TOO_LONG' }));
689
+ return;
690
+ }
691
+ if (typeof publicDescription === 'string' && publicDescription.trim().length > 280) {
692
+ res.writeHead(400, { 'Content-Type': 'application/json' });
693
+ res.end(JSON.stringify({ success: false, error: 'Public description must be 280 characters or fewer.', code: 'PLAN_FEATURE_PUBLIC_DESCRIPTION_TOO_LONG' }));
694
+ return;
695
+ }
696
+ if (publicDisplayOrder !== null && !Number.isFinite(publicDisplayOrder)) {
697
+ res.writeHead(400, { 'Content-Type': 'application/json' });
698
+ res.end(JSON.stringify({ success: false, error: 'Public display order must be a non-negative number.', code: 'PLAN_FEATURE_PUBLIC_DISPLAY_ORDER_INVALID' }));
699
+ return;
700
+ }
701
+ try {
702
+ const feature = core.updatePlanFeatureCatalogMarketingCopy({
703
+ featureKey,
704
+ publicLabel,
705
+ publicDescription,
706
+ publicDisplayOrder
707
+ });
708
+ auditAdminAction(req, 'platform-plan-feature-catalog-updated', {
709
+ featureKey,
710
+ publicLabel: feature.publicLabel ?? null,
711
+ publicDescription: feature.publicDescription ?? null,
712
+ publicDisplayOrder: feature.publicDisplayOrder ?? null
713
+ });
714
+ res.writeHead(200, { 'Content-Type': 'application/json' });
715
+ res.end(JSON.stringify({ success: true, feature }));
716
+ }
717
+ catch (error) {
718
+ const statusCode = resolveErrorStatusCode(error, 400);
719
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
720
+ res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to update feature catalog entry'), code: error?.code }));
721
+ }
722
+ });
642
723
  addRoute('POST', '/api/platform/plans/:planId/versions', async (req, res, params) => {
643
724
  if (!ensureCloudPlanData(req, res))
644
725
  return;
@@ -855,9 +936,23 @@ export function registerAdminRoutes(deps) {
855
936
  res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
856
937
  return;
857
938
  }
858
- const users = core.listSystemUsers();
939
+ const reqUrl = new URL(req.url || '/api/taskforce/admin/system-users', 'http://localhost');
940
+ const query = String(reqUrl.searchParams.get('q') || '').trim();
941
+ const sort = reqUrl.searchParams.get('sort') === 'oldest' ? 'oldest' : 'newest';
942
+ const limitRaw = Number(reqUrl.searchParams.get('limit') || '50');
943
+ const offsetRaw = Number(reqUrl.searchParams.get('offset') || '0');
944
+ const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(200, Math.floor(limitRaw))) : 50;
945
+ const offset = Number.isFinite(offsetRaw) ? Math.max(0, Math.floor(offsetRaw)) : 0;
946
+ const total = core.countSystemUsers({ query });
947
+ const users = core.listSystemUsers({ query, sort, limit, offset });
859
948
  res.writeHead(200, { 'Content-Type': 'application/json' });
860
- res.end(JSON.stringify({ users }));
949
+ res.end(JSON.stringify({
950
+ users,
951
+ total,
952
+ limit,
953
+ offset,
954
+ hasMore: offset + users.length < total
955
+ }));
861
956
  });
862
957
  // GET /api/taskforce/admin/audit-logs - Read recent admin audit events
863
958
  addRoute('GET', '/api/taskforce/admin/audit-logs', async (req, res) => {
@@ -1136,7 +1231,7 @@ export function registerAdminRoutes(deps) {
1136
1231
  }
1137
1232
  const globalSettings = core.getGlobalSettings();
1138
1233
  const authSettings = globalSettings.auth || {};
1139
- const billingSettings = globalSettings.billing || {};
1234
+ const onboardingPolicy = core.getOnboardingPolicy();
1140
1235
  const effective = buildAuthBehavior(core);
1141
1236
  res.writeHead(200, { 'Content-Type': 'application/json' });
1142
1237
  res.end(JSON.stringify({
@@ -1147,14 +1242,8 @@ export function registerAdminRoutes(deps) {
1147
1242
  ...(typeof authSettings.authRateLimitEnabled === 'boolean' ? { authRateLimitEnabled: authSettings.authRateLimitEnabled } : {}),
1148
1243
  ...(typeof authSettings.authRateLimitMaxRequests === 'number' ? { authRateLimitMaxRequests: authSettings.authRateLimitMaxRequests } : {}),
1149
1244
  ...(typeof authSettings.authRateLimitWindowMs === 'number' ? { authRateLimitWindowMs: authSettings.authRateLimitWindowMs } : {}),
1150
- ...((billingSettings.onboardingPolicy?.mode === 'require_plan_selection'
1151
- || billingSettings.onboardingPolicy?.mode === 'auto_grant_free'
1152
- || billingSettings.onboardingPolicy?.mode === 'auto_start_trial')
1153
- ? { onboardingPolicyMode: billingSettings.onboardingPolicy.mode }
1154
- : {}),
1155
- ...(typeof billingSettings.onboardingPolicy?.planVersionId === 'string' || billingSettings.onboardingPolicy?.planVersionId === null
1156
- ? { onboardingPlanVersionId: billingSettings.onboardingPolicy?.planVersionId ?? null }
1157
- : {})
1245
+ onboardingPolicyMode: onboardingPolicy.mode,
1246
+ onboardingPlanId: onboardingPolicy.planId
1158
1247
  },
1159
1248
  status: {
1160
1249
  allowSelfRegistration: effective.allowSelfRegistration,
@@ -1164,7 +1253,7 @@ export function registerAdminRoutes(deps) {
1164
1253
  authRateLimitMaxRequests: effective.authRateLimitMaxRequests,
1165
1254
  authRateLimitWindowMs: effective.authRateLimitWindowMs,
1166
1255
  onboardingPolicyMode: effective.onboardingPolicyMode,
1167
- onboardingPlanVersionId: effective.onboardingPlanVersionId
1256
+ onboardingPlanId: effective.onboardingPlanId
1168
1257
  }
1169
1258
  }));
1170
1259
  });
@@ -1471,7 +1560,8 @@ export function registerAdminRoutes(deps) {
1471
1560
  workspaceAssetStore.deleteLink({
1472
1561
  workspaceId,
1473
1562
  assetId,
1474
- taskId: link.taskId,
1563
+ targetType: link.targetType,
1564
+ targetId: link.targetId,
1475
1565
  role: link.role
1476
1566
  });
1477
1567
  }
@@ -1796,14 +1886,14 @@ export function registerAdminRoutes(deps) {
1796
1886
  : body?.onboardingPolicyMode === 'require_plan_selection'
1797
1887
  ? 'require_plan_selection'
1798
1888
  : currentOnboardingPolicy.mode;
1799
- const onboardingPlanVersionId = typeof body?.onboardingPlanVersionId === 'string'
1800
- ? body.onboardingPlanVersionId.trim() || null
1801
- : body?.onboardingPlanVersionId === null
1889
+ const onboardingPlanId = typeof body?.onboardingPlanId === 'string'
1890
+ ? body.onboardingPlanId.trim().toLowerCase() || null
1891
+ : body?.onboardingPlanId === null
1802
1892
  ? null
1803
- : currentOnboardingPolicy.planVersionId;
1893
+ : currentOnboardingPolicy.planId;
1804
1894
  const validation = core.validateOnboardingPolicy({
1805
1895
  mode: onboardingPolicyMode,
1806
- planVersionId: onboardingPlanVersionId
1896
+ planId: onboardingPlanId
1807
1897
  });
1808
1898
  if (!validation.valid) {
1809
1899
  res.writeHead(400, { 'Content-Type': 'application/json' });
@@ -1824,7 +1914,7 @@ export function registerAdminRoutes(deps) {
1824
1914
  billing: {
1825
1915
  onboardingPolicy: {
1826
1916
  mode: onboardingPolicyMode,
1827
- planVersionId: onboardingPlanVersionId ?? null
1917
+ planId: onboardingPlanId ?? null
1828
1918
  }
1829
1919
  }
1830
1920
  });
@@ -1914,6 +2004,169 @@ export function registerAdminRoutes(deps) {
1914
2004
  res.writeHead(200, { 'Content-Type': 'application/json' });
1915
2005
  res.end(JSON.stringify({ success: true, provider: result.provider, messageId: result.messageId || null }));
1916
2006
  });
2007
+ // === SECTION: Admin auth provider settings ===
2008
+ // GET /api/taskforce/admin/auth-provider-settings - Read OAuth provider config (secrets masked)
2009
+ addRoute('GET', '/api/taskforce/admin/auth-provider-settings', async (req, res) => {
2010
+ if (!ensureAdminRole(req)) {
2011
+ res.writeHead(403, { 'Content-Type': 'application/json' });
2012
+ res.end(JSON.stringify({ error: 'Forbidden: admin role required.' }));
2013
+ return;
2014
+ }
2015
+ if (context.authConfig?.runtimeMode !== 'cloud') {
2016
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2017
+ res.end(JSON.stringify({
2018
+ status: {
2019
+ google: { enabled: false, clientId: '', hasClientSecret: false, ready: false },
2020
+ github: { enabled: false, clientId: '', hasClientSecret: false, ready: false },
2021
+ apple: { enabled: false, servicesId: '', teamId: '', keyId: '', hasPrivateKeyPem: false, ready: false }
2022
+ }
2023
+ }));
2024
+ return;
2025
+ }
2026
+ const providers = core.getGlobalSettings().oauthProviders || {};
2027
+ const g = providers.google || {};
2028
+ const gh = providers.github || {};
2029
+ const ap = providers.apple || {};
2030
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2031
+ res.end(JSON.stringify({
2032
+ status: {
2033
+ google: {
2034
+ enabled: g.enabled === true,
2035
+ clientId: String(g.clientId || ''),
2036
+ hasClientSecret: typeof g.clientSecret === 'string' && g.clientSecret.trim().length > 0,
2037
+ ready: g.enabled === true && Boolean(g.clientId) && typeof g.clientSecret === 'string' && g.clientSecret.trim().length > 0
2038
+ },
2039
+ github: {
2040
+ enabled: gh.enabled === true,
2041
+ clientId: String(gh.clientId || ''),
2042
+ hasClientSecret: typeof gh.clientSecret === 'string' && gh.clientSecret.trim().length > 0,
2043
+ ready: gh.enabled === true && Boolean(gh.clientId) && typeof gh.clientSecret === 'string' && gh.clientSecret.trim().length > 0
2044
+ },
2045
+ apple: {
2046
+ enabled: ap.enabled === true,
2047
+ servicesId: String(ap.servicesId || ''),
2048
+ teamId: String(ap.teamId || ''),
2049
+ keyId: String(ap.keyId || ''),
2050
+ hasPrivateKeyPem: typeof ap.privateKeyPem === 'string' && ap.privateKeyPem.trim().length > 0,
2051
+ ready: ap.enabled === true && Boolean(ap.servicesId) && Boolean(ap.teamId) && Boolean(ap.keyId)
2052
+ && typeof ap.privateKeyPem === 'string' && ap.privateKeyPem.trim().length > 0
2053
+ }
2054
+ }
2055
+ }));
2056
+ });
2057
+ // GET /api/taskforce/admin/auth-provider-settings/health - Per-provider readiness check
2058
+ addRoute('GET', '/api/taskforce/admin/auth-provider-settings/health', async (req, res) => {
2059
+ if (!ensureAdminRole(req)) {
2060
+ res.writeHead(403, { 'Content-Type': 'application/json' });
2061
+ res.end(JSON.stringify({ error: 'Forbidden: admin role required.' }));
2062
+ return;
2063
+ }
2064
+ const providers = core.getGlobalSettings().oauthProviders || {};
2065
+ const g = providers.google || {};
2066
+ const gh = providers.github || {};
2067
+ const ap = providers.apple || {};
2068
+ const googleReady = g.enabled === true && Boolean(g.clientId) && typeof g.clientSecret === 'string' && g.clientSecret.trim().length > 0;
2069
+ const googleReason = !g.enabled
2070
+ ? 'Provider disabled.'
2071
+ : !g.clientId ? 'Missing Client ID.'
2072
+ : !googleReady ? 'Missing Client Secret.'
2073
+ : null;
2074
+ const githubReady = gh.enabled === true && Boolean(gh.clientId) && typeof gh.clientSecret === 'string' && gh.clientSecret.trim().length > 0;
2075
+ const githubReason = !gh.enabled
2076
+ ? 'Provider disabled.'
2077
+ : !gh.clientId ? 'Missing Client ID.'
2078
+ : !githubReady ? 'Missing Client Secret.'
2079
+ : null;
2080
+ const appleBaseReady = ap.enabled === true && Boolean(ap.servicesId) && Boolean(ap.teamId) && Boolean(ap.keyId)
2081
+ && typeof ap.privateKeyPem === 'string' && ap.privateKeyPem.trim().length > 0;
2082
+ let appleSecretMintOk;
2083
+ let appleReason = null;
2084
+ if (!ap.enabled) {
2085
+ appleReason = 'Provider disabled.';
2086
+ }
2087
+ else if (!ap.servicesId || !ap.teamId || !ap.keyId) {
2088
+ appleReason = 'Missing Services ID, Team ID, or Key ID.';
2089
+ }
2090
+ else if (!appleBaseReady) {
2091
+ appleReason = 'Missing private key PEM.';
2092
+ }
2093
+ else {
2094
+ // Attempt a test-mint to verify the key is valid
2095
+ const mintResult = testMintAppleClientSecret({
2096
+ teamId: ap.teamId,
2097
+ servicesId: ap.servicesId,
2098
+ keyId: ap.keyId,
2099
+ privateKeyPem: ap.privateKeyPem
2100
+ });
2101
+ appleSecretMintOk = mintResult.ok;
2102
+ if (!mintResult.ok) {
2103
+ appleReason = `Client-secret JWT mint failed: ${mintResult.error}`;
2104
+ }
2105
+ }
2106
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2107
+ res.end(JSON.stringify({
2108
+ success: true,
2109
+ health: {
2110
+ google: { enabled: g.enabled === true, ready: googleReady, reason: googleReason },
2111
+ github: { enabled: gh.enabled === true, ready: githubReady, reason: githubReason },
2112
+ apple: {
2113
+ enabled: ap.enabled === true,
2114
+ ready: appleBaseReady && appleSecretMintOk !== false,
2115
+ reason: appleReason,
2116
+ ...(typeof appleSecretMintOk === 'boolean' ? { secretMintOk: appleSecretMintOk } : {})
2117
+ }
2118
+ }
2119
+ }));
2120
+ });
2121
+ // POST /api/taskforce/admin/auth-provider-settings - Save OAuth provider config (including secrets)
2122
+ addRoute('POST', '/api/taskforce/admin/auth-provider-settings', async (req, res) => {
2123
+ if (!ensureAdminRole(req)) {
2124
+ res.writeHead(403, { 'Content-Type': 'application/json' });
2125
+ res.end(JSON.stringify({ error: 'Forbidden: admin role required.' }));
2126
+ return;
2127
+ }
2128
+ if (!requireCloudRuntime(res, 'Auth provider settings'))
2129
+ return;
2130
+ const body = await parseJsonBody(req);
2131
+ const update = {};
2132
+ if (body?.google && typeof body.google === 'object') {
2133
+ const g = body.google;
2134
+ update.google = {
2135
+ ...(typeof g.enabled === 'boolean' ? { enabled: g.enabled } : {}),
2136
+ ...(typeof g.clientId === 'string' ? { clientId: g.clientId } : {}),
2137
+ ...(typeof g.clientSecret === 'string' ? { clientSecret: g.clientSecret } : {})
2138
+ };
2139
+ }
2140
+ if (body?.github && typeof body.github === 'object') {
2141
+ const gh = body.github;
2142
+ update.github = {
2143
+ ...(typeof gh.enabled === 'boolean' ? { enabled: gh.enabled } : {}),
2144
+ ...(typeof gh.clientId === 'string' ? { clientId: gh.clientId } : {}),
2145
+ ...(typeof gh.clientSecret === 'string' ? { clientSecret: gh.clientSecret } : {})
2146
+ };
2147
+ }
2148
+ if (body?.apple && typeof body.apple === 'object') {
2149
+ const ap = body.apple;
2150
+ update.apple = {
2151
+ ...(typeof ap.enabled === 'boolean' ? { enabled: ap.enabled } : {}),
2152
+ ...(typeof ap.servicesId === 'string' ? { servicesId: ap.servicesId } : {}),
2153
+ ...(typeof ap.teamId === 'string' ? { teamId: ap.teamId } : {}),
2154
+ ...(typeof ap.keyId === 'string' ? { keyId: ap.keyId } : {}),
2155
+ ...(typeof ap.privateKeyPem === 'string' ? { privateKeyPem: ap.privateKeyPem } : {})
2156
+ };
2157
+ }
2158
+ if (Object.keys(update).length === 0) {
2159
+ res.writeHead(400, { 'Content-Type': 'application/json' });
2160
+ res.end(JSON.stringify({ error: 'No provider settings provided.' }));
2161
+ return;
2162
+ }
2163
+ core.updateGlobalSettings({ oauthProviders: update });
2164
+ auditAdminAction(req, 'auth-provider-settings.update', {
2165
+ providers: Object.keys(update)
2166
+ });
2167
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2168
+ res.end(JSON.stringify({ success: true }));
2169
+ });
1917
2170
  // === SECTION: Cloud MCP HTTP transport ===
1918
2171
  addRoute('POST', '/mcp', async (req, res) => { await handleCloudMcpRequest(req, res); });
1919
2172
  addRoute('POST', '/mcp/', async (req, res) => { await handleCloudMcpRequest(req, res); });
@@ -6,9 +6,20 @@
6
6
  *
7
7
  * Routes registered here:
8
8
  * GET /api/taskforce/auth/runtime-config
9
+ * GET /api/taskforce/auth/providers
10
+ * GET /api/taskforce/auth/oauth/:provider/start
11
+ * GET /api/taskforce/auth/oauth/:provider/callback
12
+ * POST /api/taskforce/auth/oauth/apple/callback
13
+ * GET /api/taskforce/account/login-methods
14
+ * DELETE /api/taskforce/account/login-methods/:provider
15
+ * POST /api/taskforce/account/login-methods/password
9
16
  * GET /api/taskforce/auth/session
10
17
  * GET /api/taskforce/auth/admin-session
11
18
  * POST /api/taskforce/auth/profile
19
+ * POST /api/taskforce/auth/profile/avatar/init
20
+ * POST /api/taskforce/auth/profile/avatar/upload
21
+ * POST /api/taskforce/auth/profile/avatar/finalize
22
+ * POST /api/taskforce/auth/profile/avatar/discard
12
23
  * POST /api/taskforce/auth/login
13
24
  * POST /api/taskforce/auth/register
14
25
  * POST /api/taskforce/auth/verify-email/request
@@ -26,6 +37,7 @@
26
37
  * GET /api/taskforce/account/plan-preview
27
38
  * GET /api/taskforce/auth/workspace-members
28
39
  * GET /api/taskforce/workspace/assignee-options
40
+ * POST /api/taskforce/workspace/ai-profiles/avatar
29
41
  * GET /api/taskforce/admin/users
30
42
  * POST /api/taskforce/admin/users/invite
31
43
  * PATCH /api/taskforce/admin/users/:id/role
@@ -46,13 +58,14 @@ export interface AuthRouteDependencies {
46
58
  context: RouteContext;
47
59
  requestWorkspaceId: (req: IncomingMessage) => string;
48
60
  requestUserId: (req: IncomingMessage) => string;
61
+ resolveAuthenticatedUserId: (req: IncomingMessage) => string;
49
62
  requestRole: (req: IncomingMessage) => string;
50
63
  ensureAppAccess: (req: IncomingMessage, res: ServerResponse) => boolean;
51
64
  ensureAuthenticatedUser: (req: IncomingMessage) => boolean;
52
- ensureWorkspaceAdminRole: (req: IncomingMessage) => boolean;
65
+ ensureWorkspaceAdminRole: (req: IncomingMessage, workspaceIdOverride?: string | null) => boolean;
53
66
  ensureCloudPlanData: (req: IncomingMessage, res: ServerResponse) => boolean;
54
67
  requireCloudRuntime: (res: ServerResponse, feature: string) => boolean;
55
- auditAuthAction: (req: IncomingMessage, action: string, details: Record<string, unknown>) => void;
68
+ auditAuthAction: (req: IncomingMessage, action: string, details: Record<string, unknown>, workspaceIdOverride?: string | null) => void;
56
69
  resolveAdminRequestActor: (req: IncomingMessage) => {
57
70
  authenticated: boolean;
58
71
  userId: string;