@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.
- package/README.md +140 -247
- package/dist/Taskforce.module.css +102 -5
- package/dist/TaskforceCore.js +635 -238
- package/dist/TaskforceCore.test.js +2243 -495
- package/dist/annotatedAttachments/service.d.ts +1 -1
- package/dist/annotatedAttachments/types.d.ts +1 -1
- package/dist/compat/workspaceSyncCompat.js +6 -0
- package/dist/components/features/AgentsModule.js +1 -1
- package/dist/components/features/AgentsModule.test.js +1 -1
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +1 -1
- package/dist/components/features/DocumentViewer.js +32 -9
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +193 -6
- package/dist/components/features/TaskSettings.js +87 -41
- package/dist/components/features/TaskSettings.test.js +180 -40
- package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
- package/dist/components/features/documentWorkspaceModel.js +57 -0
- package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
- package/dist/components/features/useDocumentWorkspaceController.js +113 -0
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +3 -3
- package/dist/components/task/TaskActionHeader.test.js +8 -0
- package/dist/components/task/TaskCard.js +16 -1
- package/dist/components/task/TaskCard.test.js +31 -0
- package/dist/components/task/TaskForm.d.ts +9 -8
- package/dist/components/task/TaskForm.js +374 -219
- package/dist/components/task/TaskForm.test.js +482 -361
- package/dist/components/task/TaskKanban.d.ts +1 -1
- package/dist/components/task/TaskKanban.js +33 -161
- package/dist/components/task/TaskStatusActions.d.ts +6 -1
- package/dist/components/task/TaskStatusActions.js +8 -3
- package/dist/components/task/TaskStatusActions.test.js +9 -0
- package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
- package/dist/components/task/taskKanbanDropRules.js +187 -0
- package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
- package/dist/components/task/taskKanbanDropRules.test.js +114 -0
- package/dist/components/ui/Modal.d.ts +1 -1
- package/dist/components/ui/Modal.js +1 -0
- package/dist/components/views/AppShellHeader.js +5 -1
- package/dist/components/views/AppShellHeader.test.js +4 -0
- package/dist/components/views/PlanComparisonPage.d.ts +3 -2
- package/dist/components/views/PlanComparisonPage.js +9 -10
- package/dist/components/views/PlanComparisonPage.test.js +55 -41
- package/dist/components/views/PlansPage.d.ts +5 -1
- package/dist/components/views/PlansPage.js +150 -81
- package/dist/components/views/PlansPage.test.js +367 -41
- package/dist/components/views/StandaloneLayout.js +135 -54
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +6 -1
- package/dist/core/AiProfileService.js +161 -16
- package/dist/core/AiProfileService.test.js +3 -1
- package/dist/core/AiProfiles.test.js +200 -0
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/EntitlementsPolicy.test.js +75 -13
- package/dist/core/PlanEntitlementService.d.ts +9 -1
- package/dist/core/PlanEntitlementService.js +144 -19
- package/dist/core/PlanVersionPolicy.test.js +19 -26
- package/dist/core/SignupMonetizationSettings.test.js +75 -15
- package/dist/core/SystemAdmin.test.js +212 -56
- package/dist/core/TaskActivityService.d.ts +4 -1
- package/dist/core/TaskActivityService.js +45 -0
- package/dist/core/TaskAuditTimeline.test.js +52 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +18 -11
- package/dist/core/Taskforce.js +93 -12
- package/dist/core/WorkspacePlanMode.test.js +73 -8
- package/dist/core/shared.d.ts +2 -0
- package/dist/core/shared.js +11 -0
- package/dist/documentReviews/service.d.ts +1 -1
- package/dist/documentReviews/types.d.ts +1 -1
- package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
- package/dist/hooks/sync/auth.d.ts +37 -0
- package/dist/hooks/sync/auth.js +63 -0
- package/dist/hooks/sync/bootstrap.d.ts +34 -0
- package/dist/hooks/sync/bootstrap.js +52 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
- package/dist/hooks/sync/orchestratorShared.js +209 -0
- package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
- package/dist/hooks/sync/orchestratorShared.test.js +49 -0
- package/dist/hooks/sync/realtime.d.ts +25 -0
- package/dist/hooks/sync/realtime.js +60 -0
- package/dist/hooks/sync/recovery.d.ts +69 -0
- package/dist/hooks/sync/recovery.js +118 -0
- package/dist/hooks/sync/transfers.d.ts +123 -0
- package/dist/hooks/sync/transfers.js +435 -0
- package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
- package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
- package/dist/hooks/ui/useResourceExport.d.ts +3 -1
- package/dist/hooks/ui/useResourceExport.js +5 -2
- package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
- package/dist/hooks/useRealtimeSync.js +2 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +12 -8
- package/dist/hooks/useSyncOrchestrator.js +388 -879
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +641 -6
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +57 -7
- package/dist/hooks/useTaskMutations.test.js +176 -0
- package/dist/hooks/useTaskforce.d.ts +13 -4
- package/dist/hooks/useTaskforce.js +302 -817
- package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +33 -11
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
- package/dist/hooks/workspace/workspaceLocalState.js +100 -0
- package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
- package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
- package/dist/localization/locales/en-US.js +2 -2
- package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
- package/dist/mcp/canonicalAssetHelpers.js +259 -0
- package/dist/mcp/clientIntegrations.d.ts +12 -1
- package/dist/mcp/clientIntegrations.js +161 -6
- package/dist/mcp/clientIntegrations.test.js +76 -4
- package/dist/mcp/collaborationRegistrar.d.ts +2 -0
- package/dist/mcp/collaborationRegistrar.js +71 -0
- package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
- package/dist/mcp/documentAssetRegistrar.js +346 -0
- package/dist/mcp/documentHelpers.d.ts +100 -0
- package/dist/mcp/documentHelpers.js +161 -0
- package/dist/mcp/runtime.js +2455 -3925
- package/dist/mcp/runtime.test.js +240 -8
- package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
- package/dist/mcp/taskAttachmentHelpers.js +60 -0
- package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
- package/dist/mcp/taskAttachmentTypes.js +1 -0
- package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
- package/dist/mcp/taskPlanningRegistrar.js +418 -0
- package/dist/mcp/toolCatalog.d.ts +35 -0
- package/dist/mcp/toolCatalog.js +3 -0
- package/dist/plugins/vite.js +1 -1
- package/dist/runtime/appGateDefinitions.d.ts +1 -1
- package/dist/runtime/appGateDefinitions.js +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +46 -3
- package/dist/server/index.test.js +12 -1
- package/dist/server/routes/admin.d.ts +1 -5
- package/dist/server/routes/admin.js +151 -80
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +1 -4
- package/dist/server/routes/auth.js +59 -64
- package/dist/server/routes/authSupport.d.ts +30 -0
- package/dist/server/routes/authSupport.js +81 -0
- package/dist/server/routes/billing.d.ts +1 -1
- package/dist/server/routes/billing.js +122 -255
- package/dist/server/routes/billing.test.js +257 -86
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +4 -2
- package/dist/server/routes/documents.js +73 -7
- package/dist/server/routes/primitives.d.ts +11 -0
- package/dist/server/routes/primitives.js +73 -0
- package/dist/server/routes/resources.d.ts +1 -1
- package/dist/server/routes/resources.js +1 -1
- package/dist/server/routes/shared.d.ts +31 -1
- package/dist/server/routes/shared.js +26 -0
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +20 -0
- package/dist/server/routes/sync.js +111 -238
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +50 -8
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +1 -1
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +84 -181
- package/dist/server/routes.test.js +912 -32
- package/dist/shared/taskActor.d.ts +10 -0
- package/dist/shared/taskActor.js +1 -0
- package/dist/sync/collaborationSyncPayload.js +4 -0
- package/dist/sync/collaborationSyncPayload.test.js +8 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -1
- package/dist/sync/collaborationSyncState.js +19 -0
- package/dist/sync/contentSyncState.js +28 -17
- package/dist/sync/syncApplyHandlers.d.ts +87 -1
- package/dist/sync/syncApplyHandlers.js +370 -8
- package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.test.js +379 -0
- package/dist/sync/syncService.d.ts +9 -1
- package/dist/sync/syncService.js +44 -2
- package/dist/sync/syncService.test.js +62 -0
- package/dist/sync/workspacePullFeed.d.ts +1 -0
- package/dist/sync/workspacePullFeed.js +66 -59
- package/dist/sync/workspacePullFeed.test.js +72 -4
- package/dist/sync/workspaceSyncModel.d.ts +25 -1
- package/dist/sync/workspaceSyncModel.js +77 -3
- package/dist/sync/workspaceSyncModel.test.js +153 -0
- package/dist/sync/workspaceSyncState.d.ts +2 -0
- package/dist/sync/workspaceSyncState.js +1 -0
- package/dist/types.d.ts +2 -10
- package/dist/ui/assets/AgentsModule-N2MnQBZk.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
- package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-Dhm7M8e7.js} +1 -1
- package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
- package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-BcS4afRn.js} +1 -1
- package/dist/ui/assets/index-C2-OXZV7.css +1 -0
- package/dist/ui/assets/index-DcSI5F86.js +6 -0
- package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-BSUaRwF8.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +196 -0
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +10 -1
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/playwright-auth.sh +9 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-C3GyzrWm.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
- package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
- package/dist/ui/assets/index-CLIMgR6g.js +0 -6
- package/dist/ui/assets/index-DneETxcu.css +0 -1
package/dist/server/routes.js
CHANGED
|
@@ -14,7 +14,7 @@ import { parseAiProfileBootstrapHeaders } from '../mcp/aiProfileBootstrap.js';
|
|
|
14
14
|
import { createTaskforceMcpServer, isMcpWriteToolName } from '../mcp/runtime.js';
|
|
15
15
|
import { applyPrefixedRateLimitHeaders } from './rateLimit.js';
|
|
16
16
|
import { createSessionTokenCookie, resolveRequestAccess } from './auth.js';
|
|
17
|
-
import {
|
|
17
|
+
import { sendEmail } from './email.js';
|
|
18
18
|
import { normalizeTaskDataRelativePath, ObjectStorageClient } from '../storage/objectStorageClient.js';
|
|
19
19
|
import { DocumentRegistry, createDocumentHash } from '../storage/documentRegistry.js';
|
|
20
20
|
import { TaskAssetStore } from '../storage/taskAssetStore.js';
|
|
@@ -34,12 +34,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 {
|
|
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
|
-
|
|
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, '<')
|
|
160
|
-
.replace(/>/g, '>')
|
|
161
|
-
.replace(/"/g, '"')
|
|
162
|
-
.replace(/'/g, ''');
|
|
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\
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
|
|
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 (
|
|
826
|
-
return {
|
|
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:
|
|
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
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
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
|
-
|
|
1477
|
-
|
|
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,
|