@taskforcehq/taskforce 0.3.303 → 0.3.305

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +140 -247
  2. package/dist/Taskforce.module.css +102 -5
  3. package/dist/TaskforceCore.js +635 -238
  4. package/dist/TaskforceCore.test.js +2243 -495
  5. package/dist/annotatedAttachments/service.d.ts +1 -1
  6. package/dist/annotatedAttachments/types.d.ts +1 -1
  7. package/dist/compat/workspaceSyncCompat.js +6 -0
  8. package/dist/components/features/AgentsModule.js +1 -1
  9. package/dist/components/features/AgentsModule.test.js +1 -1
  10. package/dist/components/features/DocumentIndex.d.ts +1 -1
  11. package/dist/components/features/DocumentIndex.js +1 -1
  12. package/dist/components/features/DocumentViewer.d.ts +1 -1
  13. package/dist/components/features/DocumentViewer.js +32 -9
  14. package/dist/components/features/DocumentWorkspace.d.ts +5 -22
  15. package/dist/components/features/DocumentWorkspace.js +12 -155
  16. package/dist/components/features/DocumentWorkspace.test.js +193 -6
  17. package/dist/components/features/TaskSettings.js +87 -41
  18. package/dist/components/features/TaskSettings.test.js +180 -40
  19. package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
  20. package/dist/components/features/documentWorkspaceModel.js +57 -0
  21. package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
  22. package/dist/components/features/useDocumentWorkspaceController.js +113 -0
  23. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  24. package/dist/components/task/TaskActionHeader.js +3 -3
  25. package/dist/components/task/TaskActionHeader.test.js +8 -0
  26. package/dist/components/task/TaskCard.js +16 -1
  27. package/dist/components/task/TaskCard.test.js +31 -0
  28. package/dist/components/task/TaskForm.d.ts +9 -8
  29. package/dist/components/task/TaskForm.js +374 -219
  30. package/dist/components/task/TaskForm.test.js +482 -361
  31. package/dist/components/task/TaskKanban.d.ts +1 -1
  32. package/dist/components/task/TaskKanban.js +33 -161
  33. package/dist/components/task/TaskStatusActions.d.ts +6 -1
  34. package/dist/components/task/TaskStatusActions.js +8 -3
  35. package/dist/components/task/TaskStatusActions.test.js +9 -0
  36. package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
  37. package/dist/components/task/taskKanbanDropRules.js +187 -0
  38. package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
  39. package/dist/components/task/taskKanbanDropRules.test.js +114 -0
  40. package/dist/components/ui/Modal.d.ts +1 -1
  41. package/dist/components/ui/Modal.js +1 -0
  42. package/dist/components/views/AppShellHeader.js +5 -1
  43. package/dist/components/views/AppShellHeader.test.js +4 -0
  44. package/dist/components/views/PlanComparisonPage.d.ts +3 -2
  45. package/dist/components/views/PlanComparisonPage.js +9 -10
  46. package/dist/components/views/PlanComparisonPage.test.js +55 -41
  47. package/dist/components/views/PlansPage.d.ts +5 -1
  48. package/dist/components/views/PlansPage.js +150 -81
  49. package/dist/components/views/PlansPage.test.js +367 -41
  50. package/dist/components/views/StandaloneLayout.js +135 -54
  51. package/dist/components/views/WidgetView.js +11 -2
  52. package/dist/components/views/panels/FilterToolbar.test.js +6 -4
  53. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
  54. package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
  55. package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
  56. package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
  57. package/dist/config/envSchema.js +1 -1
  58. package/dist/core/AiProfileService.d.ts +6 -1
  59. package/dist/core/AiProfileService.js +161 -16
  60. package/dist/core/AiProfileService.test.js +3 -1
  61. package/dist/core/AiProfiles.test.js +200 -0
  62. package/dist/core/AuthTokenService.test.d.ts +1 -0
  63. package/dist/core/AuthTokenService.test.js +78 -0
  64. package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
  65. package/dist/core/EntitlementsPolicy.test.js +75 -13
  66. package/dist/core/PlanEntitlementService.d.ts +9 -1
  67. package/dist/core/PlanEntitlementService.js +144 -19
  68. package/dist/core/PlanVersionPolicy.test.js +19 -26
  69. package/dist/core/SignupMonetizationSettings.test.js +75 -15
  70. package/dist/core/SystemAdmin.test.js +212 -56
  71. package/dist/core/TaskActivityService.d.ts +4 -1
  72. package/dist/core/TaskActivityService.js +45 -0
  73. package/dist/core/TaskAuditTimeline.test.js +52 -1
  74. package/dist/core/TaskTaxonomyValidation.test.js +53 -0
  75. package/dist/core/Taskforce.d.ts +18 -11
  76. package/dist/core/Taskforce.js +93 -12
  77. package/dist/core/WorkspacePlanMode.test.js +73 -8
  78. package/dist/core/shared.d.ts +2 -0
  79. package/dist/core/shared.js +11 -0
  80. package/dist/documentReviews/service.d.ts +1 -1
  81. package/dist/documentReviews/types.d.ts +1 -1
  82. package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
  83. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
  84. package/dist/hooks/sync/auth.d.ts +37 -0
  85. package/dist/hooks/sync/auth.js +63 -0
  86. package/dist/hooks/sync/bootstrap.d.ts +34 -0
  87. package/dist/hooks/sync/bootstrap.js +52 -0
  88. package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
  89. package/dist/hooks/sync/orchestratorShared.js +209 -0
  90. package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
  91. package/dist/hooks/sync/orchestratorShared.test.js +49 -0
  92. package/dist/hooks/sync/realtime.d.ts +25 -0
  93. package/dist/hooks/sync/realtime.js +60 -0
  94. package/dist/hooks/sync/recovery.d.ts +69 -0
  95. package/dist/hooks/sync/recovery.js +118 -0
  96. package/dist/hooks/sync/transfers.d.ts +123 -0
  97. package/dist/hooks/sync/transfers.js +435 -0
  98. package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
  99. package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
  100. package/dist/hooks/ui/useResourceExport.d.ts +3 -1
  101. package/dist/hooks/ui/useResourceExport.js +5 -2
  102. package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
  103. package/dist/hooks/useRealtimeSync.js +2 -1
  104. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
  105. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
  106. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
  107. package/dist/hooks/useSyncOrchestrator.d.ts +12 -8
  108. package/dist/hooks/useSyncOrchestrator.js +388 -879
  109. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +641 -6
  110. package/dist/hooks/useTaskMutations.d.ts +2 -1
  111. package/dist/hooks/useTaskMutations.js +57 -7
  112. package/dist/hooks/useTaskMutations.test.js +176 -0
  113. package/dist/hooks/useTaskforce.d.ts +13 -4
  114. package/dist/hooks/useTaskforce.js +302 -817
  115. package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
  116. package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
  117. package/dist/hooks/useWorkspaceSyncController.js +33 -11
  118. package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
  119. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
  120. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
  121. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
  122. package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
  123. package/dist/hooks/workspace/workspaceLocalState.js +100 -0
  124. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
  125. package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
  126. package/dist/localization/locales/en-US.js +2 -2
  127. package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
  128. package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
  129. package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
  130. package/dist/mcp/canonicalAssetHelpers.js +259 -0
  131. package/dist/mcp/clientIntegrations.d.ts +12 -1
  132. package/dist/mcp/clientIntegrations.js +161 -6
  133. package/dist/mcp/clientIntegrations.test.js +76 -4
  134. package/dist/mcp/collaborationRegistrar.d.ts +2 -0
  135. package/dist/mcp/collaborationRegistrar.js +71 -0
  136. package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
  137. package/dist/mcp/documentAssetRegistrar.js +346 -0
  138. package/dist/mcp/documentHelpers.d.ts +100 -0
  139. package/dist/mcp/documentHelpers.js +161 -0
  140. package/dist/mcp/runtime.js +2455 -3925
  141. package/dist/mcp/runtime.test.js +240 -8
  142. package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
  143. package/dist/mcp/taskAttachmentHelpers.js +60 -0
  144. package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
  145. package/dist/mcp/taskAttachmentTypes.js +1 -0
  146. package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
  147. package/dist/mcp/taskPlanningRegistrar.js +418 -0
  148. package/dist/mcp/toolCatalog.d.ts +35 -0
  149. package/dist/mcp/toolCatalog.js +3 -0
  150. package/dist/plugins/vite.js +1 -1
  151. package/dist/runtime/appGateDefinitions.d.ts +1 -1
  152. package/dist/runtime/appGateDefinitions.js +1 -1
  153. package/dist/server/index.d.ts +1 -0
  154. package/dist/server/index.js +46 -3
  155. package/dist/server/index.test.js +12 -1
  156. package/dist/server/routes/admin.d.ts +1 -5
  157. package/dist/server/routes/admin.js +151 -80
  158. package/dist/server/routes/annotatedAttachments.d.ts +1 -1
  159. package/dist/server/routes/annotatedAttachments.js +1 -1
  160. package/dist/server/routes/auth.d.ts +1 -4
  161. package/dist/server/routes/auth.js +59 -64
  162. package/dist/server/routes/authSupport.d.ts +30 -0
  163. package/dist/server/routes/authSupport.js +81 -0
  164. package/dist/server/routes/billing.d.ts +1 -1
  165. package/dist/server/routes/billing.js +122 -255
  166. package/dist/server/routes/billing.test.js +257 -86
  167. package/dist/server/routes/documentReviews.d.ts +1 -1
  168. package/dist/server/routes/documentReviews.js +1 -1
  169. package/dist/server/routes/documents.d.ts +4 -2
  170. package/dist/server/routes/documents.js +73 -7
  171. package/dist/server/routes/primitives.d.ts +11 -0
  172. package/dist/server/routes/primitives.js +73 -0
  173. package/dist/server/routes/resources.d.ts +1 -1
  174. package/dist/server/routes/resources.js +1 -1
  175. package/dist/server/routes/shared.d.ts +31 -1
  176. package/dist/server/routes/shared.js +26 -0
  177. package/dist/server/routes/sync.d.ts +1 -1
  178. package/dist/server/routes/sync.integration.test.js +20 -0
  179. package/dist/server/routes/sync.js +111 -238
  180. package/dist/server/routes/tasks.d.ts +1 -1
  181. package/dist/server/routes/tasks.js +50 -8
  182. package/dist/server/routes/workspaces.d.ts +1 -1
  183. package/dist/server/routes/workspaces.js +1 -1
  184. package/dist/server/routes.d.ts +2 -9
  185. package/dist/server/routes.js +84 -181
  186. package/dist/server/routes.test.js +912 -32
  187. package/dist/shared/taskActor.d.ts +10 -0
  188. package/dist/shared/taskActor.js +1 -0
  189. package/dist/sync/collaborationSyncPayload.js +4 -0
  190. package/dist/sync/collaborationSyncPayload.test.js +8 -0
  191. package/dist/sync/collaborationSyncState.d.ts +3 -1
  192. package/dist/sync/collaborationSyncState.js +19 -0
  193. package/dist/sync/contentSyncState.js +28 -17
  194. package/dist/sync/syncApplyHandlers.d.ts +87 -1
  195. package/dist/sync/syncApplyHandlers.js +370 -8
  196. package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
  197. package/dist/sync/syncApplyHandlers.test.js +379 -0
  198. package/dist/sync/syncService.d.ts +9 -1
  199. package/dist/sync/syncService.js +44 -2
  200. package/dist/sync/syncService.test.js +62 -0
  201. package/dist/sync/workspacePullFeed.d.ts +1 -0
  202. package/dist/sync/workspacePullFeed.js +66 -59
  203. package/dist/sync/workspacePullFeed.test.js +72 -4
  204. package/dist/sync/workspaceSyncModel.d.ts +25 -1
  205. package/dist/sync/workspaceSyncModel.js +77 -3
  206. package/dist/sync/workspaceSyncModel.test.js +153 -0
  207. package/dist/sync/workspaceSyncState.d.ts +2 -0
  208. package/dist/sync/workspaceSyncState.js +1 -0
  209. package/dist/types.d.ts +2 -10
  210. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +1 -0
  211. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
  212. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
  213. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
  214. package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-Dhm7M8e7.js} +1 -1
  215. package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
  216. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
  217. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
  218. package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-BcS4afRn.js} +1 -1
  219. package/dist/ui/assets/index-C2-OXZV7.css +1 -0
  220. package/dist/ui/assets/index-DcSI5F86.js +6 -0
  221. package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-BSUaRwF8.js} +1 -1
  222. package/dist/ui/index.html +4 -4
  223. package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
  224. package/dist/utils/commercialLifecyclePresentation.js +196 -0
  225. package/dist/utils/taskActivity.js +4 -0
  226. package/dist/utils/taskActivity.test.js +25 -1
  227. package/package.json +10 -1
  228. package/scripts/check-cloud-mcp.mjs +83 -0
  229. package/scripts/playwright-auth.sh +9 -1
  230. package/scripts/run-billing-performance-smoke.sh +24 -0
  231. package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
  232. package/dist/ui/assets/DocumentWorkspace-C3GyzrWm.js +0 -250
  233. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
  234. package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
  235. package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
  236. package/dist/ui/assets/index-CLIMgR6g.js +0 -6
  237. package/dist/ui/assets/index-DneETxcu.css +0 -1
@@ -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 { resolveEffectiveEmailConfig, sendEmail } from './email.js';
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,14 @@ 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 { requestHeader, requestRole, requestRuntimeMode, requestUserId, makeAuditAdminAction, makeAuditAuthAction, makeEnsureAdminRole, makeEnsureAppAccess, makeEnsureAuthenticatedUser, makeEnsureCloudPlanData, makeRequestWorkspaceId, makeRequireCloudRuntime, makeResolveAdminRequestActor, makeResolveAuthenticatedUserId, makeResolveAuthenticatedMutationUserId, ensureWorkspaceAdminRole, } from './routes/shared.js';
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, ensureWorkspaceAdminRole, } 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
- const DEFAULT_MAX_JSON_BODY_BYTES = 1024 * 1024; // 1MB
44
+ export { parseJsonBody } from './routes/primitives.js';
43
45
  const DEFAULT_DOCUMENT_RETENTION_DAYS = 7;
44
46
  const STORAGE_INTEGRITY_HISTORY_LIMIT = 50;
45
47
  const MCP_TOKEN_SCOPE_VALUES = new Set(['tasks:read', 'tasks:write', 'workspace:read']);
@@ -70,20 +72,6 @@ function readPositiveInteger(value, fallback) {
70
72
  return fallback;
71
73
  return parsed;
72
74
  }
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
75
  // Convert route pattern like '/api/taskforce/tasks/:id' to regex
88
76
  function createRoutePattern(path) {
89
77
  const keys = [];
@@ -93,74 +81,6 @@ function createRoutePattern(path) {
93
81
  });
94
82
  return { pattern: new RegExp(`^${pattern}$`), keys };
95
83
  }
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, '&lt;')
160
- .replace(/>/g, '&gt;')
161
- .replace(/"/g, '&quot;')
162
- .replace(/'/g, '&#39;');
163
- }
164
84
  function resolveErrorStatusCode(error, fallback = 400) {
165
85
  if (error instanceof TaskforceRuleError)
166
86
  return error.statusCode;
@@ -206,6 +126,51 @@ function inferMcpOauthProviderFromRedirectUris(redirectUris) {
206
126
  }
207
127
  return MCP_OAUTH_DEFAULT_PROVIDER;
208
128
  }
129
+ /**
130
+ * Create API routes for Taskforce
131
+ *
132
+ * Core routes (29 total):
133
+ *
134
+ * **Task Management (12 routes)**
135
+ * - GET /api/taskforce/config
136
+ * - GET /api/taskforce/tasks
137
+ * - POST /api/taskforce/task
138
+ * - PATCH /api/taskforce/task/:id
139
+ * - DELETE /api/taskforce/task/:id
140
+ * - GET /api/taskforce/archive
141
+ * - POST /api/taskforce/task/:id/complete
142
+ * - POST /api/taskforce/task/:id/cancel
143
+ * - POST /api/taskforce/task/:id/comment
144
+ * - POST /api/taskforce/task/:id/unarchive
145
+ * - POST /api/taskforce/bulk-create
146
+ * - POST /api/taskforce/bulk-archive
147
+ * - POST /api/taskforce/bulk-update-fields
148
+ * - POST /api/taskforce/bulk-update-fields/preview
149
+ *
150
+ * **Configuration Management**
151
+ * - GET /api/taskforce/categories
152
+ * - POST /api/taskforce/categories
153
+ * - GET /api/taskforce/types
154
+ * - POST /api/taskforce/types
155
+ * - GET /api/taskforce/priorities
156
+ * - POST /api/taskforce/priorities
157
+ * - GET /api/taskforce/taxonomies
158
+ * - POST /api/taskforce/taxonomies
159
+ *
160
+ * **Workflow Settings**
161
+ * - GET /api/taskforce/approaches
162
+ * - POST /api/taskforce/approaches
163
+ *
164
+ * **Templates & Setup**
165
+ * - GET /api/taskforce/initiative-templates
166
+ * - POST /api/taskforce/initiative-templates/create
167
+ *
168
+ * **Advanced Features (3 routes)**
169
+ * - GET /api/taskforce/folders
170
+ * - POST /api/taskforce/validate-paths
171
+ * - POST /api/taskforce/export-resources
172
+ * - GET /api/taskforce/health
173
+ */
209
174
  export function createRoutes(core, context = {}) {
210
175
  const routes = [];
211
176
  const uiStateCache = new Map();
@@ -299,33 +264,6 @@ export function createRoutes(core, context = {}) {
299
264
  return null;
300
265
  return resolved;
301
266
  };
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
267
  const logAuthLink = (kind, email, token, req) => {
330
268
  const base = resolvePublicAuthBaseUrl(req);
331
269
  const path = kind === 'verify-email'
@@ -333,34 +271,6 @@ export function createRoutes(core, context = {}) {
333
271
  : `/login?mode=reset&token=${encodeURIComponent(token)}`;
334
272
  console.info(`[Taskforce Auth] ${kind} email=${email} link=${base}${path}`);
335
273
  };
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
274
  const deliverAuthEmail = async (kind, email, token, req) => {
365
275
  const base = resolvePublicAuthBaseUrl(req);
366
276
  const path = kind === 'verify-email'
@@ -372,13 +282,13 @@ export function createRoutes(core, context = {}) {
372
282
  ? 'Verify your Taskforce account'
373
283
  : 'Reset your Taskforce password';
374
284
  const text = kind === 'verify-email'
375
- ? `Welcome to Taskforce.\n\nVerify your email by opening this link:\n${link}\n\nIf you did not create an account, you can ignore this message.`
285
+ ? `Welcome to Taskforce.\n\nYou're almost in. Enter this verification token on the verification screen to activate your account:\n\n${token}\n\nPrefer the faster route? Open the link below to verify your email automatically:\n${link}\n\nIf you didn't create a Taskforce account, you can safely ignore this email.`
376
286
  : `Reset your Taskforce password using this link:\n${link}\n\nIf you did not request this reset, you can ignore this message.`;
377
287
  const result = await sendEmail({
378
288
  to: email,
379
289
  subject,
380
290
  text
381
- }, getEmailConfig());
291
+ }, getRouteEmailConfig(core, context));
382
292
  if (result.ok) {
383
293
  console.info(`[Taskforce Auth Email] kind=${kind} provider=${result.provider} to=${email} messageId=${result.messageId || '-'}`);
384
294
  }
@@ -396,7 +306,7 @@ export function createRoutes(core, context = {}) {
396
306
  to: email,
397
307
  subject,
398
308
  text
399
- }, getEmailConfig());
309
+ }, getRouteEmailConfig(core, context));
400
310
  if (result.ok) {
401
311
  console.info(`[Taskforce Auth Email] kind=invite provider=${result.provider} to=${email} messageId=${result.messageId || '-'}`);
402
312
  }
@@ -799,7 +709,10 @@ export function createRoutes(core, context = {}) {
799
709
  const contentDisposition = params.forceDownload
800
710
  ? `attachment; filename="${resolvedDownloadName.replace(/"/g, '')}"`
801
711
  : 'inline';
802
- if (params.asset.storageProvider === 'r2' && objectStorageClient && resolvedObjectStorage.provider === 'r2' && resolvedObjectStorage.r2) {
712
+ const readFromObjectStorage = async () => {
713
+ if (!objectStorageClient || resolvedObjectStorage.provider !== 'r2' || !resolvedObjectStorage.r2) {
714
+ return { statusCode: 404 };
715
+ }
803
716
  if (!params.forceDownload) {
804
717
  const object = await objectStorageClient.getObject(params.asset.storageKey);
805
718
  if (!object) {
@@ -820,19 +733,22 @@ export function createRoutes(core, context = {}) {
820
733
  Location: objectStorageClient.createSignedGetUrl(params.asset.storageKey, resolvedObjectStorage.r2.signedDownloadTtlSeconds, { responseContentDisposition: contentDisposition })
821
734
  }
822
735
  };
736
+ };
737
+ if (params.asset.storageProvider === 'r2' && objectStorageClient && resolvedObjectStorage.provider === 'r2' && resolvedObjectStorage.r2) {
738
+ return readFromObjectStorage();
823
739
  }
824
740
  const filePath = ensureWithinDir(path.join(basePath, params.asset.storageKey), basePath);
825
- if (!filePath || !fs.existsSync(filePath)) {
826
- return { statusCode: 404 };
741
+ if (filePath && fs.existsSync(filePath)) {
742
+ return {
743
+ statusCode: 200,
744
+ headers: {
745
+ 'Content-Type': params.asset.mimeType || contentTypeForExt(ext),
746
+ 'Content-Disposition': contentDisposition
747
+ },
748
+ body: fs.readFileSync(filePath)
749
+ };
827
750
  }
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
- };
751
+ return readFromObjectStorage();
836
752
  };
837
753
  const getAssetMetadata = (pathValue) => {
838
754
  const canonical = getCanonicalAssetMetadata(pathValue);
@@ -1109,7 +1025,7 @@ export function createRoutes(core, context = {}) {
1109
1025
  return null;
1110
1026
  return workspaceId;
1111
1027
  };
1112
- const ensureContextUploadAllowed = (req, res, workspaceId) => {
1028
+ const ensureContextUploadAllowed = (req, res, workspaceId, options) => {
1113
1029
  if (requestRuntimeMode(req) !== 'cloud')
1114
1030
  return true;
1115
1031
  if (!ensureAuthenticatedUser(req)) {
@@ -1120,12 +1036,17 @@ export function createRoutes(core, context = {}) {
1120
1036
  const gate = core.evaluateFeatureAccess({
1121
1037
  userId: requestUserId(req),
1122
1038
  workspaceId,
1123
- featureKey: 'context.uploads'
1039
+ featureKey: 'context.uploads',
1040
+ context: {
1041
+ requestedBytes: options?.requestedBytes
1042
+ }
1124
1043
  });
1125
1044
  if (!gate.allowed) {
1126
1045
  res.writeHead(403, { 'Content-Type': 'application/json' });
1127
1046
  res.end(JSON.stringify({
1128
- error: 'Document cloud storage is unavailable for this account entitlement.',
1047
+ error: gate.reasonCode === 'UPLOAD_STORAGE_LIMIT_REACHED'
1048
+ ? 'This upload exceeds the document cloud storage limit for the current plan.'
1049
+ : 'Document cloud storage is unavailable for this account entitlement.',
1129
1050
  code: gate.reasonCode
1130
1051
  }));
1131
1052
  return false;
@@ -1458,24 +1379,13 @@ export function createRoutes(core, context = {}) {
1458
1379
  serverName: 'taskforce-cloud',
1459
1380
  logStartup: false,
1460
1381
  runtimeMode: 'cloud',
1461
- auth: {
1462
- scopes: authScopes,
1463
- userId: auth.authenticatedViaToken?.userId || auth.authenticatedViaOAuth?.userId || auth.userId || null,
1464
- tokenId: auth.authenticatedViaToken?.tokenId || auth.authenticatedViaOAuth?.grantId || null,
1465
- aiProfileToken: auth.aiProfileToken,
1466
- aiProfileName: auth.aiProfileName,
1467
- aiProfileIcon: auth.aiProfileIcon,
1468
- aiProfileColor: auth.aiProfileColor,
1469
- aiProfileDescription: auth.aiProfileDescription,
1470
- aiProfileRole: auth.aiProfileRole,
1471
- aiProfileProvider: auth.aiProfileProvider,
1472
- aiProfileModel: auth.aiProfileModel,
1473
- aiProfileSurfaceType: auth.aiProfileSurfaceType,
1474
- actorType: auth.authenticatedViaToken ? 'token' : auth.authenticatedViaOAuth ? 'oauth' : 'user',
1382
+ auth: buildCloudMcpRuntimeAuth({
1383
+ req,
1384
+ auth,
1385
+ requestHeader,
1475
1386
  requestId: String(req.headers['x-request-id'] || req.headers['x-railway-request-id'] || '').trim() || randomUUID(),
1476
- ip: req.socket?.remoteAddress || null,
1477
- userAgent: requestHeader(req, 'user-agent', '') || null,
1478
- },
1387
+ authScopes,
1388
+ }),
1479
1389
  });
1480
1390
  const transport = new StreamableHTTPServerTransport({
1481
1391
  sessionIdGenerator: undefined,
@@ -1562,9 +1472,6 @@ export function createRoutes(core, context = {}) {
1562
1472
  requireCloudRuntime,
1563
1473
  auditAuthAction,
1564
1474
  resolveAdminRequestActor,
1565
- shouldExposeDevAuthTokens,
1566
- resolveRuntimeConfigBaseUrl,
1567
- resolvePublicAuthBaseUrl,
1568
1475
  resolveEffectiveObjectStorageConfig,
1569
1476
  getObjectStorageClient,
1570
1477
  });
@@ -1615,10 +1522,6 @@ export function createRoutes(core, context = {}) {
1615
1522
  appendDocumentPurgeHistory,
1616
1523
  getCollaborationTelemetry,
1617
1524
  isCollaborationPolicyEnabled,
1618
- resolveRuntimeConfigBaseUrl,
1619
- resolvePublicAuthBaseUrl,
1620
- parseFormBody,
1621
- escapeHtml,
1622
1525
  normalizeMcpOauthScopeString,
1623
1526
  MCP_TOKEN_SCOPE_VALUES,
1624
1527
  MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS,