@taskforcehq/taskforce 0.3.304 → 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.
- package/README.md +140 -247
- package/dist/Taskforce.module.css +295 -89
- package/dist/TaskforceCore.js +378 -111
- package/dist/TaskforceCore.test.js +1221 -107
- package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/assets/fonts/Noir_medium.woff2 +0 -0
- package/dist/assets/fonts/Noir_regular.woff2 +0 -0
- package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/compat/workspaceSyncCompat.js +52 -2
- package/dist/compat/workspaceSyncCompat.test.js +38 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
- package/dist/components/context/ContextAttachmentManager.js +549 -0
- package/dist/components/features/AgentsModule.d.ts +10 -1
- package/dist/components/features/AgentsModule.js +260 -15
- package/dist/components/features/AgentsModule.test.js +255 -5
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +6 -2
- package/dist/components/features/DocumentViewer.js +90 -10
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +226 -6
- package/dist/components/features/TaskSettings.js +29 -3
- package/dist/components/features/TaskSettings.test.js +42 -1
- package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
- package/dist/components/features/documentWorkspaceModel.js +57 -0
- package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
- package/dist/components/features/useDocumentWorkspaceController.js +113 -0
- package/dist/components/task/TaskCard.js +19 -28
- package/dist/components/task/TaskCard.test.js +38 -3
- package/dist/components/task/TaskContextUpload.js +4 -526
- package/dist/components/task/TaskForm.d.ts +8 -8
- package/dist/components/task/TaskForm.js +362 -230
- package/dist/components/task/TaskForm.test.js +558 -381
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +96 -14
- package/dist/components/task/TaskKanban.test.js +36 -0
- package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
- package/dist/components/ui/EditableAvatarButton.js +18 -0
- package/dist/components/views/PlanComparisonPage.d.ts +9 -1
- package/dist/components/views/PlanComparisonPage.js +70 -17
- package/dist/components/views/PlanComparisonPage.test.js +289 -41
- package/dist/components/views/PlansPage.js +239 -106
- package/dist/components/views/PlansPage.test.js +593 -42
- package/dist/components/views/StandaloneLayout.js +310 -554
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
- package/dist/components/views/panels/PlanningDrawer.js +18 -11
- package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
- package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
- package/dist/components/views/planningScope.d.ts +20 -0
- package/dist/components/views/planningScope.js +25 -0
- package/dist/components/views/planningScope.test.d.ts +1 -0
- package/dist/components/views/planningScope.test.js +60 -0
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +27 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +87 -15
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +42 -2
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
- package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
- package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +45 -3
- package/dist/core/AiProfileService.js +774 -63
- package/dist/core/AiProfileService.test.js +22 -2
- package/dist/core/AiProfiles.test.js +761 -2
- package/dist/core/AttachmentLinkService.js +57 -15
- package/dist/core/AuthIdentityService.d.ts +107 -0
- package/dist/core/AuthIdentityService.js +284 -0
- package/dist/core/AuthTokenService.d.ts +4 -0
- package/dist/core/AuthTokenService.js +34 -6
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/EntitlementsPolicy.test.js +77 -15
- package/dist/core/GlobalSettingsService.js +115 -6
- package/dist/core/PlanEntitlementService.d.ts +30 -3
- package/dist/core/PlanEntitlementService.js +454 -93
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +54 -28
- package/dist/core/PlanningEntities.test.js +52 -0
- package/dist/core/SignupMonetizationSettings.test.js +241 -44
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +244 -88
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +228 -14
- package/dist/core/Taskforce.js +814 -201
- package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
- package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
- package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
- package/dist/core/WorkspaceLifecycleService.js +0 -5
- package/dist/core/WorkspacePermissions.test.js +25 -1
- package/dist/core/WorkspacePlanMode.test.js +128 -27
- package/dist/core/shared.d.ts +3 -1
- package/dist/core/shared.js +16 -1
- package/dist/core/types.d.ts +68 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
- package/dist/hooks/sync/auth.js +3 -1
- package/dist/hooks/sync/bootstrap.js +2 -0
- package/dist/hooks/sync/controlPlane.d.ts +37 -0
- package/dist/hooks/sync/controlPlane.js +78 -0
- package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
- package/dist/hooks/sync/controlPlane.test.js +121 -0
- package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
- package/dist/hooks/sync/lifecyclePolicy.js +93 -0
- package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
- package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
- package/dist/hooks/sync/orchestratorShared.js +68 -47
- package/dist/hooks/sync/orchestratorShared.test.js +50 -1
- package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
- package/dist/hooks/sync/pullLifecycle.js +24 -0
- package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
- package/dist/hooks/sync/pullLifecycle.test.js +80 -0
- package/dist/hooks/sync/recovery.d.ts +16 -2
- package/dist/hooks/sync/recovery.js +171 -53
- package/dist/hooks/sync/recovery.test.d.ts +1 -0
- package/dist/hooks/sync/recovery.test.js +292 -0
- package/dist/hooks/sync/transfers.d.ts +16 -3
- package/dist/hooks/sync/transfers.js +186 -65
- package/dist/hooks/sync/transfers.test.d.ts +1 -0
- package/dist/hooks/sync/transfers.test.js +396 -0
- package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
- package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
- package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
- package/dist/hooks/sync/useSyncStatusActions.js +88 -0
- package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
- package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
- package/dist/hooks/sync/useSyncStatusControls.js +60 -0
- package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +16 -6
- package/dist/hooks/useSyncOrchestrator.js +424 -175
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +487 -10
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +12 -1
- package/dist/hooks/useTaskMutations.test.js +30 -1
- package/dist/hooks/useTaskforce.d.ts +65 -4
- package/dist/hooks/useTaskforce.js +564 -243
- package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +2454 -208
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +17 -2
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
- package/dist/localization/locales/en-US.d.ts +0 -1
- package/dist/localization/locales/en-US.js +0 -1
- package/dist/localization/locales/es-419.js +0 -1
- package/dist/localization/locales/pt-BR.js +0 -1
- package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- package/dist/mcp/collaborationRegistrar.d.ts +2 -0
- package/dist/mcp/collaborationRegistrar.js +71 -0
- package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
- package/dist/mcp/documentAssetRegistrar.js +346 -0
- package/dist/mcp/runtime.d.ts +2 -0
- package/dist/mcp/runtime.js +2433 -3534
- package/dist/mcp/runtime.test.js +440 -2
- package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
- package/dist/mcp/taskPlanningRegistrar.js +418 -0
- package/dist/mcp/toolCatalog.d.ts +35 -0
- package/dist/mcp/toolCatalog.js +3 -0
- package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
- package/dist/migrations/taskSchemaMigrations.js +171 -61
- package/dist/migrations/taskSchemaMigrations.test.js +15 -0
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/dist/resources/templates/workflows/collaborate.yaml +1 -1
- package/dist/resources/templates/workflows/evaluate.yaml +2 -2
- package/dist/resources/templates/workflows/plan.yaml +1 -1
- package/dist/resources/templates/workflows/review.yaml +2 -2
- package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
- package/dist/server/auth/providers/apple.d.ts +33 -0
- package/dist/server/auth/providers/apple.js +82 -0
- package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
- package/dist/server/auth/providers/appleClientSecret.js +65 -0
- package/dist/server/auth/providers/github.d.ts +26 -0
- package/dist/server/auth/providers/github.js +86 -0
- package/dist/server/auth/providers/google.d.ts +21 -0
- package/dist/server/auth/providers/google.js +56 -0
- package/dist/server/auth/providers/types.d.ts +21 -0
- package/dist/server/auth/providers/types.js +1 -0
- package/dist/server/auth.d.ts +2 -0
- package/dist/server/auth.js +6 -3
- package/dist/server/documentReviewRoutes.test.js +36 -3
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.js +120 -9
- package/dist/server/index.rateLimit.test.js +3 -0
- package/dist/server/index.test.js +106 -3
- package/dist/server/routes/admin.d.ts +4 -5
- package/dist/server/routes/admin.js +410 -80
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +16 -6
- package/dist/server/routes/auth.js +1015 -118
- package/dist/server/routes/authSupport.d.ts +30 -0
- package/dist/server/routes/authSupport.js +81 -0
- package/dist/server/routes/billing.d.ts +1 -1
- package/dist/server/routes/billing.js +276 -34
- package/dist/server/routes/billing.test.js +760 -52
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +242 -74
- package/dist/server/routes/primitives.d.ts +11 -0
- package/dist/server/routes/primitives.js +73 -0
- package/dist/server/routes/resources.d.ts +1 -1
- package/dist/server/routes/resources.js +1 -1
- package/dist/server/routes/shared.d.ts +29 -3
- package/dist/server/routes/shared.js +69 -4
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +437 -39
- package/dist/server/routes/sync.js +62 -606
- package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
- package/dist/server/routes/syncAuxRoutes.js +91 -0
- package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
- package/dist/server/routes/syncAuxRoutes.test.js +158 -0
- package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
- package/dist/server/routes/syncPullApplyRoutes.js +204 -0
- package/dist/server/routes/syncPushRoutes.d.ts +24 -0
- package/dist/server/routes/syncPushRoutes.js +212 -0
- package/dist/server/routes/syncRouteGuards.d.ts +36 -0
- package/dist/server/routes/syncRouteGuards.js +67 -0
- package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
- package/dist/server/routes/syncRouteGuards.test.js +94 -0
- package/dist/server/routes/syncRouteTypes.d.ts +13 -0
- package/dist/server/routes/syncRouteTypes.js +1 -0
- package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
- package/dist/server/routes/syncSnapshotRoutes.js +180 -0
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +44 -2
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +37 -6
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +136 -165
- package/dist/server/routes.test.js +2457 -162
- package/dist/services/workflowExportSnapshots.test.js +2 -0
- package/dist/shared/aiProfileSeatScope.d.ts +5 -0
- package/dist/shared/aiProfileSeatScope.js +21 -0
- package/dist/shared/passwordPolicy.d.ts +13 -0
- package/dist/shared/passwordPolicy.js +84 -0
- package/dist/shared/passwordPolicy.test.d.ts +1 -0
- package/dist/shared/passwordPolicy.test.js +34 -0
- package/dist/storage/workspaceAssetStore.d.ts +15 -3
- package/dist/storage/workspaceAssetStore.js +132 -44
- package/dist/storage/workspaceAssetStore.test.js +93 -6
- package/dist/styles/fonts.css +70 -0
- package/dist/sync/collaborationSyncPayload.d.ts +6 -1
- package/dist/sync/collaborationSyncPayload.js +17 -11
- package/dist/sync/collaborationSyncPayload.test.js +13 -1
- package/dist/sync/contentSyncState.d.ts +4 -8
- package/dist/sync/contentSyncState.js +33 -77
- package/dist/sync/syncApplyHandlers.d.ts +7 -0
- package/dist/sync/syncApplyHandlers.js +28 -13
- package/dist/sync/syncApplyHandlers.test.js +72 -3
- package/dist/sync/syncFieldSemantics.d.ts +12 -0
- package/dist/sync/syncFieldSemantics.js +55 -0
- package/dist/sync/syncResourceAdapters.d.ts +44 -0
- package/dist/sync/syncResourceAdapters.js +77 -0
- package/dist/sync/syncService.d.ts +6 -0
- package/dist/sync/syncService.js +60 -0
- package/dist/sync/syncService.test.js +71 -3
- package/dist/sync/taskSyncChangeKey.d.ts +2 -0
- package/dist/sync/taskSyncChangeKey.js +143 -0
- package/dist/sync/taskSyncPayload.js +3 -8
- package/dist/sync/workspacePullFeed.js +4 -3
- package/dist/sync/workspacePullFeed.test.js +43 -0
- package/dist/sync/workspaceRepair.js +5 -2
- package/dist/sync/workspaceSyncModel.d.ts +48 -0
- package/dist/sync/workspaceSyncModel.js +195 -31
- package/dist/sync/workspaceSyncModel.test.js +325 -12
- package/dist/sync/workspaceSyncState.d.ts +19 -0
- package/dist/sync/workspaceSyncState.js +3 -1
- package/dist/sync/workspaceSyncSurface.js +3 -8
- package/dist/types.d.ts +17 -0
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BlS-cqnl.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
- package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
- package/dist/ui/assets/{InitiativesModule-BjR6iBf9.js → InitiativesModule-9E6ParrY.js} +1 -1
- package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
- package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
- package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
- package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
- package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
- package/dist/ui/assets/TaskSettings-DZX4jk7e.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-DnFqdUME.js → WorkflowsModule-BA7jcEpH.js} +1 -1
- package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
- package/dist/ui/assets/index-C6k75jr8.js +6 -0
- package/dist/ui/assets/{vendor-icons-I7ZwG1z_.js → vendor-icons-DuEd_65c.js} +1 -1
- package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/ui/fonts/Noir_medium.otf +0 -0
- package/dist/ui/fonts/Noir_medium.woff2 +0 -0
- package/dist/ui/fonts/Noir_regular.otf +0 -0
- package/dist/ui/fonts/Noir_regular.woff2 +0 -0
- package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/ui/index.html +3 -3
- package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
- package/dist/utils/assignees.d.ts +2 -0
- package/dist/utils/assignees.js +2 -0
- package/dist/utils/avatarUpload.d.ts +27 -0
- package/dist/utils/avatarUpload.js +132 -0
- package/dist/utils/avatarUpload.test.d.ts +1 -0
- package/dist/utils/avatarUpload.test.js +40 -0
- package/dist/utils/bootstrapTrace.d.ts +7 -0
- package/dist/utils/bootstrapTrace.js +29 -0
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +199 -0
- package/dist/utils/contextAssetEvents.d.ts +2 -0
- package/dist/utils/syncEventDetails.d.ts +3 -0
- package/dist/utils/syncEventDetails.js +64 -0
- package/dist/utils/syncEventDetails.test.d.ts +1 -0
- package/dist/utils/syncEventDetails.test.js +23 -0
- package/dist/utils/syncEventPresentation.d.ts +15 -0
- package/dist/utils/syncEventPresentation.js +129 -0
- package/dist/utils/syncEventPresentation.test.d.ts +1 -0
- package/dist/utils/syncEventPresentation.test.js +64 -0
- package/dist/utils/syncStatusPresentation.d.ts +72 -0
- package/dist/utils/syncStatusPresentation.js +217 -0
- package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
- package/dist/utils/syncStatusPresentation.test.js +164 -0
- package/dist/utils/taskActivity.d.ts +2 -0
- package/dist/utils/taskActivity.js +228 -22
- package/dist/utils/taskActivity.test.js +119 -8
- package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
- package/dist/utils/workspaceSyncPresentation.js +68 -2
- package/dist/utils/workspaceSyncPresentation.test.js +36 -1
- package/package.json +10 -2
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- package/scripts/playwright-auth.sh +5 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/scripts/run-e2e-realtime.sh +17 -3
- package/scripts/run-smoke.sh +17 -5
- package/scripts/run-soak.sh +17 -5
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/src/resources/templates/workflows/collaborate.yaml +1 -1
- package/src/resources/templates/workflows/evaluate.yaml +2 -2
- package/src/resources/templates/workflows/plan.yaml +1 -1
- package/src/resources/templates/workflows/review.yaml +2 -2
- package/dist/ui/assets/AgentsModule-BLWVbuKP.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-BH_6DF6x.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
- package/dist/ui/assets/index-DXUdtH1B.js +0 -6
- package/dist/ui/assets/index-Ii0B0rTO.css +0 -1
- package/scripts/migrate-planning-model.ts +0 -233
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
53
53
|
import { TaskforceCore } from '../../core/Taskforce.js';
|
|
54
54
|
import { type ResolvedObjectStorageConfig } from '../../storage/objectStorage.js';
|
|
55
|
-
import { type RouteHandler, type RouteContext } from '
|
|
55
|
+
import { type RouteHandler, type RouteContext } from './primitives.js';
|
|
56
56
|
export interface TaskRouteDependencies {
|
|
57
57
|
addRoute: (method: string, path: string, handler: RouteHandler) => void;
|
|
58
58
|
core: TaskforceCore;
|
|
@@ -54,9 +54,9 @@ import { toPublicObjectStorageConfig } from '../../storage/objectStorage.js';
|
|
|
54
54
|
import { getRuntimeCapabilities } from '../../runtime/capabilities.js';
|
|
55
55
|
import { resolveBuildInfo } from '../buildInfo.js';
|
|
56
56
|
import { resolveRealtimeSyncFlag } from '../../sync/realtimeFeature.js';
|
|
57
|
-
import { parseJsonBody } from '
|
|
57
|
+
import { parseJsonBody } from './primitives.js';
|
|
58
58
|
import { resolveRequestAccess } from '../auth.js';
|
|
59
|
-
import {
|
|
59
|
+
import { getRouteTimingNow, logSlowBootstrapRoute, requestBootstrapTraceId, requestHeader, requestRuntimeMode, resolveErrorStatusCode, serializeJsonWithTiming, setServerTimingHeader } from './shared.js';
|
|
60
60
|
import { listBuiltInCustomTaxonomyLibraryPacks } from '../../shared/customTaxonomyLibrary.js';
|
|
61
61
|
import { listBuiltInTaxonomyLibraryPacks } from '../../shared/taxonomyLibrary.js';
|
|
62
62
|
import { shouldUseProvisionalTaskReferences } from '../../utils/taskReferences.js';
|
|
@@ -506,6 +506,48 @@ export function registerTaskRoutes(deps) {
|
|
|
506
506
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
507
507
|
res.end(JSON.stringify(data));
|
|
508
508
|
});
|
|
509
|
+
// GET /api/taskforce/planning/bootstrap - List planning entities plus lightweight task summaries
|
|
510
|
+
addRoute('GET', '/api/taskforce/planning/bootstrap', async (req, res) => {
|
|
511
|
+
if (!ensureAppAccess(req, res))
|
|
512
|
+
return;
|
|
513
|
+
const startedAt = getRouteTimingNow();
|
|
514
|
+
const workspaceId = requestWorkspaceId(req);
|
|
515
|
+
const traceId = requestBootstrapTraceId(req);
|
|
516
|
+
const diagnostics = {};
|
|
517
|
+
const data = core.listPlanningBootstrap(workspaceId, diagnostics);
|
|
518
|
+
const serializeStartedAt = getRouteTimingNow();
|
|
519
|
+
const serialized = serializeJsonWithTiming(data, serializeStartedAt);
|
|
520
|
+
const totalDurationMs = Math.max(0, getRouteTimingNow() - startedAt);
|
|
521
|
+
const details = diagnostics;
|
|
522
|
+
setServerTimingHeader(res, [
|
|
523
|
+
{ name: 'planning-bootstrap', durationMs: totalDurationMs },
|
|
524
|
+
{ name: 'planning-bootstrap-initiatives', durationMs: details.initiativeLoadMs ?? 0 },
|
|
525
|
+
{ name: 'planning-bootstrap-workstreams', durationMs: details.workstreamLoadMs ?? 0 },
|
|
526
|
+
{ name: 'planning-bootstrap-tasks', durationMs: details.taskSummaryQueryMs ?? 0 },
|
|
527
|
+
{ name: 'planning-bootstrap-build', durationMs: details.taskSummaryBuildMs ?? 0 },
|
|
528
|
+
{ name: 'planning-bootstrap-json', durationMs: serialized.durationMs },
|
|
529
|
+
]);
|
|
530
|
+
logSlowBootstrapRoute({
|
|
531
|
+
route: '/api/taskforce/planning/bootstrap',
|
|
532
|
+
workspaceId,
|
|
533
|
+
traceId,
|
|
534
|
+
durationMs: totalDurationMs,
|
|
535
|
+
details: {
|
|
536
|
+
initiativeCount: details.initiativeCount ?? 0,
|
|
537
|
+
workstreamCount: details.workstreamCount ?? 0,
|
|
538
|
+
workstreamLinkedTaskCount: details.workstreamLinkedTaskCount ?? 0,
|
|
539
|
+
workstreamTaskSummaryCount: details.workstreamTaskSummaryCount ?? 0,
|
|
540
|
+
responseBytes: serialized.byteLength,
|
|
541
|
+
initiativeLoadMs: details.initiativeLoadMs ?? 0,
|
|
542
|
+
workstreamLoadMs: details.workstreamLoadMs ?? 0,
|
|
543
|
+
taskSummaryQueryMs: details.taskSummaryQueryMs ?? 0,
|
|
544
|
+
taskSummaryBuildMs: details.taskSummaryBuildMs ?? 0,
|
|
545
|
+
jsonSerializeMs: serialized.durationMs,
|
|
546
|
+
},
|
|
547
|
+
});
|
|
548
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
549
|
+
res.end(serialized.body);
|
|
550
|
+
});
|
|
509
551
|
// POST /api/taskforce/initiative - Create initiative
|
|
510
552
|
addRoute('POST', '/api/taskforce/initiative', async (req, res) => {
|
|
511
553
|
if (!ensureAppAccess(req, res))
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { IncomingMessage, ServerResponse } from 'http';
|
|
15
15
|
import type { TaskforceCore } from '../../core/Taskforce.js';
|
|
16
|
-
import type
|
|
16
|
+
import { type RouteHandler, type RouteContext } from './primitives.js';
|
|
17
17
|
import { ObjectStorageClient } from '../../storage/objectStorageClient.js';
|
|
18
18
|
export interface WorkspaceRouteDependencies {
|
|
19
19
|
addRoute: (method: string, path: string, handler: RouteHandler) => void;
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import * as fs from 'fs';
|
|
15
15
|
import * as path from 'path';
|
|
16
|
-
import { parseJsonBody } from '
|
|
16
|
+
import { parseJsonBody } from './primitives.js';
|
|
17
17
|
import { resolveRequestAccess, createSessionTokenCookie, resolveSessionCookieAttributesForRequest, } from '../auth.js';
|
|
18
18
|
import { isWorkspaceSwitchingEnabledForRuntime } from '../../shared/runtimeContract.js';
|
|
19
|
-
import { resolveErrorStatusCode } from './shared.js';
|
|
19
|
+
import { getRouteTimingNow, logSlowBootstrapRoute, requestBootstrapTraceId, resolveErrorStatusCode, serializeJsonWithTiming, setServerTimingHeader } from './shared.js';
|
|
20
20
|
// === SECTION: Registration ===
|
|
21
21
|
export function registerWorkspaceRoutes(deps) {
|
|
22
22
|
const { addRoute, core, context, ensureAppAccess, auditAuthAction, getObjectStorageClient, ensureWithinDir, sanitizeStorageSegment, sanitizeTaskIdForPath, normalizeDocumentPathInput, } = deps;
|
|
@@ -94,6 +94,8 @@ export function registerWorkspaceRoutes(deps) {
|
|
|
94
94
|
// === SECTION: Workspace CRUD routes ===
|
|
95
95
|
// GET /api/taskforce/workspaces - List authenticated user's accessible workspaces
|
|
96
96
|
addRoute('GET', '/api/taskforce/workspaces', async (req, res) => {
|
|
97
|
+
const routeStartedAt = getRouteTimingNow();
|
|
98
|
+
const traceId = requestBootstrapTraceId(req);
|
|
97
99
|
const authConfig = context.authConfig;
|
|
98
100
|
if (!isWorkspaceSwitchingEnabledForRuntime(authConfig?.runtimeMode)) {
|
|
99
101
|
res.writeHead(501, { 'Content-Type': 'application/json' });
|
|
@@ -106,6 +108,7 @@ export function registerWorkspaceRoutes(deps) {
|
|
|
106
108
|
return;
|
|
107
109
|
}
|
|
108
110
|
const access = resolveRequestAccess(req, authConfig);
|
|
111
|
+
const authResolvedAt = getRouteTimingNow();
|
|
109
112
|
if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
|
|
110
113
|
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
111
114
|
res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
|
|
@@ -113,17 +116,45 @@ export function registerWorkspaceRoutes(deps) {
|
|
|
113
116
|
}
|
|
114
117
|
if (!ensureAppAccess(req, res))
|
|
115
118
|
return;
|
|
119
|
+
const appAccessCheckedAt = getRouteTimingNow();
|
|
116
120
|
const workspaces = core.listUserWorkspaces(access.userId);
|
|
121
|
+
const workspacesLoadedAt = getRouteTimingNow();
|
|
117
122
|
const requestedCurrentWorkspaceId = String(access.workspaceId || '').trim();
|
|
118
123
|
const currentWorkspaceId = workspaces.some((workspace) => workspace.id === requestedCurrentWorkspaceId)
|
|
119
124
|
? requestedCurrentWorkspaceId
|
|
120
125
|
: (workspaces[0]?.id || 'default');
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
const currentWorkspaceResolvedAt = getRouteTimingNow();
|
|
127
|
+
const serialized = serializeJsonWithTiming({
|
|
123
128
|
success: true,
|
|
124
129
|
workspaces,
|
|
125
130
|
currentWorkspaceId: currentWorkspaceId || access.workspaceId
|
|
126
|
-
})
|
|
131
|
+
}, currentWorkspaceResolvedAt);
|
|
132
|
+
const totalDurationMs = getRouteTimingNow() - routeStartedAt;
|
|
133
|
+
setServerTimingHeader(res, [
|
|
134
|
+
{ name: 'workspaces', durationMs: totalDurationMs },
|
|
135
|
+
{ name: 'workspaces-auth', durationMs: authResolvedAt - routeStartedAt },
|
|
136
|
+
{ name: 'workspaces-app-access', durationMs: appAccessCheckedAt - authResolvedAt },
|
|
137
|
+
{ name: 'workspaces-list', durationMs: workspacesLoadedAt - appAccessCheckedAt },
|
|
138
|
+
{ name: 'workspaces-current', durationMs: currentWorkspaceResolvedAt - workspacesLoadedAt },
|
|
139
|
+
{ name: 'workspaces-json', durationMs: serialized.durationMs },
|
|
140
|
+
]);
|
|
141
|
+
logSlowBootstrapRoute({
|
|
142
|
+
route: '/api/taskforce/workspaces',
|
|
143
|
+
workspaceId: currentWorkspaceId || access.workspaceId || 'default',
|
|
144
|
+
traceId,
|
|
145
|
+
durationMs: totalDurationMs,
|
|
146
|
+
details: {
|
|
147
|
+
workspaceCount: workspaces.length,
|
|
148
|
+
payloadBytes: serialized.byteLength,
|
|
149
|
+
authMs: authResolvedAt - routeStartedAt,
|
|
150
|
+
appAccessMs: appAccessCheckedAt - authResolvedAt,
|
|
151
|
+
listWorkspacesMs: workspacesLoadedAt - appAccessCheckedAt,
|
|
152
|
+
currentWorkspaceMs: currentWorkspaceResolvedAt - workspacesLoadedAt,
|
|
153
|
+
serializeMs: serialized.durationMs,
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
157
|
+
res.end(serialized.body);
|
|
127
158
|
});
|
|
128
159
|
// POST /api/taskforce/workspaces - Create workspace for current authenticated user
|
|
129
160
|
addRoute('POST', '/api/taskforce/workspaces', async (req, res) => {
|
|
@@ -281,7 +312,7 @@ export function registerWorkspaceRoutes(deps) {
|
|
|
281
312
|
return;
|
|
282
313
|
}
|
|
283
314
|
const resolved = core.resolveUserAccess(access.userId, workspaceId, access.role);
|
|
284
|
-
if (!resolved || resolved.
|
|
315
|
+
if (!resolved || resolved.disabled) {
|
|
285
316
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
286
317
|
res.end(JSON.stringify({ success: false, error: 'Workspace access is not active for this user.' }));
|
|
287
318
|
return;
|
package/dist/server/routes.d.ts
CHANGED
|
@@ -4,17 +4,14 @@
|
|
|
4
4
|
* Extracted from vite.ts plugin to be reusable in both Vite plugin and standalone server.
|
|
5
5
|
* This is a minimal implementation with core routes - additional routes can be added incrementally.
|
|
6
6
|
*/
|
|
7
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
8
7
|
import { TaskforceCore } from '../core/Taskforce.js';
|
|
9
|
-
import { type
|
|
10
|
-
|
|
11
|
-
export type RouteHandler = (req: IncomingMessage, res: ServerResponse, params: Record<string, string>) => Promise<void>;
|
|
8
|
+
import { type RouteContext, type RouteHandler } from './routes/primitives.js';
|
|
9
|
+
export { parseJsonBody } from './routes/primitives.js';
|
|
12
10
|
export interface Route {
|
|
13
11
|
pattern: RegExp;
|
|
14
12
|
keys: string[];
|
|
15
13
|
handler: RouteHandler;
|
|
16
14
|
}
|
|
17
|
-
export declare function parseJsonBody(req: IncomingMessage, maxBytes?: number): Promise<any>;
|
|
18
15
|
/**
|
|
19
16
|
* Create API routes for Taskforce
|
|
20
17
|
*
|
|
@@ -60,10 +57,6 @@ export declare function parseJsonBody(req: IncomingMessage, maxBytes?: number):
|
|
|
60
57
|
* - POST /api/taskforce/export-resources
|
|
61
58
|
* - GET /api/taskforce/health
|
|
62
59
|
*/
|
|
63
|
-
export interface RouteContext {
|
|
64
|
-
authConfig?: TaskforceAuthConfig;
|
|
65
|
-
objectStorage?: ResolvedObjectStorageConfig;
|
|
66
|
-
}
|
|
67
60
|
export declare function createRoutes(core: TaskforceCore, context?: RouteContext): Route[];
|
|
68
61
|
export declare function matchRoute(method: string, pathname: string, routes: Route[]): {
|
|
69
62
|
handler: RouteHandler;
|
package/dist/server/routes.js
CHANGED
|
@@ -14,7 +14,7 @@ import { parseAiProfileBootstrapHeaders } from '../mcp/aiProfileBootstrap.js';
|
|
|
14
14
|
import { createTaskforceMcpServer, isMcpWriteToolName } from '../mcp/runtime.js';
|
|
15
15
|
import { applyPrefixedRateLimitHeaders } from './rateLimit.js';
|
|
16
16
|
import { createSessionTokenCookie, resolveRequestAccess } from './auth.js';
|
|
17
|
-
import {
|
|
17
|
+
import { sendEmail } from './email.js';
|
|
18
18
|
import { normalizeTaskDataRelativePath, ObjectStorageClient } from '../storage/objectStorageClient.js';
|
|
19
19
|
import { DocumentRegistry, createDocumentHash } from '../storage/documentRegistry.js';
|
|
20
20
|
import { TaskAssetStore } from '../storage/taskAssetStore.js';
|
|
@@ -34,12 +34,35 @@ import { registerResourceRoutes } from './routes/resources.js';
|
|
|
34
34
|
import { registerDocumentRoutes } from './routes/documents.js';
|
|
35
35
|
import { registerDocumentReviewRoutes } from './routes/documentReviews.js';
|
|
36
36
|
import { registerAnnotatedAttachmentRoutes } from './routes/annotatedAttachments.js';
|
|
37
|
-
import {
|
|
37
|
+
import { escapeHtml, parseJsonBody } from './routes/primitives.js';
|
|
38
|
+
import { getRouteEmailConfig, resolvePublicAuthBaseUrl, resolveRuntimeConfigBaseUrl } from './routes/authSupport.js';
|
|
39
|
+
import { buildCloudMcpRuntimeAuth, requestHeader, requestRole, requestRuntimeMode, requestUserId, makeAuditAdminAction, makeAuditAuthAction, makeEnsureAdminRole, makeEnsureAppAccess, makeEnsureAuthenticatedUser, makeEnsureCloudPlanData, makeRequestWorkspaceId, makeRequireCloudRuntime, makeResolveAdminRequestActor, makeResolveAuthenticatedUserId, makeResolveAuthenticatedMutationUserId, } from './routes/shared.js';
|
|
38
40
|
import { resolveDeploymentConfig } from '../config/deployment.js';
|
|
39
41
|
import { getTaskforceCloudEnvironmentConfig, resolveTaskforceCloudEnvironmentFromBaseUrl } from '../config/cloudEnvironment.js';
|
|
40
42
|
import { getCanonicalDocumentName } from '../utils/documentNames.js';
|
|
41
43
|
import { MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS } from '../mcp/toolRegistry.js';
|
|
42
|
-
|
|
44
|
+
export { parseJsonBody } from './routes/primitives.js';
|
|
45
|
+
function pathMatchesRouteAuthExclusion(pathname, excludedPaths) {
|
|
46
|
+
if (!Array.isArray(excludedPaths))
|
|
47
|
+
return false;
|
|
48
|
+
return excludedPaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));
|
|
49
|
+
}
|
|
50
|
+
function shouldRequireAuthenticatedCloudApiRoute(pathname, context) {
|
|
51
|
+
const authConfig = context.authConfig;
|
|
52
|
+
if (!authConfig?.enabled)
|
|
53
|
+
return false;
|
|
54
|
+
if (!pathname.startsWith('/api/taskforce/'))
|
|
55
|
+
return false;
|
|
56
|
+
if (pathname.startsWith('/api/taskforce/sync/'))
|
|
57
|
+
return false;
|
|
58
|
+
if (pathMatchesRouteAuthExclusion(pathname, authConfig.excludedPaths))
|
|
59
|
+
return false;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
function writeRouteAuthenticationRequired(res) {
|
|
63
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
64
|
+
res.end(JSON.stringify({ error: 'Authentication required.' }));
|
|
65
|
+
}
|
|
43
66
|
const DEFAULT_DOCUMENT_RETENTION_DAYS = 7;
|
|
44
67
|
const STORAGE_INTEGRITY_HISTORY_LIMIT = 50;
|
|
45
68
|
const MCP_TOKEN_SCOPE_VALUES = new Set(['tasks:read', 'tasks:write', 'workspace:read']);
|
|
@@ -70,20 +93,6 @@ function readPositiveInteger(value, fallback) {
|
|
|
70
93
|
return fallback;
|
|
71
94
|
return parsed;
|
|
72
95
|
}
|
|
73
|
-
function resolveBooleanSetting(value, fallback) {
|
|
74
|
-
if (typeof value === 'boolean')
|
|
75
|
-
return value;
|
|
76
|
-
if (typeof value === 'number')
|
|
77
|
-
return value !== 0;
|
|
78
|
-
const normalized = String(value || '').trim().toLowerCase();
|
|
79
|
-
if (!normalized)
|
|
80
|
-
return fallback;
|
|
81
|
-
if (['1', 'true', 'yes', 'on'].includes(normalized))
|
|
82
|
-
return true;
|
|
83
|
-
if (['0', 'false', 'no', 'off'].includes(normalized))
|
|
84
|
-
return false;
|
|
85
|
-
return fallback;
|
|
86
|
-
}
|
|
87
96
|
// Convert route pattern like '/api/taskforce/tasks/:id' to regex
|
|
88
97
|
function createRoutePattern(path) {
|
|
89
98
|
const keys = [];
|
|
@@ -93,74 +102,6 @@ function createRoutePattern(path) {
|
|
|
93
102
|
});
|
|
94
103
|
return { pattern: new RegExp(`^${pattern}$`), keys };
|
|
95
104
|
}
|
|
96
|
-
// Helper to parse JSON body from request with size guard.
|
|
97
|
-
export async function parseJsonBody(req, maxBytes = DEFAULT_MAX_JSON_BODY_BYTES) {
|
|
98
|
-
const safeMaxBytes = Number.isFinite(maxBytes) && maxBytes > 0
|
|
99
|
-
? Math.floor(maxBytes)
|
|
100
|
-
: DEFAULT_MAX_JSON_BODY_BYTES;
|
|
101
|
-
return new Promise((resolve, reject) => {
|
|
102
|
-
let body = '';
|
|
103
|
-
let receivedBytes = 0;
|
|
104
|
-
req.on('data', chunk => {
|
|
105
|
-
receivedBytes += chunk.length;
|
|
106
|
-
if (receivedBytes > safeMaxBytes) {
|
|
107
|
-
req.pause();
|
|
108
|
-
reject(createHttpError(`Payload too large (max ${safeMaxBytes} bytes)`, 413));
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
body += chunk;
|
|
112
|
-
});
|
|
113
|
-
req.on('end', () => {
|
|
114
|
-
try {
|
|
115
|
-
resolve(body ? JSON.parse(body) : {});
|
|
116
|
-
}
|
|
117
|
-
catch (e) {
|
|
118
|
-
reject(createHttpError('Invalid JSON', 400));
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
req.on('error', reject);
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
async function parseFormBody(req, maxBytes = DEFAULT_MAX_JSON_BODY_BYTES) {
|
|
125
|
-
const safeMaxBytes = Number.isFinite(maxBytes) && maxBytes > 0
|
|
126
|
-
? Math.floor(maxBytes)
|
|
127
|
-
: DEFAULT_MAX_JSON_BODY_BYTES;
|
|
128
|
-
return new Promise((resolve, reject) => {
|
|
129
|
-
let body = '';
|
|
130
|
-
let receivedBytes = 0;
|
|
131
|
-
req.on('data', chunk => {
|
|
132
|
-
receivedBytes += chunk.length;
|
|
133
|
-
if (receivedBytes > safeMaxBytes) {
|
|
134
|
-
req.pause();
|
|
135
|
-
reject(createHttpError(`Payload too large (max ${safeMaxBytes} bytes)`, 413));
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
body += chunk;
|
|
139
|
-
});
|
|
140
|
-
req.on('end', () => {
|
|
141
|
-
try {
|
|
142
|
-
const params = new URLSearchParams(body);
|
|
143
|
-
const parsed = {};
|
|
144
|
-
params.forEach((value, key) => {
|
|
145
|
-
parsed[String(key)] = String(value);
|
|
146
|
-
});
|
|
147
|
-
resolve(parsed);
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
reject(createHttpError('Invalid form body', 400));
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
req.on('error', reject);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
function escapeHtml(value) {
|
|
157
|
-
return String(value || '')
|
|
158
|
-
.replace(/&/g, '&')
|
|
159
|
-
.replace(/</g, '<')
|
|
160
|
-
.replace(/>/g, '>')
|
|
161
|
-
.replace(/"/g, '"')
|
|
162
|
-
.replace(/'/g, ''');
|
|
163
|
-
}
|
|
164
105
|
function resolveErrorStatusCode(error, fallback = 400) {
|
|
165
106
|
if (error instanceof TaskforceRuleError)
|
|
166
107
|
return error.statusCode;
|
|
@@ -206,12 +147,73 @@ function inferMcpOauthProviderFromRedirectUris(redirectUris) {
|
|
|
206
147
|
}
|
|
207
148
|
return MCP_OAUTH_DEFAULT_PROVIDER;
|
|
208
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Create API routes for Taskforce
|
|
152
|
+
*
|
|
153
|
+
* Core routes (29 total):
|
|
154
|
+
*
|
|
155
|
+
* **Task Management (12 routes)**
|
|
156
|
+
* - GET /api/taskforce/config
|
|
157
|
+
* - GET /api/taskforce/tasks
|
|
158
|
+
* - POST /api/taskforce/task
|
|
159
|
+
* - PATCH /api/taskforce/task/:id
|
|
160
|
+
* - DELETE /api/taskforce/task/:id
|
|
161
|
+
* - GET /api/taskforce/archive
|
|
162
|
+
* - POST /api/taskforce/task/:id/complete
|
|
163
|
+
* - POST /api/taskforce/task/:id/cancel
|
|
164
|
+
* - POST /api/taskforce/task/:id/comment
|
|
165
|
+
* - POST /api/taskforce/task/:id/unarchive
|
|
166
|
+
* - POST /api/taskforce/bulk-create
|
|
167
|
+
* - POST /api/taskforce/bulk-archive
|
|
168
|
+
* - POST /api/taskforce/bulk-update-fields
|
|
169
|
+
* - POST /api/taskforce/bulk-update-fields/preview
|
|
170
|
+
*
|
|
171
|
+
* **Configuration Management**
|
|
172
|
+
* - GET /api/taskforce/categories
|
|
173
|
+
* - POST /api/taskforce/categories
|
|
174
|
+
* - GET /api/taskforce/types
|
|
175
|
+
* - POST /api/taskforce/types
|
|
176
|
+
* - GET /api/taskforce/priorities
|
|
177
|
+
* - POST /api/taskforce/priorities
|
|
178
|
+
* - GET /api/taskforce/taxonomies
|
|
179
|
+
* - POST /api/taskforce/taxonomies
|
|
180
|
+
*
|
|
181
|
+
* **Workflow Settings**
|
|
182
|
+
* - GET /api/taskforce/approaches
|
|
183
|
+
* - POST /api/taskforce/approaches
|
|
184
|
+
*
|
|
185
|
+
* **Templates & Setup**
|
|
186
|
+
* - GET /api/taskforce/initiative-templates
|
|
187
|
+
* - POST /api/taskforce/initiative-templates/create
|
|
188
|
+
*
|
|
189
|
+
* **Advanced Features (3 routes)**
|
|
190
|
+
* - GET /api/taskforce/folders
|
|
191
|
+
* - POST /api/taskforce/validate-paths
|
|
192
|
+
* - POST /api/taskforce/export-resources
|
|
193
|
+
* - GET /api/taskforce/health
|
|
194
|
+
*/
|
|
209
195
|
export function createRoutes(core, context = {}) {
|
|
210
196
|
const routes = [];
|
|
211
197
|
const uiStateCache = new Map();
|
|
212
198
|
const addRoute = (method, path, handler) => {
|
|
199
|
+
const wrappedHandler = async (req, res, params) => {
|
|
200
|
+
const runtimeHeader = requestHeader(req, 'x-taskforce-runtime-mode', '').trim().toLowerCase();
|
|
201
|
+
const runtimeMode = runtimeHeader === 'cloud'
|
|
202
|
+
? 'cloud'
|
|
203
|
+
: runtimeHeader === 'local'
|
|
204
|
+
? 'local'
|
|
205
|
+
: (context.authConfig?.runtimeMode || requestRuntimeMode(req));
|
|
206
|
+
if (runtimeMode === 'cloud' && shouldRequireAuthenticatedCloudApiRoute(path, context)) {
|
|
207
|
+
const access = resolveRequestAccess(req, context.authConfig);
|
|
208
|
+
if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
|
|
209
|
+
writeRouteAuthenticationRequired(res);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
await handler(req, res, params);
|
|
214
|
+
};
|
|
213
215
|
const { pattern, keys } = createRoutePattern(`${method}:${path}`);
|
|
214
|
-
routes.push({ pattern, keys, handler });
|
|
216
|
+
routes.push({ pattern, keys, handler: wrappedHandler });
|
|
215
217
|
};
|
|
216
218
|
const buildUiStateCacheKey = (workspaceId, userId, stateKey) => `${workspaceId}::${userId}::${stateKey}`;
|
|
217
219
|
const readCachedUiState = (workspaceId, userId, stateKey) => {
|
|
@@ -299,33 +301,6 @@ export function createRoutes(core, context = {}) {
|
|
|
299
301
|
return null;
|
|
300
302
|
return resolved;
|
|
301
303
|
};
|
|
302
|
-
const shouldExposeDevAuthTokens = () => {
|
|
303
|
-
const explicit = String(process.env.TASKFORCE_AUTH_EXPOSE_DEV_TOKENS || '').trim().toLowerCase();
|
|
304
|
-
if (explicit === '1' || explicit === 'true' || explicit === 'yes')
|
|
305
|
-
return true;
|
|
306
|
-
if (explicit === '0' || explicit === 'false' || explicit === 'no')
|
|
307
|
-
return false;
|
|
308
|
-
return String(process.env.NODE_ENV || '').trim().toLowerCase() !== 'production';
|
|
309
|
-
};
|
|
310
|
-
const resolveRuntimeConfigBaseUrl = (req) => {
|
|
311
|
-
const host = String(req.headers['x-forwarded-host'] || req.headers.host || '').trim();
|
|
312
|
-
if (!host)
|
|
313
|
-
return '';
|
|
314
|
-
const proto = String(req.headers['x-forwarded-proto'] || '').trim().toLowerCase() === 'https'
|
|
315
|
-
? 'https'
|
|
316
|
-
: (req.socket?.encrypted ? 'https' : 'http');
|
|
317
|
-
return `${proto}://${host}`.replace(/\/+$/, '');
|
|
318
|
-
};
|
|
319
|
-
const resolvePublicAuthBaseUrl = (req) => {
|
|
320
|
-
const configured = String(process.env.TASKFORCE_AUTH_PUBLIC_BASE_URL || '').trim().replace(/\/+$/, '');
|
|
321
|
-
if (configured)
|
|
322
|
-
return configured;
|
|
323
|
-
const host = String(req.headers['x-forwarded-host'] || req.headers.host || '').trim();
|
|
324
|
-
const proto = String(req.headers['x-forwarded-proto'] || '').trim().toLowerCase() === 'http' ? 'http' : 'https';
|
|
325
|
-
if (!host)
|
|
326
|
-
return resolveDeploymentConfig(process.env).cloudBaseUrl || 'https://app.taskforcehq.ai';
|
|
327
|
-
return `${proto}://${host}`;
|
|
328
|
-
};
|
|
329
304
|
const logAuthLink = (kind, email, token, req) => {
|
|
330
305
|
const base = resolvePublicAuthBaseUrl(req);
|
|
331
306
|
const path = kind === 'verify-email'
|
|
@@ -333,34 +308,6 @@ export function createRoutes(core, context = {}) {
|
|
|
333
308
|
: `/login?mode=reset&token=${encodeURIComponent(token)}`;
|
|
334
309
|
console.info(`[Taskforce Auth] ${kind} email=${email} link=${base}${path}`);
|
|
335
310
|
};
|
|
336
|
-
const getEmailConfig = () => {
|
|
337
|
-
if (context.authConfig?.runtimeMode !== 'cloud') {
|
|
338
|
-
return resolveEffectiveEmailConfig(process.env, { provider: 'console' });
|
|
339
|
-
}
|
|
340
|
-
const globalSettings = core.getGlobalSettings();
|
|
341
|
-
return resolveEffectiveEmailConfig(process.env, globalSettings.email || null);
|
|
342
|
-
};
|
|
343
|
-
const getAuthBehavior = () => {
|
|
344
|
-
const globalSettings = core.getGlobalSettings();
|
|
345
|
-
const authSettings = globalSettings.auth || {};
|
|
346
|
-
const authRateLimitMaxRequestsRaw = Number(authSettings.authRateLimitMaxRequests);
|
|
347
|
-
const authRateLimitWindowMsRaw = Number(authSettings.authRateLimitWindowMs);
|
|
348
|
-
const onboardingPolicy = core.getOnboardingPolicy();
|
|
349
|
-
return {
|
|
350
|
-
allowSelfRegistration: resolveBooleanSetting(authSettings.allowSelfRegistration, true),
|
|
351
|
-
requireVerifiedEmail: resolveBooleanSetting(authSettings.requireVerifiedEmail, true),
|
|
352
|
-
defaultNewUserBetaAccess: resolveBooleanSetting(authSettings.defaultNewUserBetaAccess, false),
|
|
353
|
-
authRateLimitEnabled: resolveBooleanSetting(authSettings.authRateLimitEnabled, true),
|
|
354
|
-
authRateLimitMaxRequests: Number.isFinite(authRateLimitMaxRequestsRaw) && authRateLimitMaxRequestsRaw >= 1
|
|
355
|
-
? Math.floor(authRateLimitMaxRequestsRaw)
|
|
356
|
-
: 10,
|
|
357
|
-
authRateLimitWindowMs: Number.isFinite(authRateLimitWindowMsRaw) && authRateLimitWindowMsRaw >= 1000
|
|
358
|
-
? Math.floor(authRateLimitWindowMsRaw)
|
|
359
|
-
: 15 * 60_000,
|
|
360
|
-
onboardingPolicyMode: onboardingPolicy.mode,
|
|
361
|
-
onboardingPlanVersionId: onboardingPolicy.planVersionId
|
|
362
|
-
};
|
|
363
|
-
};
|
|
364
311
|
const deliverAuthEmail = async (kind, email, token, req) => {
|
|
365
312
|
const base = resolvePublicAuthBaseUrl(req);
|
|
366
313
|
const path = kind === 'verify-email'
|
|
@@ -378,7 +325,7 @@ export function createRoutes(core, context = {}) {
|
|
|
378
325
|
to: email,
|
|
379
326
|
subject,
|
|
380
327
|
text
|
|
381
|
-
},
|
|
328
|
+
}, getRouteEmailConfig(core, context));
|
|
382
329
|
if (result.ok) {
|
|
383
330
|
console.info(`[Taskforce Auth Email] kind=${kind} provider=${result.provider} to=${email} messageId=${result.messageId || '-'}`);
|
|
384
331
|
}
|
|
@@ -396,7 +343,7 @@ export function createRoutes(core, context = {}) {
|
|
|
396
343
|
to: email,
|
|
397
344
|
subject,
|
|
398
345
|
text
|
|
399
|
-
},
|
|
346
|
+
}, getRouteEmailConfig(core, context));
|
|
400
347
|
if (result.ok) {
|
|
401
348
|
console.info(`[Taskforce Auth Email] kind=invite provider=${result.provider} to=${email} messageId=${result.messageId || '-'}`);
|
|
402
349
|
}
|
|
@@ -799,7 +746,10 @@ export function createRoutes(core, context = {}) {
|
|
|
799
746
|
const contentDisposition = params.forceDownload
|
|
800
747
|
? `attachment; filename="${resolvedDownloadName.replace(/"/g, '')}"`
|
|
801
748
|
: 'inline';
|
|
802
|
-
|
|
749
|
+
const readFromObjectStorage = async () => {
|
|
750
|
+
if (!objectStorageClient || resolvedObjectStorage.provider !== 'r2' || !resolvedObjectStorage.r2) {
|
|
751
|
+
return { statusCode: 404 };
|
|
752
|
+
}
|
|
803
753
|
if (!params.forceDownload) {
|
|
804
754
|
const object = await objectStorageClient.getObject(params.asset.storageKey);
|
|
805
755
|
if (!object) {
|
|
@@ -820,19 +770,22 @@ export function createRoutes(core, context = {}) {
|
|
|
820
770
|
Location: objectStorageClient.createSignedGetUrl(params.asset.storageKey, resolvedObjectStorage.r2.signedDownloadTtlSeconds, { responseContentDisposition: contentDisposition })
|
|
821
771
|
}
|
|
822
772
|
};
|
|
773
|
+
};
|
|
774
|
+
if (params.asset.storageProvider === 'r2' && objectStorageClient && resolvedObjectStorage.provider === 'r2' && resolvedObjectStorage.r2) {
|
|
775
|
+
return readFromObjectStorage();
|
|
823
776
|
}
|
|
824
777
|
const filePath = ensureWithinDir(path.join(basePath, params.asset.storageKey), basePath);
|
|
825
|
-
if (
|
|
826
|
-
return {
|
|
778
|
+
if (filePath && fs.existsSync(filePath)) {
|
|
779
|
+
return {
|
|
780
|
+
statusCode: 200,
|
|
781
|
+
headers: {
|
|
782
|
+
'Content-Type': params.asset.mimeType || contentTypeForExt(ext),
|
|
783
|
+
'Content-Disposition': contentDisposition
|
|
784
|
+
},
|
|
785
|
+
body: fs.readFileSync(filePath)
|
|
786
|
+
};
|
|
827
787
|
}
|
|
828
|
-
return
|
|
829
|
-
statusCode: 200,
|
|
830
|
-
headers: {
|
|
831
|
-
'Content-Type': params.asset.mimeType || contentTypeForExt(ext),
|
|
832
|
-
'Content-Disposition': contentDisposition
|
|
833
|
-
},
|
|
834
|
-
body: fs.readFileSync(filePath)
|
|
835
|
-
};
|
|
788
|
+
return readFromObjectStorage();
|
|
836
789
|
};
|
|
837
790
|
const getAssetMetadata = (pathValue) => {
|
|
838
791
|
const canonical = getCanonicalAssetMetadata(pathValue);
|
|
@@ -1109,7 +1062,7 @@ export function createRoutes(core, context = {}) {
|
|
|
1109
1062
|
return null;
|
|
1110
1063
|
return workspaceId;
|
|
1111
1064
|
};
|
|
1112
|
-
const ensureContextUploadAllowed = (req, res, workspaceId) => {
|
|
1065
|
+
const ensureContextUploadAllowed = (req, res, workspaceId, options) => {
|
|
1113
1066
|
if (requestRuntimeMode(req) !== 'cloud')
|
|
1114
1067
|
return true;
|
|
1115
1068
|
if (!ensureAuthenticatedUser(req)) {
|
|
@@ -1120,12 +1073,17 @@ export function createRoutes(core, context = {}) {
|
|
|
1120
1073
|
const gate = core.evaluateFeatureAccess({
|
|
1121
1074
|
userId: requestUserId(req),
|
|
1122
1075
|
workspaceId,
|
|
1123
|
-
featureKey: 'context.uploads'
|
|
1076
|
+
featureKey: 'context.uploads',
|
|
1077
|
+
context: {
|
|
1078
|
+
requestedBytes: options?.requestedBytes
|
|
1079
|
+
}
|
|
1124
1080
|
});
|
|
1125
1081
|
if (!gate.allowed) {
|
|
1126
1082
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
1127
1083
|
res.end(JSON.stringify({
|
|
1128
|
-
error:
|
|
1084
|
+
error: gate.reasonCode === 'UPLOAD_STORAGE_LIMIT_REACHED'
|
|
1085
|
+
? 'This upload exceeds the document cloud storage limit for the current plan.'
|
|
1086
|
+
: 'Document cloud storage is unavailable for this account entitlement.',
|
|
1129
1087
|
code: gate.reasonCode
|
|
1130
1088
|
}));
|
|
1131
1089
|
return false;
|
|
@@ -1491,7 +1449,26 @@ export function createRoutes(core, context = {}) {
|
|
|
1491
1449
|
const auditAdminAction = makeAuditAdminAction(core, context);
|
|
1492
1450
|
const auditAuthAction = makeAuditAuthAction(core, requestWorkspaceId, context);
|
|
1493
1451
|
const ensureAdminRole = makeEnsureAdminRole(core, context);
|
|
1494
|
-
const ensureAuthenticatedUser = makeEnsureAuthenticatedUser();
|
|
1452
|
+
const ensureAuthenticatedUser = makeEnsureAuthenticatedUser(context.authConfig);
|
|
1453
|
+
const ensureWorkspaceAdminRole = (req, workspaceIdOverride) => {
|
|
1454
|
+
const hasWorkspaceOverride = typeof workspaceIdOverride === 'string' && workspaceIdOverride.trim().length > 0;
|
|
1455
|
+
const workspaceId = String(hasWorkspaceOverride ? workspaceIdOverride : requestWorkspaceId(req) || '').trim() || 'default';
|
|
1456
|
+
const userId = String(resolveAuthenticatedUserId(req) || '').trim();
|
|
1457
|
+
if (userId && userId !== 'anonymous') {
|
|
1458
|
+
const membership = core.listWorkspaceUsers(workspaceId).find((user) => String(user.userId || '').trim() === userId);
|
|
1459
|
+
if (membership) {
|
|
1460
|
+
const role = String(membership.role || '').trim().toLowerCase();
|
|
1461
|
+
const status = String(membership.status || '').trim().toLowerCase();
|
|
1462
|
+
return membership.disabled !== true
|
|
1463
|
+
&& status === 'active'
|
|
1464
|
+
&& (role === 'owner' || role === 'admin');
|
|
1465
|
+
}
|
|
1466
|
+
if (hasWorkspaceOverride)
|
|
1467
|
+
return false;
|
|
1468
|
+
}
|
|
1469
|
+
const fallbackRole = String(requestRole(req) || '').trim().toLowerCase();
|
|
1470
|
+
return fallbackRole === 'owner' || fallbackRole === 'admin';
|
|
1471
|
+
};
|
|
1495
1472
|
const isCollaborationPolicyEnabled = () => {
|
|
1496
1473
|
const rawDefault = String(process.env.TASKFORCE_COLLAB_POLICY_V1 || 'true').trim().toLowerCase();
|
|
1497
1474
|
const envDefaultEnabled = !(rawDefault === '0' || rawDefault === 'false' || rawDefault === 'off' || rawDefault === 'no');
|
|
@@ -1543,6 +1520,7 @@ export function createRoutes(core, context = {}) {
|
|
|
1543
1520
|
context,
|
|
1544
1521
|
requestWorkspaceId,
|
|
1545
1522
|
requestUserId,
|
|
1523
|
+
resolveAuthenticatedUserId,
|
|
1546
1524
|
requestRole,
|
|
1547
1525
|
ensureAppAccess,
|
|
1548
1526
|
ensureAuthenticatedUser,
|
|
@@ -1551,9 +1529,6 @@ export function createRoutes(core, context = {}) {
|
|
|
1551
1529
|
requireCloudRuntime,
|
|
1552
1530
|
auditAuthAction,
|
|
1553
1531
|
resolveAdminRequestActor,
|
|
1554
|
-
shouldExposeDevAuthTokens,
|
|
1555
|
-
resolveRuntimeConfigBaseUrl,
|
|
1556
|
-
resolvePublicAuthBaseUrl,
|
|
1557
1532
|
resolveEffectiveObjectStorageConfig,
|
|
1558
1533
|
getObjectStorageClient,
|
|
1559
1534
|
});
|
|
@@ -1604,10 +1579,6 @@ export function createRoutes(core, context = {}) {
|
|
|
1604
1579
|
appendDocumentPurgeHistory,
|
|
1605
1580
|
getCollaborationTelemetry,
|
|
1606
1581
|
isCollaborationPolicyEnabled,
|
|
1607
|
-
resolveRuntimeConfigBaseUrl,
|
|
1608
|
-
resolvePublicAuthBaseUrl,
|
|
1609
|
-
parseFormBody,
|
|
1610
|
-
escapeHtml,
|
|
1611
1582
|
normalizeMcpOauthScopeString,
|
|
1612
1583
|
MCP_TOKEN_SCOPE_VALUES,
|
|
1613
1584
|
MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS,
|