@taskforcehq/taskforce 0.3.305 → 0.3.307
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 +0 -2
- package/dist/Taskforce.module.css +603 -107
- package/dist/TaskforceCore.js +216 -43
- package/dist/TaskforceCore.test.js +611 -28
- 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 +46 -2
- package/dist/compat/workspaceSyncCompat.test.js +38 -1
- package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
- package/dist/components/activity/EntityActivityTimeline.js +81 -0
- package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
- package/dist/components/context/ContextAttachmentManager.js +561 -0
- package/dist/components/features/AgentsModule.d.ts +14 -1
- package/dist/components/features/AgentsModule.js +366 -18
- package/dist/components/features/AgentsModule.test.js +507 -8
- package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
- package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
- package/dist/components/features/DocumentIndex.d.ts +2 -2
- package/dist/components/features/DocumentIndex.js +3 -4
- package/dist/components/features/DocumentViewer.d.ts +7 -1
- package/dist/components/features/DocumentViewer.js +62 -4
- package/dist/components/features/DocumentWorkspace.d.ts +10 -2
- package/dist/components/features/DocumentWorkspace.js +15 -4
- package/dist/components/features/DocumentWorkspace.test.js +51 -1
- package/dist/components/features/TaskSettings.d.ts +2 -0
- package/dist/components/features/TaskSettings.js +83 -78
- package/dist/components/features/TaskSettings.test.js +171 -82
- package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
- package/dist/components/task/TaskActionHeader.js +11 -5
- package/dist/components/task/TaskActionHeader.test.js +10 -0
- package/dist/components/task/TaskCard.js +24 -29
- package/dist/components/task/TaskCard.test.js +38 -3
- package/dist/components/task/TaskContextUpload.js +4 -526
- package/dist/components/task/TaskContextUpload.test.js +3 -3
- package/dist/components/task/TaskForm.js +61 -116
- package/dist/components/task/TaskForm.test.js +110 -8
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +94 -21
- package/dist/components/task/TaskKanban.test.js +36 -0
- package/dist/components/task/TaskList.js +5 -1
- package/dist/components/task/TaskList.test.js +4 -0
- package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
- package/dist/components/ui/EditableAvatarButton.js +18 -0
- package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
- package/dist/components/ui/HierarchyActionBadge.js +5 -0
- package/dist/components/views/AppShellHeader.d.ts +3 -1
- package/dist/components/views/AppShellHeader.js +27 -11
- package/dist/components/views/AppShellHeader.test.js +18 -1
- package/dist/components/views/PlanComparisonPage.d.ts +9 -1
- package/dist/components/views/PlanComparisonPage.js +78 -14
- package/dist/components/views/PlanComparisonPage.test.js +257 -0
- package/dist/components/views/PlansPage.js +127 -36
- package/dist/components/views/PlansPage.test.js +294 -12
- package/dist/components/views/StandaloneLayout.js +529 -592
- package/dist/components/views/WidgetView.js +10 -8
- package/dist/components/views/panels/PlanningDrawer.d.ts +25 -5
- package/dist/components/views/panels/PlanningDrawer.js +270 -55
- package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
- package/dist/components/views/panels/PlanningDrawer.test.js +529 -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 +26 -1
- package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
- 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 -0
- package/dist/core/AiProfileService.d.ts +46 -2
- package/dist/core/AiProfileService.js +1073 -82
- package/dist/core/AiProfileService.test.js +19 -1
- package/dist/core/AiProfiles.test.js +1249 -44
- package/dist/core/AttachmentLinkService.d.ts +3 -0
- package/dist/core/AttachmentLinkService.js +110 -23
- 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.js +3 -3
- package/dist/core/EntitlementsPolicy.test.js +2 -2
- package/dist/core/GlobalSettingsService.js +127 -6
- package/dist/core/McpTokenService.d.ts +12 -0
- package/dist/core/McpTokenService.js +44 -1
- package/dist/core/PlanEntitlementService.d.ts +23 -2
- package/dist/core/PlanEntitlementService.js +343 -57
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +35 -2
- package/dist/core/PlanningEntities.test.js +418 -4
- package/dist/core/SignupMonetizationSettings.test.js +199 -27
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +42 -42
- package/dist/core/TaskActivityService.d.ts +28 -1
- package/dist/core/TaskActivityService.js +227 -46
- package/dist/core/TaskActivitySync.test.d.ts +1 -0
- package/dist/core/TaskActivitySync.test.js +90 -0
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/TaskAuditTimeline.test.js +96 -0
- package/dist/core/Taskforce.d.ts +415 -40
- package/dist/core/Taskforce.ids.test.js +58 -0
- package/dist/core/Taskforce.js +1543 -266
- package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
- package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
- package/dist/core/Taskforce.mcpAuth.test.js +109 -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 +117 -16
- package/dist/core/shared.d.ts +1 -1
- package/dist/core/shared.js +5 -1
- package/dist/core/types.d.ts +75 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +28 -2
- package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
- package/dist/hooks/bootstrapTimeouts.js +8 -0
- package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
- package/dist/hooks/bootstrapTimeouts.test.js +12 -0
- 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 +13 -4
- package/dist/hooks/sync/orchestratorShared.js +129 -48
- 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 +179 -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 +210 -66
- package/dist/hooks/sync/transfers.test.d.ts +1 -0
- package/dist/hooks/sync/transfers.test.js +928 -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/useFilterSort.js +13 -13
- package/dist/hooks/useFilterSort.test.js +87 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
- package/dist/hooks/useSyncOrchestrator.js +409 -183
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- package/dist/hooks/useTaskforce.d.ts +145 -52
- package/dist/hooks/useTaskforce.js +592 -253
- package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.runtime-routing.test.js +246 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +2468 -201
- package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.js +13 -2
- package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +328 -40
- 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/canonicalAssetHelpers.d.ts +16 -4
- package/dist/mcp/canonicalAssetHelpers.js +18 -10
- package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
- package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
- package/dist/mcp/clientIntegrations.d.ts +1 -0
- package/dist/mcp/clientIntegrations.js +25 -0
- package/dist/mcp/clientIntegrations.test.js +14 -3
- package/dist/mcp/clientProfileDefaults.js +3 -3
- package/dist/mcp/collaborationRegistrar.js +7 -7
- package/dist/mcp/documentAssetRegistrar.js +116 -10
- package/dist/mcp/runtime.d.ts +3 -0
- package/dist/mcp/runtime.js +1099 -185
- package/dist/mcp/runtime.test.js +1491 -140
- package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
- package/dist/mcp/taskPlanningRegistrar.js +48 -0
- package/dist/mcp/toolRegistry.d.ts +56 -0
- package/dist/mcp/toolRegistry.js +8 -0
- package/dist/mcp/toolRegistry.test.js +4 -0
- package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
- package/dist/migrations/taskSchemaMigrations.js +192 -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/aiProfileAvatarAssets.d.ts +49 -0
- package/dist/server/aiProfileAvatarAssets.js +319 -0
- package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
- package/dist/server/aiProfileAvatarAssets.test.js +213 -0
- package/dist/server/annotatedAttachmentsRoutes.test.js +69 -10
- 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 +124 -9
- package/dist/server/index.rateLimit.test.js +3 -0
- package/dist/server/index.test.js +111 -3
- package/dist/server/routes/admin.d.ts +3 -0
- package/dist/server/routes/admin.js +459 -28
- package/dist/server/routes/annotatedAttachments.js +51 -0
- package/dist/server/routes/auth.d.ts +26 -2
- package/dist/server/routes/auth.js +1170 -71
- package/dist/server/routes/authSupport.d.ts +5 -1
- package/dist/server/routes/authSupport.js +13 -2
- package/dist/server/routes/billing.js +372 -35
- package/dist/server/routes/billing.test.js +595 -12
- package/dist/server/routes/documents.d.ts +1 -0
- package/dist/server/routes/documents.js +182 -70
- package/dist/server/routes/shared.d.ts +28 -2
- package/dist/server/routes/shared.js +69 -14
- package/dist/server/routes/sync.integration.test.js +658 -39
- package/dist/server/routes/sync.js +74 -608
- 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 +182 -0
- package/dist/server/routes/tasks.js +55 -10
- package/dist/server/routes/workspaces.js +36 -5
- package/dist/server/routes.js +147 -11
- package/dist/server/routes.test.js +2704 -183
- 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/aiProfileSurfaceType.d.ts +1 -1
- package/dist/shared/aiProfileSurfaceType.js +10 -8
- 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/objectStorage.d.ts +1 -0
- package/dist/storage/objectStorage.js +2 -1
- package/dist/storage/objectStorageClient.d.ts +1 -0
- package/dist/storage/objectStorageClient.js +41 -8
- package/dist/storage/objectStorageClient.test.js +26 -1
- package/dist/storage/objectStorageConfig.js +3 -1
- package/dist/storage/objectStorageConfig.test.js +13 -1
- 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 +19 -11
- package/dist/sync/collaborationSyncPayload.test.js +14 -1
- package/dist/sync/collaborationSyncState.d.ts +3 -3
- package/dist/sync/collaborationSyncState.js +19 -12
- package/dist/sync/contentSyncPayload.d.ts +5 -1
- package/dist/sync/contentSyncPayload.js +9 -0
- package/dist/sync/contentSyncPayload.test.js +39 -0
- package/dist/sync/contentSyncState.d.ts +9 -9
- package/dist/sync/contentSyncState.js +248 -107
- package/dist/sync/planningSyncPayload.js +5 -0
- package/dist/sync/planningSyncPayload.test.js +24 -0
- package/dist/sync/syncApplyHandlers.d.ts +11 -4
- package/dist/sync/syncApplyHandlers.js +115 -42
- package/dist/sync/syncApplyHandlers.test.js +251 -4
- package/dist/sync/syncFieldSemantics.d.ts +12 -0
- package/dist/sync/syncFieldSemantics.js +55 -0
- package/dist/sync/syncResourceAdapters.d.ts +46 -0
- package/dist/sync/syncResourceAdapters.js +79 -0
- package/dist/sync/syncService.d.ts +6 -0
- package/dist/sync/syncService.js +65 -1
- package/dist/sync/syncService.test.js +96 -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.d.ts +4 -0
- package/dist/sync/workspacePullFeed.js +132 -72
- package/dist/sync/workspacePullFeed.test.js +92 -0
- package/dist/sync/workspaceRepair.js +5 -2
- package/dist/sync/workspaceSyncModel.d.ts +73 -6
- package/dist/sync/workspaceSyncModel.js +342 -73
- package/dist/sync/workspaceSyncModel.test.js +559 -14
- package/dist/sync/workspaceSyncState.d.ts +17 -0
- package/dist/sync/workspaceSyncState.js +2 -1
- package/dist/sync/workspaceSyncSurface.js +3 -8
- package/dist/types.d.ts +50 -2
- package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
- package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
- 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-D4rJKXzd.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
- package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
- package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
- package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-D4XseTwW.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-D5AcbO0L.js +1 -0
- package/dist/ui/assets/PlansPage-Dvqsz5zc.css +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/TaskContextUpload-uC5qx4Bv.js +1 -0
- package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-oOA9bcdK.js} +4 -4
- package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
- package/dist/ui/assets/index-607WPo_X.js +4 -0
- package/dist/ui/assets/index-CYyodXsg.css +1 -0
- package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-B32hGuF2.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/ui/product/agent manager.png +0 -0
- package/dist/ui/product/agent_manager_02.png +0 -0
- package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
- package/dist/utils/aiProfileEvents.d.ts +7 -0
- package/dist/utils/aiProfileEvents.js +6 -0
- package/dist/utils/assignees.d.ts +8 -0
- package/dist/utils/assignees.js +10 -1
- package/dist/utils/assignees.test.d.ts +1 -0
- package/dist/utils/assignees.test.js +25 -0
- package/dist/utils/avatarDisplay.d.ts +1 -0
- package/dist/utils/avatarDisplay.js +15 -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.js +3 -0
- package/dist/utils/contextAssetEvents.d.ts +2 -0
- package/dist/utils/contextFiles.d.ts +3 -1
- package/dist/utils/contextFiles.js +2 -1
- 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 +9 -1
- package/dist/utils/taskActivity.js +290 -22
- package/dist/utils/taskActivity.test.js +141 -8
- package/dist/utils/taskSearch.d.ts +22 -0
- package/dist/utils/taskSearch.js +87 -0
- package/dist/utils/taskSearch.test.d.ts +1 -0
- package/dist/utils/taskSearch.test.js +69 -0
- package/dist/utils/taskUtils.js +10 -3
- package/dist/utils/taskUtils.test.d.ts +1 -0
- package/dist/utils/taskUtils.test.js +19 -0
- 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 +3 -2
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- 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-N2MnQBZk.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BG6P_GVW.js +0 -3
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
- package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/TaskSettings-ln0aF7xc.js +0 -9
- package/dist/ui/assets/index-C2-OXZV7.css +0 -1
- package/dist/ui/assets/index-DcSI5F86.js +0 -6
- package/scripts/migrate-planning-model.ts +0 -233
|
@@ -199,7 +199,69 @@ describe('annotated attachment routes', () => {
|
|
|
199
199
|
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
200
200
|
}
|
|
201
201
|
});
|
|
202
|
-
it('
|
|
202
|
+
it('lists canonical image assets for the Image Notes tray', async () => {
|
|
203
|
+
const projectRoot = createProjectRoot();
|
|
204
|
+
const core = new TaskforceCore({
|
|
205
|
+
projectRoot,
|
|
206
|
+
storagePath: path.join(projectRoot, '.taskforce'),
|
|
207
|
+
});
|
|
208
|
+
try {
|
|
209
|
+
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
210
|
+
const category = core.getCategories()[0]?.value;
|
|
211
|
+
const type = core.getTypes()[0]?.value;
|
|
212
|
+
const task = core.createTask({ title: 'Review image', category, type }, { workspaceId: 'workspace-1', actorRef: 'owner-1' });
|
|
213
|
+
const assetStore = core.getWorkspaceAssetStore();
|
|
214
|
+
const asset = assetStore.upsertAsset({
|
|
215
|
+
assetId: 'asset-image-1',
|
|
216
|
+
workspaceId: 'workspace-1',
|
|
217
|
+
kind: 'image',
|
|
218
|
+
originalFilename: 'ui-review.png',
|
|
219
|
+
logicalName: 'UI Review',
|
|
220
|
+
mimeType: 'image/png',
|
|
221
|
+
storageProvider: 'local',
|
|
222
|
+
storageKey: 'workspaces/workspace-1/assets/images/asset-image-1-ui-review.png',
|
|
223
|
+
contentSha256: 'sha-image-1',
|
|
224
|
+
sizeBytes: 256,
|
|
225
|
+
});
|
|
226
|
+
assetStore.upsertLink({
|
|
227
|
+
workspaceId: 'workspace-1',
|
|
228
|
+
assetId: asset.assetId,
|
|
229
|
+
taskId: task.id,
|
|
230
|
+
role: 'attachment',
|
|
231
|
+
});
|
|
232
|
+
core.createAnnotatedAttachmentSession({
|
|
233
|
+
workspaceId: 'workspace-1',
|
|
234
|
+
taskId: '',
|
|
235
|
+
baseImageAssetId: asset.assetId,
|
|
236
|
+
title: 'UI Review session',
|
|
237
|
+
actorId: 'owner-1',
|
|
238
|
+
});
|
|
239
|
+
const routes = createRoutes(core);
|
|
240
|
+
const listImagesMatch = matchRoute('GET', '/api/taskforce/annotated-attachments/images', routes);
|
|
241
|
+
const listImagesReq = createMockReq('GET', '/api/taskforce/annotated-attachments/images?workspaceId=workspace-1');
|
|
242
|
+
const listImagesRes = createMockRes();
|
|
243
|
+
await listImagesMatch?.handler(listImagesReq, listImagesRes, {});
|
|
244
|
+
expect(listImagesRes.statusCode).toBe(200);
|
|
245
|
+
expect(listImagesRes.getHeader('cache-control')).toBe('no-store');
|
|
246
|
+
const listed = JSON.parse(listImagesRes.__getBody());
|
|
247
|
+
expect(listed.images).toHaveLength(1);
|
|
248
|
+
expect(listed.images[0]).toMatchObject({
|
|
249
|
+
assetId: asset.assetId,
|
|
250
|
+
displayName: 'UI Review',
|
|
251
|
+
path: '/api/taskforce/context/workspaces%2Fworkspace-1%2Fassets%2Fimages%2Fasset-image-1-ui-review.png',
|
|
252
|
+
attachmentCount: 1,
|
|
253
|
+
sessionCount: 1,
|
|
254
|
+
taskId: task.id,
|
|
255
|
+
taskReferenceLabel: task.referenceLabel,
|
|
256
|
+
});
|
|
257
|
+
expect(listed.images[0].imageReferenceLabel).toMatch(/^I-\d+$/);
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
core.close();
|
|
261
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
it('rejects local mutation attribution when only an active workspace user fallback is available', async () => {
|
|
203
265
|
const projectRoot = createProjectRoot();
|
|
204
266
|
const core = new TaskforceCore({
|
|
205
267
|
projectRoot,
|
|
@@ -209,9 +271,9 @@ describe('annotated attachment routes', () => {
|
|
|
209
271
|
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
210
272
|
const now = new Date().toISOString();
|
|
211
273
|
core.db.prepare(`
|
|
212
|
-
INSERT INTO users (id, tenant_id, email, display_name,
|
|
213
|
-
VALUES (?, ?, ?, ?, ?, ?, ?,
|
|
214
|
-
`).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User',
|
|
274
|
+
INSERT INTO users (id, tenant_id, email, display_name, beta_access, is_disabled, created_at, updated_at)
|
|
275
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
276
|
+
`).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User', 1, 0, now, now);
|
|
215
277
|
core.db.prepare(`
|
|
216
278
|
INSERT INTO workspace_memberships (id, tenant_id, workspace_id, user_id, role, status, created_at, updated_at)
|
|
217
279
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -252,13 +314,10 @@ describe('annotated attachment routes', () => {
|
|
|
252
314
|
});
|
|
253
315
|
const createRes = createMockRes();
|
|
254
316
|
await createMatch?.handler(createReq, createRes, {});
|
|
255
|
-
expect(createRes.statusCode).toBe(
|
|
317
|
+
expect(createRes.statusCode).toBe(401);
|
|
256
318
|
expect(JSON.parse(createRes.__getBody())).toMatchObject({
|
|
257
|
-
success:
|
|
258
|
-
|
|
259
|
-
createdByActorId: 'user-local-1',
|
|
260
|
-
updatedByActorId: 'user-local-1',
|
|
261
|
-
}),
|
|
319
|
+
success: false,
|
|
320
|
+
code: 'MUTATION_AUTH_REQUIRED',
|
|
262
321
|
});
|
|
263
322
|
}
|
|
264
323
|
finally {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Sign In adapter.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Redirect user to buildAppleAuthUrl() with response_mode=form_post.
|
|
6
|
+
* 2. Apple POSTs `code`, `state`, `id_token` and optional `user` JSON to the callback.
|
|
7
|
+
* 3. Call exchangeAppleCode() which mints the client-secret JWT, exchanges the code,
|
|
8
|
+
* and decodes the id_token payload for the canonical profile.
|
|
9
|
+
*
|
|
10
|
+
* Notes:
|
|
11
|
+
* - Apple never re-sends the user's name after the very first authorisation.
|
|
12
|
+
* If the user JSON arrives on first login it is captured; subsequent logins
|
|
13
|
+
* use the snapshot stored in user_auth_identities.
|
|
14
|
+
* - Private-relay email addresses contain `.privaterelay.appleid.com`; the
|
|
15
|
+
* `is_private_email` claim is set to `true` in that case.
|
|
16
|
+
*/
|
|
17
|
+
import type { NormalizedOAuthProfile } from './types.js';
|
|
18
|
+
export declare function buildAppleAuthUrl(input: {
|
|
19
|
+
servicesId: string;
|
|
20
|
+
redirectUri: string;
|
|
21
|
+
state: string;
|
|
22
|
+
nonce: string;
|
|
23
|
+
}): string;
|
|
24
|
+
export declare function exchangeAppleCode(input: {
|
|
25
|
+
code: string;
|
|
26
|
+
redirectUri: string;
|
|
27
|
+
servicesId: string;
|
|
28
|
+
teamId: string;
|
|
29
|
+
keyId: string;
|
|
30
|
+
privateKeyPem: string;
|
|
31
|
+
/** Optional: JSON string Apple sends on first authorisation containing { name }. */
|
|
32
|
+
userJson?: string | null;
|
|
33
|
+
}): Promise<NormalizedOAuthProfile>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getAppleClientSecret } from './appleClientSecret.js';
|
|
2
|
+
export function buildAppleAuthUrl(input) {
|
|
3
|
+
const params = new URLSearchParams({
|
|
4
|
+
client_id: input.servicesId,
|
|
5
|
+
redirect_uri: input.redirectUri,
|
|
6
|
+
response_type: 'code id_token',
|
|
7
|
+
scope: 'name email',
|
|
8
|
+
state: input.state,
|
|
9
|
+
nonce: input.nonce,
|
|
10
|
+
response_mode: 'form_post',
|
|
11
|
+
});
|
|
12
|
+
return `https://appleid.apple.com/auth/authorize?${params.toString()}`;
|
|
13
|
+
}
|
|
14
|
+
export async function exchangeAppleCode(input) {
|
|
15
|
+
const clientSecret = getAppleClientSecret({
|
|
16
|
+
servicesId: input.servicesId,
|
|
17
|
+
teamId: input.teamId,
|
|
18
|
+
keyId: input.keyId,
|
|
19
|
+
privateKeyPem: input.privateKeyPem,
|
|
20
|
+
});
|
|
21
|
+
// Step 1: exchange authorisation code for tokens
|
|
22
|
+
const tokenRes = await fetch('https://appleid.apple.com/auth/token', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
25
|
+
body: new URLSearchParams({
|
|
26
|
+
grant_type: 'authorization_code',
|
|
27
|
+
code: input.code,
|
|
28
|
+
redirect_uri: input.redirectUri,
|
|
29
|
+
client_id: input.servicesId,
|
|
30
|
+
client_secret: clientSecret,
|
|
31
|
+
}).toString(),
|
|
32
|
+
});
|
|
33
|
+
if (!tokenRes.ok) {
|
|
34
|
+
const body = await tokenRes.text().catch(() => '');
|
|
35
|
+
throw new Error(`Apple token exchange failed (${tokenRes.status}): ${body}`);
|
|
36
|
+
}
|
|
37
|
+
const tokens = await tokenRes.json();
|
|
38
|
+
const idToken = String(tokens.id_token || '').trim();
|
|
39
|
+
if (!idToken)
|
|
40
|
+
throw new Error('Apple token exchange: missing id_token');
|
|
41
|
+
// Step 2: decode id_token payload (trust Apple — skip signature verification here)
|
|
42
|
+
const parts = idToken.split('.');
|
|
43
|
+
if (parts.length !== 3)
|
|
44
|
+
throw new Error('Apple id_token: not a valid JWT');
|
|
45
|
+
let payload;
|
|
46
|
+
try {
|
|
47
|
+
payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new Error('Apple id_token: could not decode payload');
|
|
51
|
+
}
|
|
52
|
+
const sub = String(payload.sub || '').trim();
|
|
53
|
+
if (!sub)
|
|
54
|
+
throw new Error('Apple id_token: missing sub');
|
|
55
|
+
const email = String(payload.email || '').trim().toLowerCase() || null;
|
|
56
|
+
const emailVerified = payload.email_verified === true || payload.email_verified === 'true';
|
|
57
|
+
const isPrivateEmail = payload.is_private_email === true || payload.is_private_email === 'true';
|
|
58
|
+
// Step 3: extract display name from one-time `user` JSON (first-login only)
|
|
59
|
+
let displayName = null;
|
|
60
|
+
if (input.userJson) {
|
|
61
|
+
try {
|
|
62
|
+
const userObj = JSON.parse(input.userJson);
|
|
63
|
+
const firstName = String(userObj?.name?.firstName || '').trim();
|
|
64
|
+
const lastName = String(userObj?.name?.lastName || '').trim();
|
|
65
|
+
const full = [firstName, lastName].filter(Boolean).join(' ');
|
|
66
|
+
if (full)
|
|
67
|
+
displayName = full;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// malformed — ignore
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
provider: 'apple',
|
|
75
|
+
providerSubject: sub,
|
|
76
|
+
email,
|
|
77
|
+
emailVerified,
|
|
78
|
+
displayName,
|
|
79
|
+
avatarUrl: null, // Apple never provides an avatar
|
|
80
|
+
isPrivateEmail,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple OAuth client-secret JWT minter.
|
|
3
|
+
*
|
|
4
|
+
* Apple Sign In requires a short-lived JWT as the OAuth `client_secret` rather
|
|
5
|
+
* than a static secret. The JWT must be:
|
|
6
|
+
* - Signed with ES256 (ECDSA P-256) using the private key registered in App Store Connect
|
|
7
|
+
* - Valid for at most 6 months (Apple enforces this; we use 10 min for freshness)
|
|
8
|
+
* - Cached so callers don't re-sign on every request
|
|
9
|
+
*
|
|
10
|
+
* Reference: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
|
|
11
|
+
*/
|
|
12
|
+
export interface AppleClientSecretConfig {
|
|
13
|
+
/** 10-character Apple Team ID */
|
|
14
|
+
teamId: string;
|
|
15
|
+
/** Services ID (bundle ID) registered in App Store Connect */
|
|
16
|
+
servicesId: string;
|
|
17
|
+
/** Key ID of the registered Sign In with Apple key */
|
|
18
|
+
keyId: string;
|
|
19
|
+
/** PEM-encoded EC P-256 private key downloaded from App Store Connect */
|
|
20
|
+
privateKeyPem: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Returns a cached client-secret JWT for the given Apple config, minting a
|
|
24
|
+
* fresh one if the cache entry is absent or within 60 s of expiry.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getAppleClientSecret(cfg: AppleClientSecretConfig): string;
|
|
27
|
+
/**
|
|
28
|
+
* Force-mints a fresh JWT (bypasses cache) for health-check purposes.
|
|
29
|
+
* Returns success/error without updating the cache.
|
|
30
|
+
*/
|
|
31
|
+
export declare function testMintAppleClientSecret(cfg: AppleClientSecretConfig): {
|
|
32
|
+
ok: true;
|
|
33
|
+
token: string;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
error: string;
|
|
37
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple OAuth client-secret JWT minter.
|
|
3
|
+
*
|
|
4
|
+
* Apple Sign In requires a short-lived JWT as the OAuth `client_secret` rather
|
|
5
|
+
* than a static secret. The JWT must be:
|
|
6
|
+
* - Signed with ES256 (ECDSA P-256) using the private key registered in App Store Connect
|
|
7
|
+
* - Valid for at most 6 months (Apple enforces this; we use 10 min for freshness)
|
|
8
|
+
* - Cached so callers don't re-sign on every request
|
|
9
|
+
*
|
|
10
|
+
* Reference: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
|
|
11
|
+
*/
|
|
12
|
+
import { sign } from 'crypto';
|
|
13
|
+
// ~9 min — well under Apple's 10-min max, allows safe margin for clock skew
|
|
14
|
+
const CACHE_TTL_MS = 9 * 60 * 1000;
|
|
15
|
+
// Apple max is 6 months; we use 10 min so tokens stay fresh
|
|
16
|
+
const TOKEN_LIFETIME_SECONDS = 600;
|
|
17
|
+
const secretCache = new Map();
|
|
18
|
+
function configCacheKey(cfg) {
|
|
19
|
+
return `${cfg.teamId}:${cfg.servicesId}:${cfg.keyId}`;
|
|
20
|
+
}
|
|
21
|
+
function buildJwt(cfg) {
|
|
22
|
+
const now = Math.floor(Date.now() / 1000);
|
|
23
|
+
const header = Buffer.from(JSON.stringify({ alg: 'ES256', kid: cfg.keyId })).toString('base64url');
|
|
24
|
+
const payload = Buffer.from(JSON.stringify({
|
|
25
|
+
iss: cfg.teamId,
|
|
26
|
+
iat: now,
|
|
27
|
+
exp: now + TOKEN_LIFETIME_SECONDS,
|
|
28
|
+
aud: 'https://appleid.apple.com',
|
|
29
|
+
sub: cfg.servicesId
|
|
30
|
+
})).toString('base64url');
|
|
31
|
+
const signingInput = `${header}.${payload}`;
|
|
32
|
+
// sign() with dsaEncoding: 'ieee-p1363' produces raw r||s (64 bytes) required by JWT ES256
|
|
33
|
+
const signature = sign('SHA256', Buffer.from(signingInput), {
|
|
34
|
+
key: cfg.privateKeyPem,
|
|
35
|
+
dsaEncoding: 'ieee-p1363'
|
|
36
|
+
}).toString('base64url');
|
|
37
|
+
return `${signingInput}.${signature}`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns a cached client-secret JWT for the given Apple config, minting a
|
|
41
|
+
* fresh one if the cache entry is absent or within 60 s of expiry.
|
|
42
|
+
*/
|
|
43
|
+
export function getAppleClientSecret(cfg) {
|
|
44
|
+
const key = configCacheKey(cfg);
|
|
45
|
+
const cached = secretCache.get(key);
|
|
46
|
+
if (cached && cached.expiresAt > Date.now() + 60_000) {
|
|
47
|
+
return cached.token;
|
|
48
|
+
}
|
|
49
|
+
const token = buildJwt(cfg);
|
|
50
|
+
secretCache.set(key, { token, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
51
|
+
return token;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Force-mints a fresh JWT (bypasses cache) for health-check purposes.
|
|
55
|
+
* Returns success/error without updating the cache.
|
|
56
|
+
*/
|
|
57
|
+
export function testMintAppleClientSecret(cfg) {
|
|
58
|
+
try {
|
|
59
|
+
const token = buildJwt(cfg);
|
|
60
|
+
return { ok: true, token };
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub OAuth adapter.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Redirect user to buildGithubAuthUrl() (includes PKCE S256 for GitHub Apps).
|
|
6
|
+
* 2. Receive code on callback; call exchangeGithubCode() to get profile.
|
|
7
|
+
*
|
|
8
|
+
* Email selection rules:
|
|
9
|
+
* - Prefer the primary + verified email from /user/emails.
|
|
10
|
+
* - Fall back to any verified email, then to user.email (public profile field).
|
|
11
|
+
* - If none is available, email is null.
|
|
12
|
+
*/
|
|
13
|
+
import type { NormalizedOAuthProfile } from './types.js';
|
|
14
|
+
export declare function buildGithubAuthUrl(input: {
|
|
15
|
+
clientId: string;
|
|
16
|
+
redirectUri: string;
|
|
17
|
+
state: string;
|
|
18
|
+
codeChallenge: string;
|
|
19
|
+
}): string;
|
|
20
|
+
export declare function exchangeGithubCode(input: {
|
|
21
|
+
code: string;
|
|
22
|
+
codeVerifier: string;
|
|
23
|
+
redirectUri: string;
|
|
24
|
+
clientId: string;
|
|
25
|
+
clientSecret: string;
|
|
26
|
+
}): Promise<NormalizedOAuthProfile>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export function buildGithubAuthUrl(input) {
|
|
2
|
+
const params = new URLSearchParams({
|
|
3
|
+
client_id: input.clientId,
|
|
4
|
+
redirect_uri: input.redirectUri,
|
|
5
|
+
state: input.state,
|
|
6
|
+
scope: 'read:user user:email',
|
|
7
|
+
code_challenge: input.codeChallenge,
|
|
8
|
+
code_challenge_method: 'S256',
|
|
9
|
+
});
|
|
10
|
+
return `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
11
|
+
}
|
|
12
|
+
export async function exchangeGithubCode(input) {
|
|
13
|
+
// Step 1: exchange authorisation code for access token
|
|
14
|
+
const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
18
|
+
Accept: 'application/json',
|
|
19
|
+
},
|
|
20
|
+
body: new URLSearchParams({
|
|
21
|
+
client_id: input.clientId,
|
|
22
|
+
client_secret: input.clientSecret,
|
|
23
|
+
code: input.code,
|
|
24
|
+
redirect_uri: input.redirectUri,
|
|
25
|
+
code_verifier: input.codeVerifier,
|
|
26
|
+
}).toString(),
|
|
27
|
+
});
|
|
28
|
+
if (!tokenRes.ok) {
|
|
29
|
+
const body = await tokenRes.text().catch(() => '');
|
|
30
|
+
throw new Error(`GitHub token exchange failed (${tokenRes.status}): ${body}`);
|
|
31
|
+
}
|
|
32
|
+
const tokens = await tokenRes.json();
|
|
33
|
+
if (tokens.error) {
|
|
34
|
+
throw new Error(`GitHub token exchange error: ${tokens.error}${tokens.error_description ? ` — ${tokens.error_description}` : ''}`);
|
|
35
|
+
}
|
|
36
|
+
const accessToken = String(tokens.access_token || '').trim();
|
|
37
|
+
if (!accessToken)
|
|
38
|
+
throw new Error('GitHub token exchange: missing access_token');
|
|
39
|
+
// Step 2: fetch user + emails in parallel
|
|
40
|
+
const ghHeaders = {
|
|
41
|
+
Authorization: `Bearer ${accessToken}`,
|
|
42
|
+
'User-Agent': 'Taskforce/1.0',
|
|
43
|
+
Accept: 'application/vnd.github+json',
|
|
44
|
+
};
|
|
45
|
+
const [userRes, emailsRes] = await Promise.all([
|
|
46
|
+
fetch('https://api.github.com/user', { headers: ghHeaders }),
|
|
47
|
+
fetch('https://api.github.com/user/emails', { headers: ghHeaders }),
|
|
48
|
+
]);
|
|
49
|
+
if (!userRes.ok) {
|
|
50
|
+
throw new Error(`GitHub /user API failed (${userRes.status})`);
|
|
51
|
+
}
|
|
52
|
+
const user = await userRes.json();
|
|
53
|
+
const numericId = String(user.id || '').trim();
|
|
54
|
+
if (!numericId)
|
|
55
|
+
throw new Error('GitHub /user API: missing id');
|
|
56
|
+
// Step 3: resolve best email
|
|
57
|
+
let email = null;
|
|
58
|
+
let emailVerified = false;
|
|
59
|
+
if (emailsRes.ok) {
|
|
60
|
+
const emails = await emailsRes.json().catch(() => []);
|
|
61
|
+
const primaryVerified = emails.find(e => e.primary === true && e.verified === true);
|
|
62
|
+
if (primaryVerified) {
|
|
63
|
+
email = String(primaryVerified.email || '').trim().toLowerCase() || null;
|
|
64
|
+
emailVerified = true;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const anyVerified = emails.find(e => e.verified === true);
|
|
68
|
+
if (anyVerified) {
|
|
69
|
+
email = String(anyVerified.email || '').trim().toLowerCase() || null;
|
|
70
|
+
emailVerified = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!email) {
|
|
75
|
+
// Last resort: public profile email (may be null / not set)
|
|
76
|
+
email = String(user.email || '').trim().toLowerCase() || null;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
provider: 'github',
|
|
80
|
+
providerSubject: numericId,
|
|
81
|
+
email,
|
|
82
|
+
emailVerified,
|
|
83
|
+
displayName: String(user.name || user.login || '').trim() || null,
|
|
84
|
+
avatarUrl: String(user.avatar_url || '').trim() || null,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google OAuth 2.0 / OpenID Connect adapter.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Redirect user to buildGoogleAuthUrl() (PKCE + S256).
|
|
6
|
+
* 2. Receive code on callback; call exchangeGoogleCode() to get profile.
|
|
7
|
+
*/
|
|
8
|
+
import type { NormalizedOAuthProfile } from './types.js';
|
|
9
|
+
export declare function buildGoogleAuthUrl(input: {
|
|
10
|
+
clientId: string;
|
|
11
|
+
redirectUri: string;
|
|
12
|
+
state: string;
|
|
13
|
+
codeChallenge: string;
|
|
14
|
+
}): string;
|
|
15
|
+
export declare function exchangeGoogleCode(input: {
|
|
16
|
+
code: string;
|
|
17
|
+
codeVerifier: string;
|
|
18
|
+
redirectUri: string;
|
|
19
|
+
clientId: string;
|
|
20
|
+
clientSecret: string;
|
|
21
|
+
}): Promise<NormalizedOAuthProfile>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function buildGoogleAuthUrl(input) {
|
|
2
|
+
const params = new URLSearchParams({
|
|
3
|
+
client_id: input.clientId,
|
|
4
|
+
redirect_uri: input.redirectUri,
|
|
5
|
+
response_type: 'code',
|
|
6
|
+
scope: 'openid email profile',
|
|
7
|
+
state: input.state,
|
|
8
|
+
code_challenge: input.codeChallenge,
|
|
9
|
+
code_challenge_method: 'S256',
|
|
10
|
+
access_type: 'offline',
|
|
11
|
+
prompt: 'select_account',
|
|
12
|
+
});
|
|
13
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
14
|
+
}
|
|
15
|
+
export async function exchangeGoogleCode(input) {
|
|
16
|
+
// Step 1: exchange authorisation code for tokens
|
|
17
|
+
const tokenRes = await fetch('https://oauth2.googleapis.com/token', {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
20
|
+
body: new URLSearchParams({
|
|
21
|
+
grant_type: 'authorization_code',
|
|
22
|
+
code: input.code,
|
|
23
|
+
redirect_uri: input.redirectUri,
|
|
24
|
+
client_id: input.clientId,
|
|
25
|
+
client_secret: input.clientSecret,
|
|
26
|
+
code_verifier: input.codeVerifier,
|
|
27
|
+
}).toString(),
|
|
28
|
+
});
|
|
29
|
+
if (!tokenRes.ok) {
|
|
30
|
+
const body = await tokenRes.text().catch(() => '');
|
|
31
|
+
throw new Error(`Google token exchange failed (${tokenRes.status}): ${body}`);
|
|
32
|
+
}
|
|
33
|
+
const tokens = await tokenRes.json();
|
|
34
|
+
const accessToken = String(tokens.access_token || '').trim();
|
|
35
|
+
if (!accessToken)
|
|
36
|
+
throw new Error('Google token exchange: missing access_token');
|
|
37
|
+
// Step 2: fetch userinfo
|
|
38
|
+
const userinfoRes = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
|
|
39
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
40
|
+
});
|
|
41
|
+
if (!userinfoRes.ok) {
|
|
42
|
+
throw new Error(`Google userinfo failed (${userinfoRes.status})`);
|
|
43
|
+
}
|
|
44
|
+
const info = await userinfoRes.json();
|
|
45
|
+
const sub = String(info.sub || '').trim();
|
|
46
|
+
if (!sub)
|
|
47
|
+
throw new Error('Google userinfo: missing sub');
|
|
48
|
+
return {
|
|
49
|
+
provider: 'google',
|
|
50
|
+
providerSubject: sub,
|
|
51
|
+
email: String(info.email || '').trim().toLowerCase() || null,
|
|
52
|
+
emailVerified: info.email_verified === true,
|
|
53
|
+
displayName: String(info.name || '').trim() || null,
|
|
54
|
+
avatarUrl: String(info.picture || '').trim() || null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalised OAuth profile returned by every provider adapter.
|
|
3
|
+
* The callback route uses this to resolve/create the Taskforce identity.
|
|
4
|
+
*/
|
|
5
|
+
export type OAuthProviderName = 'google' | 'github' | 'apple';
|
|
6
|
+
export interface NormalizedOAuthProfile {
|
|
7
|
+
/** The provider that authenticated the user. */
|
|
8
|
+
provider: OAuthProviderName;
|
|
9
|
+
/** Stable, provider-assigned user ID (Google `sub`, GitHub numeric `id`, Apple `sub`). */
|
|
10
|
+
providerSubject: string;
|
|
11
|
+
/** Primary email address, or null if the provider withheld it (e.g. Apple private relay). */
|
|
12
|
+
email: string | null;
|
|
13
|
+
/** Whether the provider has verified ownership of the email address. */
|
|
14
|
+
emailVerified: boolean;
|
|
15
|
+
/** Human-readable display name, or null if not provided. */
|
|
16
|
+
displayName: string | null;
|
|
17
|
+
/** Avatar / profile picture URL, or null if not provided. */
|
|
18
|
+
avatarUrl: string | null;
|
|
19
|
+
/** Apple-specific: true when `email` is a private relay address. */
|
|
20
|
+
isPrivateEmail?: boolean;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export declare function createSessionTokenCookie(sessionId: string, config: Task
|
|
|
30
30
|
role?: TaskforceRole;
|
|
31
31
|
workspaceId?: string;
|
|
32
32
|
userId?: string;
|
|
33
|
+
identityId?: string;
|
|
33
34
|
secure?: boolean;
|
|
34
35
|
sameSite?: 'Lax' | 'Strict' | 'None';
|
|
35
36
|
}): string;
|
|
@@ -42,6 +43,7 @@ export interface RequestAccessContext {
|
|
|
42
43
|
systemRole: TaskforceSystemRole;
|
|
43
44
|
workspaceId: string;
|
|
44
45
|
userId: string;
|
|
46
|
+
identityId: string | null;
|
|
45
47
|
}
|
|
46
48
|
export declare function resolveRequestAccess(req: IncomingMessage, config: TaskforceAuthConfig): RequestAccessContext;
|
|
47
49
|
export declare function isRequestAuthorized(req: IncomingMessage, config: TaskforceAuthConfig): {
|
package/dist/server/auth.js
CHANGED
|
@@ -176,7 +176,8 @@ export function createSessionTokenCookie(sessionId, config, options) {
|
|
|
176
176
|
exp,
|
|
177
177
|
role: options?.role,
|
|
178
178
|
workspaceId: options?.workspaceId,
|
|
179
|
-
userId: options?.userId
|
|
179
|
+
userId: options?.userId,
|
|
180
|
+
identityId: options?.identityId
|
|
180
181
|
}));
|
|
181
182
|
const sig = signPayload(payload, config.sessionSecret);
|
|
182
183
|
const value = `${payload}.${sig}`;
|
|
@@ -240,7 +241,8 @@ export function resolveRequestAccess(req, config) {
|
|
|
240
241
|
role: config.defaultRole,
|
|
241
242
|
systemRole: 'user',
|
|
242
243
|
workspaceId: fallbackWorkspaceId,
|
|
243
|
-
userId: fallbackUserId
|
|
244
|
+
userId: fallbackUserId,
|
|
245
|
+
identityId: null
|
|
244
246
|
};
|
|
245
247
|
const sessionClaims = readSessionClaimsFromRequest(req, config);
|
|
246
248
|
if (sessionClaims?.sessionId) {
|
|
@@ -251,7 +253,8 @@ export function resolveRequestAccess(req, config) {
|
|
|
251
253
|
role,
|
|
252
254
|
systemRole: 'user',
|
|
253
255
|
workspaceId: String(sessionClaims.workspaceId || config.defaultWorkspaceId).trim() || config.defaultWorkspaceId,
|
|
254
|
-
userId: String(sessionClaims.userId || fallbackUserId).trim() || fallbackUserId
|
|
256
|
+
userId: String(sessionClaims.userId || fallbackUserId).trim() || fallbackUserId,
|
|
257
|
+
identityId: String(sessionClaims.identityId || '').trim() || null
|
|
255
258
|
};
|
|
256
259
|
}
|
|
257
260
|
return base;
|
|
@@ -70,9 +70,9 @@ describe('document review routes', () => {
|
|
|
70
70
|
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
71
71
|
const now = new Date().toISOString();
|
|
72
72
|
core.db.prepare(`
|
|
73
|
-
INSERT INTO users (id, tenant_id, email, display_name,
|
|
74
|
-
VALUES (?, ?, ?, ?, ?, ?, ?,
|
|
75
|
-
`).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User',
|
|
73
|
+
INSERT INTO users (id, tenant_id, email, display_name, beta_access, is_disabled, created_at, updated_at)
|
|
74
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
75
|
+
`).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User', 1, 0, now, now);
|
|
76
76
|
core.db.prepare(`
|
|
77
77
|
INSERT INTO workspace_memberships (id, tenant_id, workspace_id, user_id, role, status, created_at, updated_at)
|
|
78
78
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -154,4 +154,37 @@ describe('document review routes', () => {
|
|
|
154
154
|
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
155
155
|
}
|
|
156
156
|
});
|
|
157
|
+
it('blocks anonymous cloud access before reading document review sessions', async () => {
|
|
158
|
+
const projectRoot = createProjectRoot();
|
|
159
|
+
const core = new TaskforceCore({
|
|
160
|
+
projectRoot,
|
|
161
|
+
storagePath: path.join(projectRoot, '.taskforce'),
|
|
162
|
+
});
|
|
163
|
+
try {
|
|
164
|
+
const routes = createRoutes(core, {
|
|
165
|
+
authConfig: {
|
|
166
|
+
runtimeMode: 'cloud',
|
|
167
|
+
enabled: true,
|
|
168
|
+
requiredForApi: true,
|
|
169
|
+
excludedPaths: ['/api/taskforce/health', '/api/taskforce/auth'],
|
|
170
|
+
defaultRole: 'member',
|
|
171
|
+
defaultWorkspaceId: '',
|
|
172
|
+
sessionCookieName: 'taskforce_session',
|
|
173
|
+
sessionSecret: 'test-secret',
|
|
174
|
+
sessionTtlSeconds: 3600,
|
|
175
|
+
sessionCookieSameSite: 'None',
|
|
176
|
+
sessionCookieSecure: true,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
const req = createMockReq('GET', '/api/taskforce/document-reviews/sessions?assetId=asset-doc-1', undefined, { 'x-taskforce-runtime-mode': 'cloud' });
|
|
180
|
+
const res = createMockRes();
|
|
181
|
+
await matchRoute('GET', '/api/taskforce/document-reviews/sessions', routes)?.handler(req, res, {});
|
|
182
|
+
expect(res.statusCode).toBe(401);
|
|
183
|
+
expect(res.__getBody()).toContain('Authentication required.');
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
core.close();
|
|
187
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
188
|
+
}
|
|
189
|
+
});
|
|
157
190
|
});
|
|
@@ -29,6 +29,7 @@ describe('proxy cookie rewrite helpers', () => {
|
|
|
29
29
|
expect(shouldProxyCloudPath('/api/taskforce/billing/checkout-session')).toBe(true);
|
|
30
30
|
expect(shouldProxyCloudPath('/api/taskforce/auth/session')).toBe(true);
|
|
31
31
|
expect(shouldProxyCloudPath('/api/taskforce/workspace/assignee-options')).toBe(false);
|
|
32
|
+
expect(shouldProxyCloudPath('/api/taskforce/workspace/ai-profiles/avatar')).toBe(false);
|
|
32
33
|
expect(shouldProxyCloudPath('/api/taskforce/settings/mcp/tokens')).toBe(true);
|
|
33
34
|
expect(shouldProxyCloudPath('/api/taskforce/settings/mcp/test-connection')).toBe(true);
|
|
34
35
|
expect(shouldProxyCloudPath('/api/platform/plans')).toBe(true);
|