@taskforcehq/taskforce 0.3.168

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 (492) hide show
  1. package/README.md +241 -0
  2. package/dist/Taskforce.d.ts +3 -0
  3. package/dist/Taskforce.js +3 -0
  4. package/dist/Taskforce.module.css +7587 -0
  5. package/dist/TaskforceCore.CategoryFallback.test.d.ts +1 -0
  6. package/dist/TaskforceCore.CategoryFallback.test.js +98 -0
  7. package/dist/TaskforceCore.d.ts +12 -0
  8. package/dist/TaskforceCore.js +729 -0
  9. package/dist/TaskforceCore.test.d.ts +1 -0
  10. package/dist/TaskforceCore.test.js +2132 -0
  11. package/dist/TaskforceScreenshot.d.ts +11 -0
  12. package/dist/TaskforceScreenshot.js +113 -0
  13. package/dist/TaskforceWidget.d.ts +6 -0
  14. package/dist/TaskforceWidget.js +101 -0
  15. package/dist/TaskforceWidget.test.d.ts +1 -0
  16. package/dist/TaskforceWidget.test.js +92 -0
  17. package/dist/UnsavedChangesRelaxed.test.d.ts +1 -0
  18. package/dist/UnsavedChangesRelaxed.test.js +16 -0
  19. package/dist/__tests__/localizationCatalogs.test.d.ts +1 -0
  20. package/dist/__tests__/localizationCatalogs.test.js +37 -0
  21. package/dist/__tests__/planParser.test.d.ts +1 -0
  22. package/dist/__tests__/planParser.test.js +183 -0
  23. package/dist/__tests__/releaseVersion.test.d.ts +1 -0
  24. package/dist/__tests__/releaseVersion.test.js +32 -0
  25. package/dist/__tests__/structuredCommentValidation.test.d.ts +1 -0
  26. package/dist/__tests__/structuredCommentValidation.test.js +31 -0
  27. package/dist/__tests__/structuredDocValidation.test.d.ts +1 -0
  28. package/dist/__tests__/structuredDocValidation.test.js +30 -0
  29. package/dist/__tests__/templateEngine.test.d.ts +1 -0
  30. package/dist/__tests__/templateEngine.test.js +150 -0
  31. package/dist/__tests__/templates.test.d.ts +1 -0
  32. package/dist/__tests__/templates.test.js +45 -0
  33. package/dist/__tests__/workflowDocumentFixtures.test.d.ts +1 -0
  34. package/dist/__tests__/workflowDocumentFixtures.test.js +78 -0
  35. package/dist/__tests__/workflowExportMatrix.test.d.ts +1 -0
  36. package/dist/__tests__/workflowExportMatrix.test.js +113 -0
  37. package/dist/__tests__/workflowExportSnapshots.test.d.ts +1 -0
  38. package/dist/__tests__/workflowExportSnapshots.test.js +28 -0
  39. package/dist/__tests__/workflowGeneration.test.d.ts +1 -0
  40. package/dist/__tests__/workflowGeneration.test.js +12 -0
  41. package/dist/__tests__/workflowSimulation.test.d.ts +1 -0
  42. package/dist/__tests__/workflowSimulation.test.js +113 -0
  43. package/dist/__tests__/workflowV2Policy.test.d.ts +1 -0
  44. package/dist/__tests__/workflowV2Policy.test.js +80 -0
  45. package/dist/api.d.ts +30 -0
  46. package/dist/api.js +85 -0
  47. package/dist/cli.d.ts +2 -0
  48. package/dist/cli.js +217 -0
  49. package/dist/compat/priorityCompat.d.ts +2 -0
  50. package/dist/compat/priorityCompat.js +24 -0
  51. package/dist/components/features/DocumentGenerateModal.d.ts +9 -0
  52. package/dist/components/features/DocumentGenerateModal.js +114 -0
  53. package/dist/components/features/DocumentIndex.d.ts +19 -0
  54. package/dist/components/features/DocumentIndex.js +51 -0
  55. package/dist/components/features/DocumentViewer.d.ts +9 -0
  56. package/dist/components/features/DocumentViewer.js +253 -0
  57. package/dist/components/features/DocumentWorkspace.d.ts +21 -0
  58. package/dist/components/features/DocumentWorkspace.js +88 -0
  59. package/dist/components/features/TaskSettings.d.ts +209 -0
  60. package/dist/components/features/TaskSettings.js +500 -0
  61. package/dist/components/features/TaskSettings.test.d.ts +1 -0
  62. package/dist/components/features/TaskSettings.test.js +308 -0
  63. package/dist/components/features/settings/CategoryManager.d.ts +19 -0
  64. package/dist/components/features/settings/CategoryManager.js +113 -0
  65. package/dist/components/features/settings/TaxonomyEditor.d.ts +8 -0
  66. package/dist/components/features/settings/TaxonomyEditor.js +98 -0
  67. package/dist/components/features/settings/TaxonomyManager 2.d.ts +52 -0
  68. package/dist/components/features/settings/TaxonomyManager 2.js +75 -0
  69. package/dist/components/features/settings/TaxonomyManager.d.ts +54 -0
  70. package/dist/components/features/settings/TaxonomyManager.js +77 -0
  71. package/dist/components/features/settings/TypeManager.d.ts +10 -0
  72. package/dist/components/features/settings/TypeManager.js +50 -0
  73. package/dist/components/features/settings/WorkflowEditorPrototype.d.ts +15 -0
  74. package/dist/components/features/settings/WorkflowEditorPrototype.js +245 -0
  75. package/dist/components/task/TaskActionHeader.d.ts +20 -0
  76. package/dist/components/task/TaskActionHeader.js +13 -0
  77. package/dist/components/task/TaskCard.d.ts +47 -0
  78. package/dist/components/task/TaskCard.js +326 -0
  79. package/dist/components/task/TaskCard.test.d.ts +1 -0
  80. package/dist/components/task/TaskCard.test.js +95 -0
  81. package/dist/components/task/TaskContextUpload.d.ts +12 -0
  82. package/dist/components/task/TaskContextUpload.js +393 -0
  83. package/dist/components/task/TaskContextUpload.test.d.ts +1 -0
  84. package/dist/components/task/TaskContextUpload.test.js +189 -0
  85. package/dist/components/task/TaskForm.d.ts +105 -0
  86. package/dist/components/task/TaskForm.js +241 -0
  87. package/dist/components/task/TaskForm.test.d.ts +1 -0
  88. package/dist/components/task/TaskForm.test.js +288 -0
  89. package/dist/components/task/TaskKanban 2.d.ts +76 -0
  90. package/dist/components/task/TaskKanban 2.js +1004 -0
  91. package/dist/components/task/TaskKanban.d.ts +76 -0
  92. package/dist/components/task/TaskKanban.empty-column-drop.test.d.ts +1 -0
  93. package/dist/components/task/TaskKanban.empty-column-drop.test.js +66 -0
  94. package/dist/components/task/TaskKanban.js +1026 -0
  95. package/dist/components/task/TaskKanban.test.d.ts +1 -0
  96. package/dist/components/task/TaskKanban.test.js +46 -0
  97. package/dist/components/task/TaskList.d.ts +74 -0
  98. package/dist/components/task/TaskList.js +38 -0
  99. package/dist/components/task/TaskList.test.d.ts +1 -0
  100. package/dist/components/task/TaskList.test.js +270 -0
  101. package/dist/components/task/TaskScreenshotSection.d.ts +15 -0
  102. package/dist/components/task/TaskScreenshotSection.js +22 -0
  103. package/dist/components/ui/LevelSelector.d.ts +13 -0
  104. package/dist/components/ui/LevelSelector.js +25 -0
  105. package/dist/components/ui/LevelSelector.test.d.ts +1 -0
  106. package/dist/components/ui/LevelSelector.test.js +55 -0
  107. package/dist/components/ui/Markdown.d.ts +6 -0
  108. package/dist/components/ui/Markdown.js +94 -0
  109. package/dist/components/ui/Markdown.test.d.ts +1 -0
  110. package/dist/components/ui/Markdown.test.js +38 -0
  111. package/dist/components/ui/Modal.d.ts +16 -0
  112. package/dist/components/ui/Modal.js +49 -0
  113. package/dist/components/ui/MultiSelectFilter.d.ts +10 -0
  114. package/dist/components/ui/MultiSelectFilter.js +71 -0
  115. package/dist/components/ui/MultiSelectFilter.test.d.ts +1 -0
  116. package/dist/components/ui/MultiSelectFilter.test.js +74 -0
  117. package/dist/components/ui/TaxonomyDropdown.d.ts +17 -0
  118. package/dist/components/ui/TaxonomyDropdown.js +145 -0
  119. package/dist/components/ui/TaxonomyDropdown.test.d.ts +1 -0
  120. package/dist/components/ui/TaxonomyDropdown.test.js +130 -0
  121. package/dist/components/views/StandaloneLayout 2.d.ts +8 -0
  122. package/dist/components/views/StandaloneLayout 2.js +1660 -0
  123. package/dist/components/views/StandaloneLayout.d.ts +8 -0
  124. package/dist/components/views/StandaloneLayout.js +2117 -0
  125. package/dist/components/views/WidgetView.d.ts +8 -0
  126. package/dist/components/views/WidgetView.js +221 -0
  127. package/dist/config/envSchema.d.ts +28 -0
  128. package/dist/config/envSchema.js +374 -0
  129. package/dist/config/envSchema.test.d.ts +1 -0
  130. package/dist/config/envSchema.test.js +97 -0
  131. package/dist/config/surfaceBase.d.ts +2 -0
  132. package/dist/config/surfaceBase.js +8 -0
  133. package/dist/core/BulkUpdateFieldsPreview.test.d.ts +1 -0
  134. package/dist/core/BulkUpdateFieldsPreview.test.js +62 -0
  135. package/dist/core/CompletedAt.test.d.ts +1 -0
  136. package/dist/core/CompletedAt.test.js +44 -0
  137. package/dist/core/CreatedBy.test.d.ts +1 -0
  138. package/dist/core/CreatedBy.test.js +60 -0
  139. package/dist/core/EntitlementsPolicy.test.d.ts +1 -0
  140. package/dist/core/EntitlementsPolicy.test.js +145 -0
  141. package/dist/core/InitiativeTemplates.test.d.ts +1 -0
  142. package/dist/core/InitiativeTemplates.test.js +50 -0
  143. package/dist/core/ParentLifecycle.test.d.ts +1 -0
  144. package/dist/core/ParentLifecycle.test.js +98 -0
  145. package/dist/core/ScheduleFields.test.d.ts +1 -0
  146. package/dist/core/ScheduleFields.test.js +197 -0
  147. package/dist/core/SetupState.test.d.ts +1 -0
  148. package/dist/core/SetupState.test.js +113 -0
  149. package/dist/core/SystemAdmin.test.d.ts +1 -0
  150. package/dist/core/SystemAdmin.test.js +295 -0
  151. package/dist/core/TaskTaxonomyValidation.test.d.ts +1 -0
  152. package/dist/core/TaskTaxonomyValidation.test.js +64 -0
  153. package/dist/core/Taskforce.d.ts +1063 -0
  154. package/dist/core/Taskforce.ids.test.d.ts +1 -0
  155. package/dist/core/Taskforce.ids.test.js +168 -0
  156. package/dist/core/Taskforce.js +5981 -0
  157. package/dist/core/Taskforce.realtimeMutation.test.d.ts +1 -0
  158. package/dist/core/Taskforce.realtimeMutation.test.js +76 -0
  159. package/dist/core/WorkspaceDelete.test.d.ts +1 -0
  160. package/dist/core/WorkspaceDelete.test.js +140 -0
  161. package/dist/core/WorkspacePermissions.test.d.ts +1 -0
  162. package/dist/core/WorkspacePermissions.test.js +151 -0
  163. package/dist/core/WorkspacePlanMode.test.d.ts +1 -0
  164. package/dist/core/WorkspacePlanMode.test.js +104 -0
  165. package/dist/hooks/authGuardPolicy.d.ts +26 -0
  166. package/dist/hooks/authGuardPolicy.js +32 -0
  167. package/dist/hooks/authGuardPolicy.test.d.ts +1 -0
  168. package/dist/hooks/authGuardPolicy.test.js +76 -0
  169. package/dist/hooks/useDraggable.d.ts +12 -0
  170. package/dist/hooks/useDraggable.js +65 -0
  171. package/dist/hooks/useRealtimeSync.d.ts +24 -0
  172. package/dist/hooks/useRealtimeSync.js +275 -0
  173. package/dist/hooks/useRealtimeSync.test.d.ts +1 -0
  174. package/dist/hooks/useRealtimeSync.test.js +284 -0
  175. package/dist/hooks/useSyncOrchestrator.d.ts +164 -0
  176. package/dist/hooks/useSyncOrchestrator.js +1427 -0
  177. package/dist/hooks/useSyncOrchestrator.retry-closure.test.d.ts +1 -0
  178. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +367 -0
  179. package/dist/hooks/useTaskforce.d.ts +727 -0
  180. package/dist/hooks/useTaskforce.js +4396 -0
  181. package/dist/hooks/useTaskforce.priority-filters.test.d.ts +1 -0
  182. package/dist/hooks/useTaskforce.priority-filters.test.js +33 -0
  183. package/dist/hooks/useWorkspaceSyncController.d.ts +80 -0
  184. package/dist/hooks/useWorkspaceSyncController.js +598 -0
  185. package/dist/hooks/useWorkspaceSyncController.test.d.ts +1 -0
  186. package/dist/hooks/useWorkspaceSyncController.test.js +236 -0
  187. package/dist/index.d.ts +12 -0
  188. package/dist/index.js +11 -0
  189. package/dist/localization/index.d.ts +8 -0
  190. package/dist/localization/index.js +81 -0
  191. package/dist/localization/locales/en-US.d.ts +151 -0
  192. package/dist/localization/locales/en-US.js +150 -0
  193. package/dist/localization/locales/es-419.d.ts +2 -0
  194. package/dist/localization/locales/es-419.js +150 -0
  195. package/dist/localization/locales/pt-BR.d.ts +2 -0
  196. package/dist/localization/locales/pt-BR.js +150 -0
  197. package/dist/main.d.ts +1 -0
  198. package/dist/main.js +33 -0
  199. package/dist/mcp/optionResolution.d.ts +14 -0
  200. package/dist/mcp/optionResolution.js +126 -0
  201. package/dist/mcp/optionResolution.test.d.ts +1 -0
  202. package/dist/mcp/optionResolution.test.js +39 -0
  203. package/dist/mcp/server.d.ts +1 -0
  204. package/dist/mcp/server.js +1793 -0
  205. package/dist/mcp/structuredCommentValidation.d.ts +6 -0
  206. package/dist/mcp/structuredCommentValidation.js +56 -0
  207. package/dist/mcp/structuredDocValidation.d.ts +6 -0
  208. package/dist/mcp/structuredDocValidation.js +75 -0
  209. package/dist/migrations/billingSchemaParity.test.d.ts +1 -0
  210. package/dist/migrations/billingSchemaParity.test.js +46 -0
  211. package/dist/migrations/clientMigrations.d.ts +9 -0
  212. package/dist/migrations/clientMigrations.js +74 -0
  213. package/dist/migrations/flags.d.ts +6 -0
  214. package/dist/migrations/flags.js +8 -0
  215. package/dist/migrations/taskMigrations.d.ts +71 -0
  216. package/dist/migrations/taskMigrations.js +236 -0
  217. package/dist/migrations/taskMigrations.test.d.ts +1 -0
  218. package/dist/migrations/taskMigrations.test.js +34 -0
  219. package/dist/migrations/taskSchemaMigrations.d.ts +11 -0
  220. package/dist/migrations/taskSchemaMigrations.js +453 -0
  221. package/dist/plugins/index.d.ts +10 -0
  222. package/dist/plugins/index.js +10 -0
  223. package/dist/plugins/vite.d.ts +19 -0
  224. package/dist/plugins/vite.js +271 -0
  225. package/dist/public/android-chrome-192x192.png +0 -0
  226. package/dist/public/android-chrome-512x512.png +0 -0
  227. package/dist/public/apple-touch-icon.png +0 -0
  228. package/dist/public/favicon-16x16.png +0 -0
  229. package/dist/public/favicon-32x32.png +0 -0
  230. package/dist/public/favicon.ico +0 -0
  231. package/dist/public/favicon.png +0 -0
  232. package/dist/public/site.webmanifest +1 -0
  233. package/dist/public/taskforce.png +0 -0
  234. package/dist/resources/templates/WORKFLOW_V2_POLICY.md +55 -0
  235. package/dist/resources/templates/environments/aider.yaml +14 -0
  236. package/dist/resources/templates/environments/antigravity.yaml +10 -0
  237. package/dist/resources/templates/environments/claude.yaml +10 -0
  238. package/dist/resources/templates/environments/cline.yaml +10 -0
  239. package/dist/resources/templates/environments/codex.yaml +13 -0
  240. package/dist/resources/templates/environments/copilot.yaml +10 -0
  241. package/dist/resources/templates/environments/cursor.yaml +9 -0
  242. package/dist/resources/templates/environments/gemini.yaml +9 -0
  243. package/dist/resources/templates/environments/windsurf.yaml +10 -0
  244. package/dist/resources/templates/workflow-sources/README.md +9 -0
  245. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +520 -0
  246. package/dist/resources/templates/workflow-sources/workflowEditorOverrides.json +1 -0
  247. package/dist/resources/templates/workflows/collaborate.yaml +104 -0
  248. package/dist/resources/templates/workflows/do.yaml +82 -0
  249. package/dist/resources/templates/workflows/evaluate.yaml +111 -0
  250. package/dist/resources/templates/workflows/execute.yaml +142 -0
  251. package/dist/resources/templates/workflows/plan.yaml +112 -0
  252. package/dist/resources/templates/workflows/review.yaml +109 -0
  253. package/dist/runtime/capabilities.d.ts +8 -0
  254. package/dist/runtime/capabilities.js +16 -0
  255. package/dist/security/virusScan.d.ts +16 -0
  256. package/dist/security/virusScan.js +83 -0
  257. package/dist/server/auth.d.ts +51 -0
  258. package/dist/server/auth.js +266 -0
  259. package/dist/server/auth.test.d.ts +1 -0
  260. package/dist/server/auth.test.js +111 -0
  261. package/dist/server/buildInfo.d.ts +7 -0
  262. package/dist/server/buildInfo.js +100 -0
  263. package/dist/server/buildInfo.test.d.ts +1 -0
  264. package/dist/server/buildInfo.test.js +29 -0
  265. package/dist/server/cors.d.ts +7 -0
  266. package/dist/server/cors.js +76 -0
  267. package/dist/server/cors.test.d.ts +1 -0
  268. package/dist/server/cors.test.js +71 -0
  269. package/dist/server/email.d.ts +28 -0
  270. package/dist/server/email.js +138 -0
  271. package/dist/server/email.test.d.ts +1 -0
  272. package/dist/server/email.test.js +113 -0
  273. package/dist/server/index.cookieProxy.test.d.ts +1 -0
  274. package/dist/server/index.cookieProxy.test.js +19 -0
  275. package/dist/server/index.d.ts +17 -0
  276. package/dist/server/index.js +757 -0
  277. package/dist/server/index.rateLimit.test.d.ts +1 -0
  278. package/dist/server/index.rateLimit.test.js +13 -0
  279. package/dist/server/index.test.d.ts +1 -0
  280. package/dist/server/index.test.js +19 -0
  281. package/dist/server/rateLimit.d.ts +29 -0
  282. package/dist/server/rateLimit.js +99 -0
  283. package/dist/server/rateLimit.test.d.ts +1 -0
  284. package/dist/server/rateLimit.test.js +107 -0
  285. package/dist/server/realtimeSync.auth.test.d.ts +1 -0
  286. package/dist/server/realtimeSync.auth.test.js +62 -0
  287. package/dist/server/realtimeSync.replay.test.d.ts +1 -0
  288. package/dist/server/realtimeSync.replay.test.js +192 -0
  289. package/dist/server/realtimeSync.workspaceIsolation.test.d.ts +1 -0
  290. package/dist/server/realtimeSync.workspaceIsolation.test.js +101 -0
  291. package/dist/server/realtimeSyncWs.d.ts +30 -0
  292. package/dist/server/realtimeSyncWs.js +257 -0
  293. package/dist/server/routes.billing.d.ts +15 -0
  294. package/dist/server/routes.billing.js +991 -0
  295. package/dist/server/routes.billing.test.d.ts +1 -0
  296. package/dist/server/routes.billing.test.js +387 -0
  297. package/dist/server/routes.d.ts +72 -0
  298. package/dist/server/routes.js +4820 -0
  299. package/dist/server/routes.sync.d.ts +17 -0
  300. package/dist/server/routes.sync.integration.test.d.ts +1 -0
  301. package/dist/server/routes.sync.integration.test.js +470 -0
  302. package/dist/server/routes.sync.js +1141 -0
  303. package/dist/server/routes.test.d.ts +1 -0
  304. package/dist/server/routes.test.js +4945 -0
  305. package/dist/server/runtimeMode.d.ts +4 -0
  306. package/dist/server/runtimeMode.js +25 -0
  307. package/dist/server/runtimeMode.test.d.ts +1 -0
  308. package/dist/server/runtimeMode.test.js +26 -0
  309. package/dist/server/securityHeaders.d.ts +3 -0
  310. package/dist/server/securityHeaders.js +25 -0
  311. package/dist/server/securityHeaders.test.d.ts +1 -0
  312. package/dist/server/securityHeaders.test.js +32 -0
  313. package/dist/server/smtpTransport.d.ts +21 -0
  314. package/dist/server/smtpTransport.js +11 -0
  315. package/dist/server/start.d.ts +1 -0
  316. package/dist/server/start.js +17 -0
  317. package/dist/services/formatTransformers.d.ts +29 -0
  318. package/dist/services/formatTransformers.js +56 -0
  319. package/dist/services/templateEngine.d.ts +46 -0
  320. package/dist/services/templateEngine.js +148 -0
  321. package/dist/shared/runtimeContract.d.ts +12 -0
  322. package/dist/shared/runtimeContract.js +23 -0
  323. package/dist/shared/runtimeContract.test.d.ts +1 -0
  324. package/dist/shared/runtimeContract.test.js +23 -0
  325. package/dist/soak/preflight.cli.d.ts +1 -0
  326. package/dist/soak/preflight.cli.js +28 -0
  327. package/dist/soak/preflight.d.ts +26 -0
  328. package/dist/soak/preflight.js +113 -0
  329. package/dist/soak/preflight.test.d.ts +1 -0
  330. package/dist/soak/preflight.test.js +39 -0
  331. package/dist/soak/syncSoakHarness.d.ts +6 -0
  332. package/dist/soak/syncSoakHarness.js +10 -0
  333. package/dist/storage/createDatabaseAdapter.d.ts +2 -0
  334. package/dist/storage/createDatabaseAdapter.js +9 -0
  335. package/dist/storage/databaseAdapter.d.ts +22 -0
  336. package/dist/storage/databaseAdapter.js +1 -0
  337. package/dist/storage/documentIntegrity.d.ts +28 -0
  338. package/dist/storage/documentIntegrity.js +103 -0
  339. package/dist/storage/documentPurge.d.ts +14 -0
  340. package/dist/storage/documentPurge.js +46 -0
  341. package/dist/storage/documentRegistry.d.ts +39 -0
  342. package/dist/storage/documentRegistry.js +115 -0
  343. package/dist/storage/integritySchedule.d.ts +17 -0
  344. package/dist/storage/integritySchedule.js +62 -0
  345. package/dist/storage/objectStorage.d.ts +20 -0
  346. package/dist/storage/objectStorage.js +15 -0
  347. package/dist/storage/objectStorageClient.d.ts +18 -0
  348. package/dist/storage/objectStorageClient.js +301 -0
  349. package/dist/storage/objectStorageConfig.d.ts +2 -0
  350. package/dist/storage/objectStorageConfig.js +45 -0
  351. package/dist/storage/postgresAdapter.d.ts +20 -0
  352. package/dist/storage/postgresAdapter.js +204 -0
  353. package/dist/storage/postgresWorker.d.ts +1 -0
  354. package/dist/storage/postgresWorker.js +110 -0
  355. package/dist/storage/providerConfig.d.ts +6 -0
  356. package/dist/storage/providerConfig.js +11 -0
  357. package/dist/storage/sqliteAdapter.d.ts +14 -0
  358. package/dist/storage/sqliteAdapter.js +28 -0
  359. package/dist/storage/taskAssetStore.d.ts +56 -0
  360. package/dist/storage/taskAssetStore.js +297 -0
  361. package/dist/sync/cloudSyncApi.d.ts +47 -0
  362. package/dist/sync/cloudSyncApi.js +138 -0
  363. package/dist/sync/cloudSyncApi.test.d.ts +1 -0
  364. package/dist/sync/cloudSyncApi.test.js +54 -0
  365. package/dist/sync/encryptionService.d.ts +11 -0
  366. package/dist/sync/encryptionService.js +61 -0
  367. package/dist/sync/encryptionService.test.d.ts +1 -0
  368. package/dist/sync/encryptionService.test.js +52 -0
  369. package/dist/sync/realtimeFeature.d.ts +6 -0
  370. package/dist/sync/realtimeFeature.js +20 -0
  371. package/dist/sync/realtimeFeature.test.d.ts +1 -0
  372. package/dist/sync/realtimeFeature.test.js +30 -0
  373. package/dist/sync/realtimeReconcile.d.ts +30 -0
  374. package/dist/sync/realtimeReconcile.js +90 -0
  375. package/dist/sync/realtimeReconcile.test.d.ts +1 -0
  376. package/dist/sync/realtimeReconcile.test.js +43 -0
  377. package/dist/sync/syncService.d.ts +96 -0
  378. package/dist/sync/syncService.js +423 -0
  379. package/dist/sync/syncService.test.d.ts +1 -0
  380. package/dist/sync/syncService.test.js +484 -0
  381. package/dist/sync/workspaceSyncModel.d.ts +61 -0
  382. package/dist/sync/workspaceSyncModel.js +136 -0
  383. package/dist/sync/workspaceSyncModel.test.d.ts +1 -0
  384. package/dist/sync/workspaceSyncModel.test.js +73 -0
  385. package/dist/test/setup.d.ts +1 -0
  386. package/dist/test/setup.js +15 -0
  387. package/dist/types.d.ts +157 -0
  388. package/dist/types.js +41 -0
  389. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  390. package/dist/ui/assets/index-0W5vyCpn.css +1 -0
  391. package/dist/ui/assets/index-MCZuNNfn.js +23 -0
  392. package/dist/ui/assets/logo_white_01-0Vx-iIIv.png +0 -0
  393. package/dist/ui/assets/taskforce-BxLPokNB.png +0 -0
  394. package/dist/ui/assets/vendor-dnd-BI1K77C1.js +5 -0
  395. package/dist/ui/assets/vendor-icons-ZFG3SGwo.js +1 -0
  396. package/dist/ui/assets/vendor-markdown-CKYkQHfj.js +29 -0
  397. package/dist/ui/assets/vendor-react-DdIDsh7f.js +9 -0
  398. package/dist/ui/assets/vendor-router-BtiNU7Dl.js +3 -0
  399. package/dist/ui/branding/logos/logo_black_01.png +0 -0
  400. package/dist/ui/branding/logos/logo_black_02.png +0 -0
  401. package/dist/ui/branding/logos/logo_white_01.png +0 -0
  402. package/dist/ui/branding/logos/logo_white_02.png +0 -0
  403. package/dist/ui/favicon/android-chrome-192x192.png +0 -0
  404. package/dist/ui/favicon/android-chrome-512x512.png +0 -0
  405. package/dist/ui/favicon/apple-touch-icon.png +0 -0
  406. package/dist/ui/favicon/favicon-16x16.png +0 -0
  407. package/dist/ui/favicon/favicon-32x32.png +0 -0
  408. package/dist/ui/favicon/favicon.ico +0 -0
  409. package/dist/ui/favicon/favicon.png +0 -0
  410. package/dist/ui/favicon/site.webmanifest +1 -0
  411. package/dist/ui/fonts/RussoOne-Regular.ttf +0 -0
  412. package/dist/ui/images/taskforce.png +0 -0
  413. package/dist/ui/index.html +19 -0
  414. package/dist/utils/checklistMarkdown.d.ts +9 -0
  415. package/dist/utils/checklistMarkdown.js +46 -0
  416. package/dist/utils/constants.d.ts +21 -0
  417. package/dist/utils/constants.icons.test.d.ts +1 -0
  418. package/dist/utils/constants.icons.test.js +40 -0
  419. package/dist/utils/constants.js +100 -0
  420. package/dist/utils/contextFiles.d.ts +8 -0
  421. package/dist/utils/contextFiles.js +54 -0
  422. package/dist/utils/formatting.d.ts +5 -0
  423. package/dist/utils/formatting.js +30 -0
  424. package/dist/utils/planParser.d.ts +22 -0
  425. package/dist/utils/planParser.js +151 -0
  426. package/dist/utils/taskDragZones.d.ts +8 -0
  427. package/dist/utils/taskDragZones.js +14 -0
  428. package/dist/utils/taskDragZones.test.d.ts +1 -0
  429. package/dist/utils/taskDragZones.test.js +14 -0
  430. package/dist/utils/taskHierarchy.d.ts +4 -0
  431. package/dist/utils/taskHierarchy.js +69 -0
  432. package/dist/utils/taskHierarchy.test.d.ts +1 -0
  433. package/dist/utils/taskHierarchy.test.js +38 -0
  434. package/dist/utils/taskParenting.d.ts +3 -0
  435. package/dist/utils/taskParenting.js +24 -0
  436. package/dist/utils/taskParenting.test.d.ts +1 -0
  437. package/dist/utils/taskParenting.test.js +21 -0
  438. package/dist/utils/taskRelations.d.ts +6 -0
  439. package/dist/utils/taskRelations.js +24 -0
  440. package/dist/utils/taskRelations.test.d.ts +1 -0
  441. package/dist/utils/taskRelations.test.js +34 -0
  442. package/dist/utils/taskUtils.d.ts +2 -0
  443. package/dist/utils/taskUtils.js +20 -0
  444. package/dist/utils/taskforceApiClient.d.ts +7 -0
  445. package/dist/utils/taskforceApiClient.js +42 -0
  446. package/dist/utils/workspaceIdentity.d.ts +8 -0
  447. package/dist/utils/workspaceIdentity.js +47 -0
  448. package/dist/utils/workspaceIdentity.test.d.ts +1 -0
  449. package/dist/utils/workspaceIdentity.test.js +32 -0
  450. package/dist/utils/workspaceLifecycle.d.ts +26 -0
  451. package/dist/utils/workspaceLifecycle.js +69 -0
  452. package/dist/utils/workspaceLifecycle.test.d.ts +1 -0
  453. package/dist/utils/workspaceLifecycle.test.js +64 -0
  454. package/package.json +133 -0
  455. package/scripts/check-compat-boundaries.js +48 -0
  456. package/scripts/check-env.ts +24 -0
  457. package/scripts/check-localization-strings.mjs +49 -0
  458. package/scripts/check-runtime-boundaries.mjs +44 -0
  459. package/scripts/debug-global.mjs +34 -0
  460. package/scripts/debug-server.mjs +36 -0
  461. package/scripts/export-sqlite-to-postgres.mjs +125 -0
  462. package/scripts/generate-env-example.ts +8 -0
  463. package/scripts/generate-workflow-templates.mjs +134 -0
  464. package/scripts/mcp-stdio-adapter.mjs +160 -0
  465. package/scripts/postinstall.js +60 -0
  466. package/scripts/release-version-lib.mjs +88 -0
  467. package/scripts/release-version.mjs +72 -0
  468. package/scripts/run-dev-proxy.sh +49 -0
  469. package/scripts/run-e2e-realtime.sh +16 -0
  470. package/scripts/run-smoke.sh +23 -0
  471. package/scripts/run-soak.sh +29 -0
  472. package/scripts/test-global.js +143 -0
  473. package/scripts/update-homebrew-formula.sh +38 -0
  474. package/src/resources/templates/WORKFLOW_V2_POLICY.md +55 -0
  475. package/src/resources/templates/environments/aider.yaml +14 -0
  476. package/src/resources/templates/environments/antigravity.yaml +10 -0
  477. package/src/resources/templates/environments/claude.yaml +10 -0
  478. package/src/resources/templates/environments/cline.yaml +10 -0
  479. package/src/resources/templates/environments/codex.yaml +13 -0
  480. package/src/resources/templates/environments/copilot.yaml +10 -0
  481. package/src/resources/templates/environments/cursor.yaml +9 -0
  482. package/src/resources/templates/environments/gemini.yaml +9 -0
  483. package/src/resources/templates/environments/windsurf.yaml +10 -0
  484. package/src/resources/templates/workflow-sources/README.md +9 -0
  485. package/src/resources/templates/workflow-sources/workflowDocs.mjs +520 -0
  486. package/src/resources/templates/workflow-sources/workflowEditorOverrides.json +1 -0
  487. package/src/resources/templates/workflows/collaborate.yaml +104 -0
  488. package/src/resources/templates/workflows/do.yaml +82 -0
  489. package/src/resources/templates/workflows/evaluate.yaml +111 -0
  490. package/src/resources/templates/workflows/execute.yaml +142 -0
  491. package/src/resources/templates/workflows/plan.yaml +112 -0
  492. package/src/resources/templates/workflows/review.yaml +109 -0
@@ -0,0 +1,51 @@
1
+ import type { IncomingMessage } from 'http';
2
+ import type { RuntimeMode } from './runtimeMode.js';
3
+ export type TaskforceRole = 'owner' | 'admin' | 'member' | 'read-only';
4
+ export type TaskforceSystemRole = 'system_admin' | 'user';
5
+ export interface TaskforceAuthConfig {
6
+ runtimeMode: RuntimeMode;
7
+ enabled: boolean;
8
+ requiredForApi: boolean;
9
+ excludedPaths: string[];
10
+ defaultRole: TaskforceRole;
11
+ defaultWorkspaceId: string;
12
+ sessionCookieName: string;
13
+ sessionSecret: string;
14
+ sessionTtlSeconds: number;
15
+ sessionCookieSameSite: 'Lax' | 'Strict' | 'None';
16
+ sessionCookieSecure: boolean;
17
+ sessionCookieDomain?: string;
18
+ }
19
+ export declare function resolveAuthConfigFromEnv(runtimeMode: RuntimeMode, env?: NodeJS.ProcessEnv): TaskforceAuthConfig;
20
+ interface SessionCookieOverrides {
21
+ secure?: boolean;
22
+ sameSite?: 'Lax' | 'Strict' | 'None';
23
+ }
24
+ interface SessionCookieAttributes {
25
+ secure: boolean;
26
+ sameSite: 'Lax' | 'Strict' | 'None';
27
+ }
28
+ export declare function resolveSessionCookieAttributesForRequest(req: IncomingMessage, config: TaskforceAuthConfig): SessionCookieAttributes;
29
+ export declare function createSessionTokenCookie(sessionId: string, config: TaskforceAuthConfig, options?: {
30
+ role?: TaskforceRole;
31
+ workspaceId?: string;
32
+ userId?: string;
33
+ secure?: boolean;
34
+ sameSite?: 'Lax' | 'Strict' | 'None';
35
+ }): string;
36
+ export declare function createClearSessionCookie(config: TaskforceAuthConfig, options?: SessionCookieOverrides): string;
37
+ export declare function hasValidSession(req: IncomingMessage, config: TaskforceAuthConfig): boolean;
38
+ export interface RequestAccessContext {
39
+ authenticated: boolean;
40
+ sessionId: string | null;
41
+ role: TaskforceRole;
42
+ systemRole: TaskforceSystemRole;
43
+ workspaceId: string;
44
+ userId: string;
45
+ }
46
+ export declare function resolveRequestAccess(req: IncomingMessage, config: TaskforceAuthConfig): RequestAccessContext;
47
+ export declare function isRequestAuthorized(req: IncomingMessage, config: TaskforceAuthConfig): {
48
+ allowed: boolean;
49
+ reason?: string;
50
+ };
51
+ export {};
@@ -0,0 +1,266 @@
1
+ import { createHmac, timingSafeEqual } from 'crypto';
2
+ function parseBoolean(raw, fallback) {
3
+ if (raw === undefined)
4
+ return fallback;
5
+ const normalized = String(raw).trim().toLowerCase();
6
+ if (['1', 'true', 'yes', 'on'].includes(normalized))
7
+ return true;
8
+ if (['0', 'false', 'no', 'off'].includes(normalized))
9
+ return false;
10
+ return fallback;
11
+ }
12
+ function normalizeRole(raw, fallback = 'member') {
13
+ const value = String(raw || '').trim().toLowerCase();
14
+ if (value === 'owner' || value === 'admin' || value === 'member' || value === 'read-only')
15
+ return value;
16
+ if (value === 'readonly')
17
+ return 'read-only';
18
+ return fallback;
19
+ }
20
+ function parsePositiveInt(raw, fallback) {
21
+ const value = Number.parseInt(String(raw || ''), 10);
22
+ if (!Number.isFinite(value) || value <= 0)
23
+ return fallback;
24
+ return value;
25
+ }
26
+ function normalizeSameSite(raw, fallback) {
27
+ const value = String(raw || '').trim().toLowerCase();
28
+ if (value === 'lax')
29
+ return 'Lax';
30
+ if (value === 'strict')
31
+ return 'Strict';
32
+ if (value === 'none')
33
+ return 'None';
34
+ return fallback;
35
+ }
36
+ function toBase64Url(input) {
37
+ return Buffer.from(input, 'utf8').toString('base64url');
38
+ }
39
+ function fromBase64Url(input) {
40
+ try {
41
+ return Buffer.from(input, 'base64url').toString('utf8');
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ function signPayload(payload, secret) {
48
+ return createHmac('sha256', secret).update(payload).digest('base64url');
49
+ }
50
+ function parseCookieHeader(raw) {
51
+ if (!raw)
52
+ return {};
53
+ const pairs = raw.split(';');
54
+ const result = {};
55
+ for (const pair of pairs) {
56
+ const trimmed = pair.trim();
57
+ if (!trimmed)
58
+ continue;
59
+ const idx = trimmed.indexOf('=');
60
+ if (idx <= 0)
61
+ continue;
62
+ const key = trimmed.slice(0, idx).trim();
63
+ const value = trimmed.slice(idx + 1).trim();
64
+ if (key)
65
+ result[key] = value;
66
+ }
67
+ return result;
68
+ }
69
+ export function resolveAuthConfigFromEnv(runtimeMode, env = process.env) {
70
+ const explicitEnabled = env.TASKFORCE_AUTH_ENABLED;
71
+ const enabled = explicitEnabled !== undefined
72
+ ? parseBoolean(explicitEnabled, false)
73
+ : (runtimeMode === 'cloud');
74
+ const requiredForApi = parseBoolean(env.TASKFORCE_AUTH_REQUIRED_FOR_API, runtimeMode === 'cloud');
75
+ const excludedPathsRaw = env.TASKFORCE_AUTH_EXCLUDED_PATHS || '/api/taskforce/health,/api/taskforce/version,/api/taskforce/auth';
76
+ const excludedPaths = excludedPathsRaw
77
+ .split(',')
78
+ .map(v => v.trim())
79
+ .filter(Boolean);
80
+ const sessionCookieName = (env.TASKFORCE_AUTH_SESSION_COOKIE || 'taskforce_session').trim();
81
+ const sessionSecret = (env.TASKFORCE_AUTH_SESSION_SECRET || '').trim();
82
+ const sessionTtlSeconds = parsePositiveInt(env.TASKFORCE_AUTH_SESSION_TTL_SECONDS, 60 * 60 * 24 * 30);
83
+ const sessionCookieSameSite = normalizeSameSite(env.TASKFORCE_AUTH_SESSION_SAMESITE, runtimeMode === 'cloud' ? 'None' : 'Lax');
84
+ const sessionCookieSecure = parseBoolean(env.TASKFORCE_AUTH_SESSION_SECURE, runtimeMode === 'cloud');
85
+ const sessionCookieDomain = String(env.TASKFORCE_AUTH_SESSION_DOMAIN || '').trim() || undefined;
86
+ const defaultRole = normalizeRole(env.TASKFORCE_AUTH_DEFAULT_ROLE, 'member');
87
+ // No env-configured default workspace. Workspace selection must come from
88
+ // explicit header/session resolution and user setup flows.
89
+ const defaultWorkspaceId = '';
90
+ return {
91
+ runtimeMode,
92
+ enabled,
93
+ requiredForApi,
94
+ excludedPaths,
95
+ defaultRole,
96
+ defaultWorkspaceId,
97
+ sessionCookieName,
98
+ sessionSecret,
99
+ sessionTtlSeconds,
100
+ sessionCookieSameSite,
101
+ sessionCookieSecure,
102
+ sessionCookieDomain
103
+ };
104
+ }
105
+ function resolveSessionCookieAttributes(config, overrides) {
106
+ const secure = typeof overrides?.secure === 'boolean'
107
+ ? overrides.secure
108
+ : config.sessionCookieSecure;
109
+ const preferredSameSite = overrides?.sameSite || config.sessionCookieSameSite;
110
+ // Browsers reject SameSite=None cookies unless Secure is present.
111
+ const sameSite = (!secure && preferredSameSite === 'None')
112
+ ? 'Lax'
113
+ : preferredSameSite;
114
+ return { secure, sameSite };
115
+ }
116
+ function firstHeaderValue(value) {
117
+ if (Array.isArray(value))
118
+ return String(value[0] || '');
119
+ return String(value || '');
120
+ }
121
+ function isLoopbackHost(host) {
122
+ const normalized = String(host || '').trim().toLowerCase();
123
+ return normalized === 'localhost' || normalized === '127.0.0.1' || normalized === '::1';
124
+ }
125
+ export function resolveSessionCookieAttributesForRequest(req, config) {
126
+ const forwardedProto = firstHeaderValue(req.headers['x-forwarded-proto'])
127
+ .split(',')[0]
128
+ .trim()
129
+ .toLowerCase();
130
+ const forwardedSsl = firstHeaderValue(req.headers['x-forwarded-ssl']).trim().toLowerCase();
131
+ const forwardedScheme = firstHeaderValue(req.headers['x-forwarded-scheme']).trim().toLowerCase();
132
+ const hostHeader = firstHeaderValue(req.headers.host).split(',')[0].trim();
133
+ const host = hostHeader.includes(':') ? hostHeader.slice(0, hostHeader.indexOf(':')) : hostHeader;
134
+ const encryptedSocket = Boolean(req.socket?.encrypted);
135
+ let requestIsHttps;
136
+ if (forwardedProto) {
137
+ requestIsHttps = forwardedProto === 'https';
138
+ }
139
+ else if (forwardedScheme) {
140
+ requestIsHttps = forwardedScheme === 'https';
141
+ }
142
+ else if (forwardedSsl) {
143
+ requestIsHttps = forwardedSsl === 'on' || forwardedSsl === '1' || forwardedSsl === 'true';
144
+ }
145
+ else if (encryptedSocket) {
146
+ requestIsHttps = true;
147
+ }
148
+ else {
149
+ // Many production deployments terminate TLS at a proxy and may not forward
150
+ // proto headers consistently. Default to secure for non-loopback hosts.
151
+ requestIsHttps = !isLoopbackHost(host);
152
+ }
153
+ const secure = config.sessionCookieSecure && requestIsHttps;
154
+ return resolveSessionCookieAttributes(config, { secure });
155
+ }
156
+ export function createSessionTokenCookie(sessionId, config, options) {
157
+ if (!config.sessionSecret) {
158
+ throw new Error('Session secret is not configured.');
159
+ }
160
+ const attrs = resolveSessionCookieAttributes(config, options);
161
+ const exp = Date.now() + (config.sessionTtlSeconds * 1000);
162
+ const payload = toBase64Url(JSON.stringify({
163
+ sessionId,
164
+ exp,
165
+ role: options?.role,
166
+ workspaceId: options?.workspaceId,
167
+ userId: options?.userId
168
+ }));
169
+ const sig = signPayload(payload, config.sessionSecret);
170
+ const value = `${payload}.${sig}`;
171
+ const domainPart = config.sessionCookieDomain ? `; Domain=${config.sessionCookieDomain}` : '';
172
+ const securePart = attrs.secure ? '; Secure' : '';
173
+ return `${config.sessionCookieName}=${value}; Path=/; HttpOnly; SameSite=${attrs.sameSite}; Max-Age=${config.sessionTtlSeconds}${securePart}${domainPart}`;
174
+ }
175
+ export function createClearSessionCookie(config, options) {
176
+ const attrs = resolveSessionCookieAttributes(config, options);
177
+ const domainPart = config.sessionCookieDomain ? `; Domain=${config.sessionCookieDomain}` : '';
178
+ const securePart = attrs.secure ? '; Secure' : '';
179
+ return `${config.sessionCookieName}=; Path=/; HttpOnly; SameSite=${attrs.sameSite}; Max-Age=0${securePart}${domainPart}`;
180
+ }
181
+ function readSessionClaimsFromRequest(req, config) {
182
+ if (!config.sessionSecret || !config.sessionCookieName)
183
+ return null;
184
+ const cookies = parseCookieHeader(typeof req.headers.cookie === 'string' ? req.headers.cookie : undefined);
185
+ const rawValue = cookies[config.sessionCookieName];
186
+ if (!rawValue)
187
+ return null;
188
+ const splitIndex = rawValue.lastIndexOf('.');
189
+ if (splitIndex <= 0)
190
+ return null;
191
+ const payload = rawValue.slice(0, splitIndex);
192
+ const sig = rawValue.slice(splitIndex + 1);
193
+ const expectedSig = signPayload(payload, config.sessionSecret);
194
+ const sigBuf = Buffer.from(sig);
195
+ const expectedBuf = Buffer.from(expectedSig);
196
+ if (sigBuf.length !== expectedBuf.length || !timingSafeEqual(sigBuf, expectedBuf))
197
+ return null;
198
+ const decoded = fromBase64Url(payload);
199
+ if (!decoded)
200
+ return null;
201
+ try {
202
+ const parsed = JSON.parse(decoded);
203
+ if (!parsed?.sessionId || typeof parsed.sessionId !== 'string')
204
+ return null;
205
+ if (!parsed?.exp || typeof parsed.exp !== 'number' || parsed.exp < Date.now())
206
+ return null;
207
+ return parsed;
208
+ }
209
+ catch {
210
+ return null;
211
+ }
212
+ }
213
+ export function hasValidSession(req, config) {
214
+ return Boolean(readSessionClaimsFromRequest(req, config));
215
+ }
216
+ export function resolveRequestAccess(req, config) {
217
+ const fallbackUserId = 'anonymous';
218
+ const requestedWorkspaceHeader = Array.isArray(req.headers['x-taskforce-workspace-id'])
219
+ ? req.headers['x-taskforce-workspace-id'][0]
220
+ : req.headers['x-taskforce-workspace-id'];
221
+ const requestedWorkspaceId = String(requestedWorkspaceHeader || '').trim();
222
+ const fallbackWorkspaceId = (requestedWorkspaceId
223
+ && requestedWorkspaceId.length <= 120
224
+ && !/\s/.test(requestedWorkspaceId)) ? requestedWorkspaceId : config.defaultWorkspaceId;
225
+ const base = {
226
+ authenticated: false,
227
+ sessionId: null,
228
+ role: config.defaultRole,
229
+ systemRole: 'user',
230
+ workspaceId: fallbackWorkspaceId,
231
+ userId: fallbackUserId
232
+ };
233
+ const sessionClaims = readSessionClaimsFromRequest(req, config);
234
+ if (sessionClaims?.sessionId) {
235
+ const role = normalizeRole(sessionClaims.role, config.defaultRole);
236
+ return {
237
+ authenticated: true,
238
+ sessionId: sessionClaims.sessionId,
239
+ role,
240
+ systemRole: 'user',
241
+ workspaceId: String(sessionClaims.workspaceId || config.defaultWorkspaceId).trim() || config.defaultWorkspaceId,
242
+ userId: String(sessionClaims.userId || fallbackUserId).trim() || fallbackUserId
243
+ };
244
+ }
245
+ return base;
246
+ }
247
+ function pathMatchesExcluded(pathname, excludedPaths) {
248
+ return excludedPaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));
249
+ }
250
+ export function isRequestAuthorized(req, config) {
251
+ if (!config.enabled || !config.requiredForApi)
252
+ return { allowed: true };
253
+ const pathname = (req.url || '/').split('?')[0] || '/';
254
+ if (!pathname.startsWith('/api/'))
255
+ return { allowed: true };
256
+ if (pathMatchesExcluded(pathname, config.excludedPaths))
257
+ return { allowed: true };
258
+ if (!config.sessionSecret)
259
+ return { allowed: false, reason: 'Auth enabled but session secret is not configured.' };
260
+ if (hasValidSession(req, config))
261
+ return { allowed: true };
262
+ const tokenMissing = !parseCookieHeader(typeof req.headers.cookie === 'string' ? req.headers.cookie : undefined)[config.sessionCookieName];
263
+ if (tokenMissing)
264
+ return { allowed: false, reason: 'Missing auth session.' };
265
+ return { allowed: false, reason: 'Invalid auth session.' };
266
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,111 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { createClearSessionCookie, createSessionTokenCookie, hasValidSession, isRequestAuthorized, resolveAuthConfigFromEnv, resolveRequestAccess, resolveSessionCookieAttributesForRequest } from './auth';
3
+ function mockReq(url, headers = {}) {
4
+ return {
5
+ url,
6
+ headers,
7
+ socket: { encrypted: false }
8
+ };
9
+ }
10
+ describe('server/auth', () => {
11
+ it('defaults to disabled auth when no explicit config in local mode', () => {
12
+ const config = resolveAuthConfigFromEnv('local', {});
13
+ expect(config.enabled).toBe(false);
14
+ expect(config.requiredForApi).toBe(false);
15
+ expect(config.sessionCookieSecure).toBe(false);
16
+ expect(config.excludedPaths).toEqual(['/api/taskforce/health', '/api/taskforce/version', '/api/taskforce/auth']);
17
+ });
18
+ it('defaults to auth-required in cloud mode', () => {
19
+ const config = resolveAuthConfigFromEnv('cloud', {});
20
+ expect(config.enabled).toBe(true);
21
+ expect(config.requiredForApi).toBe(true);
22
+ expect(config.sessionCookieSecure).toBe(true);
23
+ });
24
+ it('allows excluded health path without session', () => {
25
+ const config = resolveAuthConfigFromEnv('cloud', {
26
+ TASKFORCE_AUTH_ENABLED: 'true',
27
+ TASKFORCE_AUTH_SESSION_SECRET: 'secret'
28
+ });
29
+ const result = isRequestAuthorized(mockReq('/api/taskforce/health'), config);
30
+ expect(result.allowed).toBe(true);
31
+ });
32
+ it('denies protected api path without session', () => {
33
+ const config = resolveAuthConfigFromEnv('cloud', {
34
+ TASKFORCE_AUTH_ENABLED: 'true',
35
+ TASKFORCE_AUTH_SESSION_SECRET: 'secret'
36
+ });
37
+ const result = isRequestAuthorized(mockReq('/api/taskforce/tasks'), config);
38
+ expect(result.allowed).toBe(false);
39
+ expect(result.reason).toContain('Missing auth session');
40
+ });
41
+ it('allows protected api path with valid signed session cookie', () => {
42
+ const config = resolveAuthConfigFromEnv('cloud', {
43
+ TASKFORCE_AUTH_ENABLED: 'true',
44
+ TASKFORCE_AUTH_SESSION_SECRET: 'session-secret'
45
+ });
46
+ const cookieHeader = createSessionTokenCookie('session-1', config);
47
+ const cookieValue = cookieHeader.split(';')[0];
48
+ const result = isRequestAuthorized(mockReq('/api/taskforce/tasks', { cookie: cookieValue }), config);
49
+ expect(result.allowed).toBe(true);
50
+ expect(hasValidSession(mockReq('/api/taskforce/tasks', { cookie: cookieValue }), config)).toBe(true);
51
+ });
52
+ it('resolves role/workspace/user from session claims', () => {
53
+ const config = resolveAuthConfigFromEnv('cloud', {
54
+ TASKFORCE_AUTH_ENABLED: 'true',
55
+ TASKFORCE_AUTH_SESSION_SECRET: 'session-secret'
56
+ });
57
+ const cookieHeader = createSessionTokenCookie('session-2', config, {
58
+ role: 'admin',
59
+ workspaceId: 'workspace-123',
60
+ userId: 'user-abc'
61
+ });
62
+ const cookieValue = cookieHeader.split(';')[0];
63
+ const access = resolveRequestAccess(mockReq('/api/taskforce/tasks', { cookie: cookieValue }), config);
64
+ expect(access.authenticated).toBe(true);
65
+ expect(access.role).toBe('admin');
66
+ expect(access.workspaceId).toBe('workspace-123');
67
+ expect(access.userId).toBe('user-abc');
68
+ });
69
+ it('uses requested workspace header for unauthenticated access context', () => {
70
+ const config = resolveAuthConfigFromEnv('local', {});
71
+ const access = resolveRequestAccess(mockReq('/api/taskforce/tasks', { 'x-taskforce-workspace-id': 'project-acme' }), config);
72
+ expect(access.authenticated).toBe(false);
73
+ expect(access.workspaceId).toBe('project-acme');
74
+ });
75
+ it('downgrades SameSite=None to Lax when request is HTTP and secure cookies are unavailable', () => {
76
+ const config = resolveAuthConfigFromEnv('cloud', {
77
+ TASKFORCE_AUTH_ENABLED: 'true',
78
+ TASKFORCE_AUTH_SESSION_SECRET: 'session-secret'
79
+ });
80
+ const attrs = resolveSessionCookieAttributesForRequest(mockReq('/api/taskforce/auth/login', { 'x-forwarded-proto': 'http' }), config);
81
+ expect(attrs.secure).toBe(false);
82
+ expect(attrs.sameSite).toBe('Lax');
83
+ const cookie = createSessionTokenCookie('session-http', config, attrs);
84
+ expect(cookie).toContain('SameSite=Lax');
85
+ expect(cookie).not.toContain('; Secure');
86
+ const clearCookie = createClearSessionCookie(config, attrs);
87
+ expect(clearCookie).toContain('SameSite=Lax');
88
+ expect(clearCookie).not.toContain('; Secure');
89
+ });
90
+ it('keeps secure cookie attributes for non-loopback hosts when TLS is terminated upstream', () => {
91
+ const config = resolveAuthConfigFromEnv('cloud', {
92
+ TASKFORCE_AUTH_ENABLED: 'true',
93
+ TASKFORCE_AUTH_SESSION_SECRET: 'session-secret'
94
+ });
95
+ const attrs = resolveSessionCookieAttributesForRequest(mockReq('/api/taskforce/auth/login', { host: 'app.taskforcehq.ai' }), config);
96
+ expect(attrs.secure).toBe(true);
97
+ expect(attrs.sameSite).toBe('None');
98
+ const cookie = createSessionTokenCookie('session-proxy', config, attrs);
99
+ expect(cookie).toContain('SameSite=None');
100
+ expect(cookie).toContain('; Secure');
101
+ });
102
+ it('uses non-secure cookie attributes on loopback hosts without https forwarding headers', () => {
103
+ const config = resolveAuthConfigFromEnv('cloud', {
104
+ TASKFORCE_AUTH_ENABLED: 'true',
105
+ TASKFORCE_AUTH_SESSION_SECRET: 'session-secret'
106
+ });
107
+ const attrs = resolveSessionCookieAttributesForRequest(mockReq('/api/taskforce/auth/login', { host: 'localhost:3000' }), config);
108
+ expect(attrs.secure).toBe(false);
109
+ expect(attrs.sameSite).toBe('Lax');
110
+ });
111
+ });
@@ -0,0 +1,7 @@
1
+ export interface TaskforceBuildInfo {
2
+ version: string;
3
+ gitSha: string | null;
4
+ buildTime: string | null;
5
+ deployId: string | null;
6
+ }
7
+ export declare function resolveBuildInfo(env?: NodeJS.ProcessEnv): TaskforceBuildInfo;
@@ -0,0 +1,100 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ let cachedPackageVersion = null;
4
+ function readPackageVersion() {
5
+ if (cachedPackageVersion)
6
+ return cachedPackageVersion;
7
+ try {
8
+ const packagePath = path.resolve(process.cwd(), 'package.json');
9
+ const raw = fs.readFileSync(packagePath, 'utf-8');
10
+ const parsed = JSON.parse(raw);
11
+ const version = String(parsed?.version || '').trim();
12
+ cachedPackageVersion = version || '0.0.0-dev';
13
+ return cachedPackageVersion;
14
+ }
15
+ catch {
16
+ cachedPackageVersion = '0.0.0-dev';
17
+ return cachedPackageVersion;
18
+ }
19
+ }
20
+ function resolveFirstNonEmpty(candidates) {
21
+ for (const candidate of candidates) {
22
+ const value = String(candidate || '').trim();
23
+ if (value)
24
+ return value;
25
+ }
26
+ return null;
27
+ }
28
+ function normalizeGitSha(raw) {
29
+ if (!raw)
30
+ return null;
31
+ return raw.slice(0, 12);
32
+ }
33
+ function normalizeBuildToken(raw, maxLength = 40) {
34
+ const input = String(raw || '').trim();
35
+ if (!input)
36
+ return null;
37
+ const normalized = input
38
+ .replace(/[^0-9A-Za-z-]+/g, '-')
39
+ .replace(/-+/g, '-')
40
+ .replace(/^-|-$/g, '')
41
+ .slice(0, maxLength);
42
+ return normalized || null;
43
+ }
44
+ function buildTimeToken(raw) {
45
+ if (!raw)
46
+ return null;
47
+ const timestamp = Date.parse(raw);
48
+ if (!Number.isFinite(timestamp))
49
+ return null;
50
+ const iso = new Date(timestamp).toISOString();
51
+ // Compact UTC marker: 2026-02-20T12:34:56.000Z -> 20260220123456
52
+ return iso.slice(0, 19).replace(/[-:T]/g, '');
53
+ }
54
+ function appendBuildMetadata(version, parts) {
55
+ const cleanVersion = String(version || '').trim() || '0.0.0-dev';
56
+ if (cleanVersion.includes('+'))
57
+ return cleanVersion;
58
+ const metadata = parts
59
+ .map(part => normalizeBuildToken(part))
60
+ .filter((part) => Boolean(part))
61
+ .filter((part, index, all) => all.indexOf(part) === index);
62
+ if (metadata.length === 0)
63
+ return cleanVersion;
64
+ return `${cleanVersion}+${metadata.join('.')}`;
65
+ }
66
+ export function resolveBuildInfo(env = process.env) {
67
+ const explicitVersion = resolveFirstNonEmpty([
68
+ env.TASKFORCE_BUILD_VERSION,
69
+ env.APP_VERSION
70
+ ]);
71
+ const packageVersion = resolveFirstNonEmpty([env.npm_package_version]) || readPackageVersion();
72
+ const gitSha = normalizeGitSha(resolveFirstNonEmpty([
73
+ env.TASKFORCE_BUILD_GIT_SHA,
74
+ env.GIT_SHA,
75
+ env.RAILWAY_GIT_COMMIT_SHA,
76
+ env.CF_PAGES_COMMIT_SHA,
77
+ env.GITHUB_SHA
78
+ ]));
79
+ const buildTime = resolveFirstNonEmpty([
80
+ env.TASKFORCE_BUILD_TIME,
81
+ env.BUILD_TIME,
82
+ env.CF_PAGES_COMMIT_TIMESTAMP
83
+ ]);
84
+ const deployId = resolveFirstNonEmpty([
85
+ env.TASKFORCE_BUILD_DEPLOY_ID,
86
+ env.DEPLOY_ID,
87
+ env.RAILWAY_DEPLOYMENT_ID,
88
+ env.CF_PAGES_URL,
89
+ env.GITHUB_RUN_ID
90
+ ]);
91
+ const version = explicitVersion
92
+ ? explicitVersion
93
+ : appendBuildMetadata(packageVersion, [deployId, gitSha, buildTimeToken(buildTime)]);
94
+ return {
95
+ version,
96
+ gitSha,
97
+ buildTime,
98
+ deployId
99
+ };
100
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { resolveBuildInfo } from './buildInfo.js';
3
+ describe('resolveBuildInfo', () => {
4
+ it('uses explicit TASKFORCE_BUILD_VERSION without appending metadata', () => {
5
+ const info = resolveBuildInfo({
6
+ TASKFORCE_BUILD_VERSION: '1.2.3-custom',
7
+ TASKFORCE_BUILD_DEPLOY_ID: 'railway-9999',
8
+ TASKFORCE_BUILD_GIT_SHA: 'abcdef1234567890',
9
+ TASKFORCE_BUILD_TIME: '2026-02-20T12:34:56Z',
10
+ npm_package_version: '0.3.13-beta.2'
11
+ });
12
+ expect(info.version).toBe('1.2.3-custom');
13
+ });
14
+ it('appends deploy metadata when version is sourced from package version', () => {
15
+ const info = resolveBuildInfo({
16
+ npm_package_version: '0.3.13-beta.2',
17
+ TASKFORCE_BUILD_DEPLOY_ID: 'railway-123456',
18
+ TASKFORCE_BUILD_GIT_SHA: 'a1b2c3d4e5f67890',
19
+ TASKFORCE_BUILD_TIME: '2026-02-20T12:34:56Z'
20
+ });
21
+ expect(info.version).toBe('0.3.13-beta.2+railway-123456.a1b2c3d4e5f6.20260220123456');
22
+ });
23
+ it('leaves package version unchanged when no deploy metadata is available', () => {
24
+ const info = resolveBuildInfo({
25
+ npm_package_version: '0.3.13-beta.2'
26
+ });
27
+ expect(info.version).toBe('0.3.13-beta.2');
28
+ });
29
+ });
@@ -0,0 +1,7 @@
1
+ import type { IncomingMessage, ServerResponse } from 'http';
2
+ export declare function getAllowedOriginsFromEnv(envValue?: string): Set<string>;
3
+ export declare function resolveAllowedOrigin(originHeader: unknown, allowedOrigins: Set<string>): string | null;
4
+ export declare function applyCorsHeaders(req: IncomingMessage, res: ServerResponse, allowedOrigins: Set<string>): {
5
+ allowed: boolean;
6
+ resolvedOrigin: string | null;
7
+ };
@@ -0,0 +1,76 @@
1
+ const DEFAULT_ALLOWED_ORIGINS = [
2
+ 'https://taskforcehq.ai',
3
+ 'https://www.taskforcehq.ai',
4
+ 'https://app.taskforcehq.ai',
5
+ 'https://admin.taskforcehq.ai',
6
+ // Allow local development and packaged desktop webviews out of the box.
7
+ '__loopback__',
8
+ '__null__',
9
+ 'tauri://localhost',
10
+ 'capacitor://localhost',
11
+ 'ionic://localhost',
12
+ 'app://.'
13
+ ];
14
+ const CORS_ALLOW_METHODS = 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
15
+ const CORS_ALLOW_HEADERS = 'Content-Type';
16
+ export function getAllowedOriginsFromEnv(envValue) {
17
+ const raw = typeof envValue === 'string' ? envValue.trim() : '';
18
+ if (!raw)
19
+ return new Set(DEFAULT_ALLOWED_ORIGINS);
20
+ return new Set(raw
21
+ .split(',')
22
+ .map(v => v.trim())
23
+ .filter(Boolean));
24
+ }
25
+ export function resolveAllowedOrigin(originHeader, allowedOrigins) {
26
+ const origin = typeof originHeader === 'string' ? originHeader.trim() : '';
27
+ if (!origin)
28
+ return null;
29
+ if (origin === 'null' && allowedOrigins.has('__null__'))
30
+ return 'null';
31
+ if (allowedOrigins.has(origin))
32
+ return origin;
33
+ if (allowedOrigins.has('__loopback__') && isLoopbackOrigin(origin))
34
+ return origin;
35
+ for (const entry of allowedOrigins) {
36
+ if (!entry.includes('*'))
37
+ continue;
38
+ const prefix = entry.replace(/\*+$/g, '');
39
+ if (prefix && origin.startsWith(prefix))
40
+ return origin;
41
+ }
42
+ return null;
43
+ }
44
+ function isLoopbackOrigin(origin) {
45
+ try {
46
+ const parsed = new URL(origin);
47
+ const protocol = parsed.protocol.toLowerCase();
48
+ if (protocol !== 'http:' && protocol !== 'https:')
49
+ return false;
50
+ const hostname = parsed.hostname.toLowerCase();
51
+ return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]';
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ export function applyCorsHeaders(req, res, allowedOrigins) {
58
+ const resolvedOrigin = resolveAllowedOrigin(req.headers.origin, allowedOrigins);
59
+ res.setHeader('Access-Control-Allow-Methods', CORS_ALLOW_METHODS);
60
+ const requestedHeaders = req.headers['access-control-request-headers'];
61
+ if (requestedHeaders) {
62
+ res.setHeader('Access-Control-Allow-Headers', requestedHeaders);
63
+ }
64
+ else {
65
+ res.setHeader('Access-Control-Allow-Headers', CORS_ALLOW_HEADERS);
66
+ }
67
+ res.setHeader('Vary', 'Origin');
68
+ if (resolvedOrigin) {
69
+ res.setHeader('Access-Control-Allow-Origin', resolvedOrigin);
70
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
71
+ }
72
+ // Requests without Origin are considered non-CORS (allowed).
73
+ const hasOriginHeader = typeof req.headers.origin === 'string' && req.headers.origin.trim().length > 0;
74
+ const allowed = !hasOriginHeader || !!resolvedOrigin;
75
+ return { allowed, resolvedOrigin };
76
+ }
@@ -0,0 +1 @@
1
+ export {};