@taskforcehq/taskforce 0.3.306 → 0.3.307
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/Taskforce.module.css +317 -27
- package/dist/TaskforceCore.js +1 -0
- package/dist/TaskforceCore.test.js +178 -0
- package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
- package/dist/components/activity/EntityActivityTimeline.js +81 -0
- package/dist/components/context/ContextAttachmentManager.js +15 -3
- package/dist/components/features/AgentsModule.d.ts +5 -1
- package/dist/components/features/AgentsModule.js +142 -39
- package/dist/components/features/AgentsModule.test.js +260 -11
- package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
- package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
- package/dist/components/features/DocumentIndex.d.ts +2 -2
- package/dist/components/features/DocumentIndex.js +3 -4
- package/dist/components/features/DocumentViewer.d.ts +3 -1
- package/dist/components/features/DocumentViewer.js +6 -5
- package/dist/components/features/DocumentWorkspace.d.ts +10 -2
- package/dist/components/features/DocumentWorkspace.js +15 -4
- package/dist/components/features/DocumentWorkspace.test.js +20 -3
- package/dist/components/features/TaskSettings.d.ts +2 -0
- package/dist/components/features/TaskSettings.js +83 -78
- package/dist/components/features/TaskSettings.test.js +171 -82
- package/dist/components/task/TaskActionHeader.js +11 -5
- package/dist/components/task/TaskActionHeader.test.js +10 -0
- package/dist/components/task/TaskCard.js +6 -2
- package/dist/components/task/TaskContextUpload.test.js +3 -3
- package/dist/components/task/TaskForm.js +14 -64
- package/dist/components/task/TaskKanban.js +0 -8
- package/dist/components/task/TaskList.js +5 -1
- package/dist/components/task/TaskList.test.js +4 -0
- package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
- package/dist/components/ui/HierarchyActionBadge.js +5 -0
- package/dist/components/views/AppShellHeader.d.ts +3 -1
- package/dist/components/views/AppShellHeader.js +27 -11
- package/dist/components/views/AppShellHeader.test.js +18 -1
- package/dist/components/views/PlanComparisonPage.js +9 -1
- package/dist/components/views/PlanComparisonPage.test.js +19 -0
- package/dist/components/views/PlansPage.js +1 -1
- package/dist/components/views/StandaloneLayout.js +256 -69
- package/dist/components/views/WidgetView.js +10 -8
- package/dist/components/views/panels/PlanningDrawer.d.ts +19 -6
- package/dist/components/views/panels/PlanningDrawer.js +259 -51
- package/dist/components/views/panels/PlanningDrawer.test.js +392 -4
- package/dist/config/envSchema.js +1 -0
- package/dist/core/AiProfileService.d.ts +7 -0
- package/dist/core/AiProfileService.js +460 -35
- package/dist/core/AiProfiles.test.js +648 -2
- package/dist/core/AttachmentLinkService.d.ts +3 -0
- package/dist/core/AttachmentLinkService.js +58 -13
- package/dist/core/GlobalSettingsService.js +12 -0
- package/dist/core/McpTokenService.d.ts +12 -0
- package/dist/core/McpTokenService.js +44 -1
- package/dist/core/PlanningEntities.test.js +366 -4
- package/dist/core/TaskActivityService.d.ts +28 -1
- package/dist/core/TaskActivityService.js +227 -46
- package/dist/core/TaskActivitySync.test.d.ts +1 -0
- package/dist/core/TaskActivitySync.test.js +90 -0
- package/dist/core/TaskAuditTimeline.test.js +96 -0
- package/dist/core/Taskforce.d.ts +190 -26
- package/dist/core/Taskforce.ids.test.js +58 -0
- package/dist/core/Taskforce.js +775 -71
- package/dist/core/Taskforce.mcpAuth.test.js +109 -0
- package/dist/core/types.d.ts +7 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +2 -1
- package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
- package/dist/hooks/bootstrapTimeouts.js +8 -0
- package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
- package/dist/hooks/bootstrapTimeouts.test.js +12 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +3 -3
- package/dist/hooks/sync/orchestratorShared.js +61 -1
- package/dist/hooks/sync/recovery.js +8 -0
- package/dist/hooks/sync/recovery.test.js +5 -5
- package/dist/hooks/sync/transfers.d.ts +2 -1
- package/dist/hooks/sync/transfers.js +36 -9
- package/dist/hooks/sync/transfers.test.js +534 -2
- package/dist/hooks/useFilterSort.js +13 -13
- package/dist/hooks/useFilterSort.test.js +87 -1
- package/dist/hooks/useSyncOrchestrator.js +50 -5
- package/dist/hooks/useTaskforce.d.ts +85 -50
- package/dist/hooks/useTaskforce.js +44 -14
- package/dist/hooks/useTaskforce.runtime-routing.test.js +94 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +99 -0
- package/dist/hooks/useWorkspaceSyncController.js +7 -1
- package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +14 -7
- package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
- package/dist/mcp/canonicalAssetHelpers.js +18 -10
- package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
- package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
- package/dist/mcp/clientIntegrations.d.ts +1 -0
- package/dist/mcp/clientIntegrations.js +25 -0
- package/dist/mcp/clientIntegrations.test.js +14 -3
- package/dist/mcp/clientProfileDefaults.js +3 -3
- package/dist/mcp/collaborationRegistrar.js +7 -7
- package/dist/mcp/documentAssetRegistrar.js +113 -7
- package/dist/mcp/runtime.d.ts +1 -0
- package/dist/mcp/runtime.js +969 -122
- package/dist/mcp/runtime.test.js +1083 -11
- package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
- package/dist/mcp/taskPlanningRegistrar.js +48 -0
- package/dist/mcp/toolRegistry.d.ts +56 -0
- package/dist/mcp/toolRegistry.js +8 -0
- package/dist/mcp/toolRegistry.test.js +4 -0
- package/dist/migrations/taskSchemaMigrations.js +21 -0
- package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
- package/dist/server/aiProfileAvatarAssets.js +319 -0
- package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
- package/dist/server/aiProfileAvatarAssets.test.js +213 -0
- package/dist/server/annotatedAttachmentsRoutes.test.js +66 -7
- package/dist/server/index.js +4 -0
- package/dist/server/index.test.js +6 -1
- package/dist/server/routes/admin.js +187 -9
- package/dist/server/routes/annotatedAttachments.js +51 -0
- package/dist/server/routes/auth.d.ts +11 -0
- package/dist/server/routes/auth.js +189 -27
- package/dist/server/routes/authSupport.d.ts +4 -0
- package/dist/server/routes/authSupport.js +12 -1
- package/dist/server/routes/billing.js +11 -0
- package/dist/server/routes/billing.test.js +3 -0
- package/dist/server/routes/documents.js +10 -0
- package/dist/server/routes/shared.js +0 -10
- package/dist/server/routes/sync.integration.test.js +221 -0
- package/dist/server/routes/sync.js +13 -3
- package/dist/server/routes/syncSnapshotRoutes.js +2 -0
- package/dist/server/routes/tasks.js +12 -9
- package/dist/server/routes.js +87 -8
- package/dist/server/routes.test.js +890 -22
- package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
- package/dist/shared/aiProfileSurfaceType.js +10 -8
- package/dist/storage/objectStorage.d.ts +1 -0
- package/dist/storage/objectStorage.js +2 -1
- package/dist/storage/objectStorageClient.d.ts +1 -0
- package/dist/storage/objectStorageClient.js +41 -8
- package/dist/storage/objectStorageClient.test.js +26 -1
- package/dist/storage/objectStorageConfig.js +3 -1
- package/dist/storage/objectStorageConfig.test.js +13 -1
- package/dist/sync/collaborationSyncPayload.js +2 -0
- package/dist/sync/collaborationSyncPayload.test.js +1 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -3
- package/dist/sync/collaborationSyncState.js +19 -12
- package/dist/sync/contentSyncPayload.d.ts +5 -1
- package/dist/sync/contentSyncPayload.js +9 -0
- package/dist/sync/contentSyncPayload.test.js +39 -0
- package/dist/sync/contentSyncState.d.ts +5 -1
- package/dist/sync/contentSyncState.js +231 -37
- package/dist/sync/planningSyncPayload.js +5 -0
- package/dist/sync/planningSyncPayload.test.js +24 -0
- package/dist/sync/syncApplyHandlers.d.ts +4 -4
- package/dist/sync/syncApplyHandlers.js +89 -31
- package/dist/sync/syncApplyHandlers.test.js +184 -4
- package/dist/sync/syncResourceAdapters.d.ts +2 -0
- package/dist/sync/syncResourceAdapters.js +2 -0
- package/dist/sync/syncService.d.ts +1 -0
- package/dist/sync/syncService.js +13 -1
- package/dist/sync/syncService.test.js +39 -0
- package/dist/sync/workspacePullFeed.d.ts +4 -0
- package/dist/sync/workspacePullFeed.js +129 -70
- package/dist/sync/workspacePullFeed.test.js +49 -0
- package/dist/sync/workspaceSyncModel.d.ts +28 -6
- package/dist/sync/workspaceSyncModel.js +159 -45
- package/dist/sync/workspaceSyncModel.test.js +269 -3
- package/dist/types.d.ts +33 -2
- package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
- package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
- package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
- package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
- package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
- package/dist/ui/assets/{InitiativesModule-9E6ParrY.js → InitiativesModule-D4XseTwW.js} +1 -1
- package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
- package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
- package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
- package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BA7jcEpH.js → WorkflowsModule-oOA9bcdK.js} +4 -4
- package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
- package/dist/ui/assets/index-607WPo_X.js +4 -0
- package/dist/ui/assets/index-CYyodXsg.css +1 -0
- package/dist/ui/assets/{vendor-icons-DuEd_65c.js → vendor-icons-B32hGuF2.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/product/agent manager.png +0 -0
- package/dist/ui/product/agent_manager_02.png +0 -0
- package/dist/utils/aiProfileEvents.d.ts +7 -0
- package/dist/utils/aiProfileEvents.js +6 -0
- package/dist/utils/assignees.d.ts +6 -0
- package/dist/utils/assignees.js +8 -1
- package/dist/utils/assignees.test.d.ts +1 -0
- package/dist/utils/assignees.test.js +25 -0
- package/dist/utils/avatarDisplay.d.ts +1 -0
- package/dist/utils/avatarDisplay.js +15 -0
- package/dist/utils/contextFiles.d.ts +3 -1
- package/dist/utils/contextFiles.js +2 -1
- package/dist/utils/taskActivity.d.ts +7 -1
- package/dist/utils/taskActivity.js +63 -1
- package/dist/utils/taskActivity.test.js +23 -1
- package/dist/utils/taskSearch.d.ts +22 -0
- package/dist/utils/taskSearch.js +87 -0
- package/dist/utils/taskSearch.test.d.ts +1 -0
- package/dist/utils/taskSearch.test.js +69 -0
- package/dist/utils/taskUtils.js +10 -3
- package/dist/utils/taskUtils.test.d.ts +1 -0
- package/dist/utils/taskUtils.test.js +19 -0
- package/package.json +1 -1
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-Z9_whf7F.js +0 -3
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +0 -252
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +0 -1
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +0 -1
- package/dist/ui/assets/PlansPage-DRXscYxH.js +0 -1
- package/dist/ui/assets/TaskSettings-DZX4jk7e.js +0 -9
- package/dist/ui/assets/index-9eT8e0Lu.css +0 -1
- package/dist/ui/assets/index-C6k75jr8.js +0 -6
|
@@ -79,6 +79,26 @@ export function registerAdminRoutes(deps) {
|
|
|
79
79
|
const workspaceAssetStore = typeof core.getDatabaseAdapter === 'function' && typeof core.getTenantId === 'function'
|
|
80
80
|
? new WorkspaceAssetStore(core.getDatabaseAdapter(), core.getTenantId())
|
|
81
81
|
: null;
|
|
82
|
+
const isAiProfileAvatarStorageKey = (storageKey, workspaceId) => {
|
|
83
|
+
const normalized = String(storageKey || '').replace(/\\/g, '/');
|
|
84
|
+
return normalized.startsWith(`workspaces/${workspaceId}/assets/images/ai-profiles/`)
|
|
85
|
+
&& normalized.includes('/avatar/');
|
|
86
|
+
};
|
|
87
|
+
const listReferencedAiProfileAvatarStorageKeys = (workspaceId) => {
|
|
88
|
+
const referenced = new Set();
|
|
89
|
+
const profiles = typeof core.listAiProfiles === 'function'
|
|
90
|
+
? core.listAiProfiles(workspaceId, { includeArchived: true })
|
|
91
|
+
: [];
|
|
92
|
+
for (const profile of Array.isArray(profiles) ? profiles : []) {
|
|
93
|
+
for (const value of [profile?.avatarUrl, profile?.avatarSourceUrl]) {
|
|
94
|
+
const storageKey = normalizeDocumentPathInput(value);
|
|
95
|
+
if (!storageKey || !isAiProfileAvatarStorageKey(storageKey, workspaceId))
|
|
96
|
+
continue;
|
|
97
|
+
referenced.add(storageKey);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return referenced;
|
|
101
|
+
};
|
|
82
102
|
// === SECTION: Email helpers ===
|
|
83
103
|
const resolveEffectiveVirusScanSettings = () => {
|
|
84
104
|
const settings = core.getGlobalSettings().objectStorage || {};
|
|
@@ -95,11 +115,15 @@ export function registerAdminRoutes(deps) {
|
|
|
95
115
|
};
|
|
96
116
|
// === SECTION: MCP helpers ===
|
|
97
117
|
const MCP_CONNECTION_TEST_TIMEOUT_MS = 5000;
|
|
98
|
-
const writeMcpHttpError = (res, statusCode, message) => {
|
|
118
|
+
const writeMcpHttpError = (res, statusCode, message, data) => {
|
|
99
119
|
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
100
120
|
res.end(JSON.stringify({
|
|
101
121
|
jsonrpc: '2.0',
|
|
102
|
-
error: {
|
|
122
|
+
error: {
|
|
123
|
+
code: -32000,
|
|
124
|
+
message,
|
|
125
|
+
...(data ? { data } : {}),
|
|
126
|
+
},
|
|
103
127
|
id: null,
|
|
104
128
|
}));
|
|
105
129
|
};
|
|
@@ -394,6 +418,8 @@ export function registerAdminRoutes(deps) {
|
|
|
394
418
|
authenticatedViaOAuth: oauthAuth || null,
|
|
395
419
|
userId: null,
|
|
396
420
|
...bootstrap,
|
|
421
|
+
aiProfileToken: bootstrap.aiProfileToken || oauthAuth?.aiProfileToken || null,
|
|
422
|
+
aiProfileName: bootstrap.aiProfileName || oauthAuth?.aiProfileName || null,
|
|
397
423
|
};
|
|
398
424
|
}
|
|
399
425
|
core.addMcpAuditLog({
|
|
@@ -506,6 +532,7 @@ export function registerAdminRoutes(deps) {
|
|
|
506
532
|
return;
|
|
507
533
|
}
|
|
508
534
|
const authScopes = auth.authenticatedViaToken?.scopes || auth.authenticatedViaOAuth?.scopes || [];
|
|
535
|
+
const requestId = String(req.headers['x-request-id'] || req.headers['x-railway-request-id'] || '').trim() || randomUUID();
|
|
509
536
|
const deniedToolName = findDeniedCloudMcpToolName(body, authScopes);
|
|
510
537
|
if (deniedToolName) {
|
|
511
538
|
core.addMcpAuditLog({
|
|
@@ -515,7 +542,7 @@ export function registerAdminRoutes(deps) {
|
|
|
515
542
|
tokenId: auth.authenticatedViaToken?.tokenId || auth.authenticatedViaOAuth?.grantId || null,
|
|
516
543
|
toolName: deniedToolName,
|
|
517
544
|
details: {
|
|
518
|
-
requestId
|
|
545
|
+
requestId,
|
|
519
546
|
ip: req.socket?.remoteAddress || null,
|
|
520
547
|
userAgent: requestHeader(req, 'user-agent', '') || null,
|
|
521
548
|
outcome: 'deny',
|
|
@@ -528,11 +555,13 @@ export function registerAdminRoutes(deps) {
|
|
|
528
555
|
}
|
|
529
556
|
if (!applyCloudMcpWriteRateLimit(req, res, auth, body))
|
|
530
557
|
return;
|
|
558
|
+
let runtime = null;
|
|
531
559
|
try {
|
|
532
|
-
|
|
560
|
+
runtime = createTaskforceMcpServer({
|
|
533
561
|
projectRoot: core.getPaths().projectRoot,
|
|
534
562
|
tenantId: core.getTenantId(),
|
|
535
563
|
workspaceId: auth.workspaceId,
|
|
564
|
+
core,
|
|
536
565
|
serverName: 'taskforce-cloud',
|
|
537
566
|
logStartup: false,
|
|
538
567
|
runtimeMode: 'cloud',
|
|
@@ -540,7 +569,7 @@ export function registerAdminRoutes(deps) {
|
|
|
540
569
|
req,
|
|
541
570
|
auth,
|
|
542
571
|
requestHeader,
|
|
543
|
-
requestId
|
|
572
|
+
requestId,
|
|
544
573
|
authScopes,
|
|
545
574
|
}),
|
|
546
575
|
});
|
|
@@ -557,8 +586,27 @@ export function registerAdminRoutes(deps) {
|
|
|
557
586
|
recordCloudMcpCredentialUsage(req, auth.authenticatedViaToken, auth.authenticatedViaOAuth);
|
|
558
587
|
}
|
|
559
588
|
catch (error) {
|
|
589
|
+
console.error('[Taskforce MCP] Cloud transport request failed:', {
|
|
590
|
+
requestId,
|
|
591
|
+
kind: 'tool_transport_failure',
|
|
592
|
+
message: error instanceof Error ? error.message : String(error),
|
|
593
|
+
});
|
|
560
594
|
if (!res.headersSent) {
|
|
561
|
-
writeMcpHttpError(res, 500, '
|
|
595
|
+
writeMcpHttpError(res, 500, 'MCP transport failure.', {
|
|
596
|
+
kind: 'tool_transport_failure',
|
|
597
|
+
retryable: true,
|
|
598
|
+
requestId,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
finally {
|
|
603
|
+
if (runtime) {
|
|
604
|
+
try {
|
|
605
|
+
await runtime.server.close();
|
|
606
|
+
}
|
|
607
|
+
catch (closeError) {
|
|
608
|
+
console.warn('[Taskforce MCP] Cloud transport cleanup failed:', closeError);
|
|
609
|
+
}
|
|
562
610
|
}
|
|
563
611
|
}
|
|
564
612
|
};
|
|
@@ -1231,8 +1279,10 @@ export function registerAdminRoutes(deps) {
|
|
|
1231
1279
|
}
|
|
1232
1280
|
const globalSettings = core.getGlobalSettings();
|
|
1233
1281
|
const authSettings = globalSettings.auth || {};
|
|
1282
|
+
const siteSettings = globalSettings.site || {};
|
|
1234
1283
|
const onboardingPolicy = core.getOnboardingPolicy();
|
|
1235
1284
|
const effective = buildAuthBehavior(core);
|
|
1285
|
+
const effectiveSite = buildSiteBehavior(core);
|
|
1236
1286
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1237
1287
|
res.end(JSON.stringify({
|
|
1238
1288
|
settings: {
|
|
@@ -1242,6 +1292,12 @@ export function registerAdminRoutes(deps) {
|
|
|
1242
1292
|
...(typeof authSettings.authRateLimitEnabled === 'boolean' ? { authRateLimitEnabled: authSettings.authRateLimitEnabled } : {}),
|
|
1243
1293
|
...(typeof authSettings.authRateLimitMaxRequests === 'number' ? { authRateLimitMaxRequests: authSettings.authRateLimitMaxRequests } : {}),
|
|
1244
1294
|
...(typeof authSettings.authRateLimitWindowMs === 'number' ? { authRateLimitWindowMs: authSettings.authRateLimitWindowMs } : {}),
|
|
1295
|
+
...(typeof siteSettings.showRunTaskforceLocally === 'boolean' ? { showRunTaskforceLocally: siteSettings.showRunTaskforceLocally } : {}),
|
|
1296
|
+
...(typeof siteSettings.pricingPageEnabled === 'boolean' ? { pricingPageEnabled: siteSettings.pricingPageEnabled } : {}),
|
|
1297
|
+
...(typeof siteSettings.animatedBackgroundEnabled === 'boolean' ? { animatedBackgroundEnabled: siteSettings.animatedBackgroundEnabled } : {}),
|
|
1298
|
+
...(siteSettings.siteTheme === 'light' || siteSettings.siteTheme === 'dark' ? { siteTheme: siteSettings.siteTheme } : {}),
|
|
1299
|
+
...(typeof siteSettings.pricingPageTitle === 'string' ? { pricingPageTitle: siteSettings.pricingPageTitle } : {}),
|
|
1300
|
+
...(typeof siteSettings.pricingPageDescription === 'string' ? { pricingPageDescription: siteSettings.pricingPageDescription } : {}),
|
|
1245
1301
|
onboardingPolicyMode: onboardingPolicy.mode,
|
|
1246
1302
|
onboardingPlanId: onboardingPolicy.planId
|
|
1247
1303
|
},
|
|
@@ -1252,6 +1308,12 @@ export function registerAdminRoutes(deps) {
|
|
|
1252
1308
|
authRateLimitEnabled: effective.authRateLimitEnabled,
|
|
1253
1309
|
authRateLimitMaxRequests: effective.authRateLimitMaxRequests,
|
|
1254
1310
|
authRateLimitWindowMs: effective.authRateLimitWindowMs,
|
|
1311
|
+
showRunTaskforceLocally: effectiveSite.showRunTaskforceLocally,
|
|
1312
|
+
pricingPageEnabled: effectiveSite.pricingPageEnabled,
|
|
1313
|
+
animatedBackgroundEnabled: effectiveSite.animatedBackgroundEnabled,
|
|
1314
|
+
siteTheme: effectiveSite.siteTheme,
|
|
1315
|
+
pricingPageTitle: effectiveSite.pricingPageTitle,
|
|
1316
|
+
pricingPageDescription: effectiveSite.pricingPageDescription,
|
|
1255
1317
|
onboardingPolicyMode: effective.onboardingPolicyMode,
|
|
1256
1318
|
onboardingPlanId: effective.onboardingPlanId
|
|
1257
1319
|
}
|
|
@@ -1273,11 +1335,27 @@ export function registerAdminRoutes(deps) {
|
|
|
1273
1335
|
: {}),
|
|
1274
1336
|
...(typeof siteSettings.pricingPageEnabled === 'boolean'
|
|
1275
1337
|
? { pricingPageEnabled: siteSettings.pricingPageEnabled }
|
|
1338
|
+
: {}),
|
|
1339
|
+
...(typeof siteSettings.animatedBackgroundEnabled === 'boolean'
|
|
1340
|
+
? { animatedBackgroundEnabled: siteSettings.animatedBackgroundEnabled }
|
|
1341
|
+
: {}),
|
|
1342
|
+
...(siteSettings.siteTheme === 'light' || siteSettings.siteTheme === 'dark'
|
|
1343
|
+
? { siteTheme: siteSettings.siteTheme }
|
|
1344
|
+
: {}),
|
|
1345
|
+
...(typeof siteSettings.pricingPageTitle === 'string'
|
|
1346
|
+
? { pricingPageTitle: siteSettings.pricingPageTitle }
|
|
1347
|
+
: {}),
|
|
1348
|
+
...(typeof siteSettings.pricingPageDescription === 'string'
|
|
1349
|
+
? { pricingPageDescription: siteSettings.pricingPageDescription }
|
|
1276
1350
|
: {})
|
|
1277
1351
|
},
|
|
1278
1352
|
status: {
|
|
1279
1353
|
showRunTaskforceLocally: siteBehavior.showRunTaskforceLocally,
|
|
1280
|
-
pricingPageEnabled: siteBehavior.pricingPageEnabled
|
|
1354
|
+
pricingPageEnabled: siteBehavior.pricingPageEnabled,
|
|
1355
|
+
animatedBackgroundEnabled: siteBehavior.animatedBackgroundEnabled,
|
|
1356
|
+
siteTheme: siteBehavior.siteTheme,
|
|
1357
|
+
pricingPageTitle: siteBehavior.pricingPageTitle,
|
|
1358
|
+
pricingPageDescription: siteBehavior.pricingPageDescription
|
|
1281
1359
|
}
|
|
1282
1360
|
}));
|
|
1283
1361
|
};
|
|
@@ -1448,9 +1526,11 @@ export function registerAdminRoutes(deps) {
|
|
|
1448
1526
|
}
|
|
1449
1527
|
const reqUrl = new URL(req.url || '/api/taskforce/admin/orphaned-assets', 'http://localhost');
|
|
1450
1528
|
const workspaceId = String(reqUrl.searchParams.get('workspaceId') || requestWorkspaceId(req) || '').trim();
|
|
1529
|
+
const aiProfileAvatarStorageKeys = listReferencedAiProfileAvatarStorageKeys(workspaceId);
|
|
1451
1530
|
const assets = workspaceAssetStore.listAllByWorkspace(workspaceId, {
|
|
1452
1531
|
includeDeleted: false
|
|
1453
|
-
}).filter((asset) =>
|
|
1532
|
+
}).filter((asset) => (!aiProfileAvatarStorageKeys.has(asset.storageKey)
|
|
1533
|
+
&& workspaceAssetStore.listLinksForAsset(asset.assetId, workspaceId).every((link) => !!link.deletedAt)))
|
|
1454
1534
|
.map((asset) => ({
|
|
1455
1535
|
assetId: asset.assetId,
|
|
1456
1536
|
workspaceId: asset.workspaceId,
|
|
@@ -1529,6 +1609,11 @@ export function registerAdminRoutes(deps) {
|
|
|
1529
1609
|
res.end(JSON.stringify({ error: 'Asset not found.' }));
|
|
1530
1610
|
return;
|
|
1531
1611
|
}
|
|
1612
|
+
if (listReferencedAiProfileAvatarStorageKeys(workspaceId).has(asset.storageKey)) {
|
|
1613
|
+
res.writeHead(409, { 'Content-Type': 'application/json' });
|
|
1614
|
+
res.end(JSON.stringify({ error: 'Asset is still referenced by an AI profile avatar.' }));
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1532
1617
|
const objectStorageClient = getObjectStorageClient();
|
|
1533
1618
|
const { basePath } = core.getPaths();
|
|
1534
1619
|
try {
|
|
@@ -1891,6 +1976,25 @@ export function registerAdminRoutes(deps) {
|
|
|
1891
1976
|
: body?.onboardingPlanId === null
|
|
1892
1977
|
? null
|
|
1893
1978
|
: currentOnboardingPolicy.planId;
|
|
1979
|
+
const pricingPageTitle = typeof body?.pricingPageTitle === 'string'
|
|
1980
|
+
? body.pricingPageTitle.trim()
|
|
1981
|
+
: undefined;
|
|
1982
|
+
const pricingPageDescription = typeof body?.pricingPageDescription === 'string'
|
|
1983
|
+
? body.pricingPageDescription.trim()
|
|
1984
|
+
: undefined;
|
|
1985
|
+
const siteTheme = body?.siteTheme === 'light' || body?.siteTheme === 'dark'
|
|
1986
|
+
? body.siteTheme
|
|
1987
|
+
: undefined;
|
|
1988
|
+
if (pricingPageTitle !== undefined && pricingPageTitle.length > 120) {
|
|
1989
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1990
|
+
res.end(JSON.stringify({ error: 'Pricing page title must be 120 characters or fewer.', code: 'PRICING_PAGE_TITLE_TOO_LONG' }));
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1993
|
+
if (pricingPageDescription !== undefined && pricingPageDescription.length > 280) {
|
|
1994
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1995
|
+
res.end(JSON.stringify({ error: 'Pricing page description must be 280 characters or fewer.', code: 'PRICING_PAGE_DESCRIPTION_TOO_LONG' }));
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1894
1998
|
const validation = core.validateOnboardingPolicy({
|
|
1895
1999
|
mode: onboardingPolicyMode,
|
|
1896
2000
|
planId: onboardingPlanId
|
|
@@ -1900,6 +2004,26 @@ export function registerAdminRoutes(deps) {
|
|
|
1900
2004
|
res.end(JSON.stringify({ error: validation.message || 'Invalid onboarding policy.', code: validation.code, details: validation.details }));
|
|
1901
2005
|
return;
|
|
1902
2006
|
}
|
|
2007
|
+
const siteSettingsUpdate = {
|
|
2008
|
+
...(typeof body?.showRunTaskforceLocally === 'boolean'
|
|
2009
|
+
? { showRunTaskforceLocally: body.showRunTaskforceLocally }
|
|
2010
|
+
: {}),
|
|
2011
|
+
...(typeof body?.pricingPageEnabled === 'boolean'
|
|
2012
|
+
? { pricingPageEnabled: body.pricingPageEnabled }
|
|
2013
|
+
: {}),
|
|
2014
|
+
...(typeof body?.animatedBackgroundEnabled === 'boolean'
|
|
2015
|
+
? { animatedBackgroundEnabled: body.animatedBackgroundEnabled }
|
|
2016
|
+
: {}),
|
|
2017
|
+
...(siteTheme !== undefined
|
|
2018
|
+
? { siteTheme }
|
|
2019
|
+
: {}),
|
|
2020
|
+
...(pricingPageTitle !== undefined
|
|
2021
|
+
? { pricingPageTitle }
|
|
2022
|
+
: {}),
|
|
2023
|
+
...(pricingPageDescription !== undefined
|
|
2024
|
+
? { pricingPageDescription }
|
|
2025
|
+
: {})
|
|
2026
|
+
};
|
|
1903
2027
|
core.updateGlobalSettings({
|
|
1904
2028
|
auth: {
|
|
1905
2029
|
...(typeof body?.allowSelfRegistration === 'boolean' ? { allowSelfRegistration: body.allowSelfRegistration } : {}),
|
|
@@ -1916,7 +2040,10 @@ export function registerAdminRoutes(deps) {
|
|
|
1916
2040
|
mode: onboardingPolicyMode,
|
|
1917
2041
|
planId: onboardingPlanId ?? null
|
|
1918
2042
|
}
|
|
1919
|
-
}
|
|
2043
|
+
},
|
|
2044
|
+
...(Object.keys(siteSettingsUpdate).length > 0
|
|
2045
|
+
? { site: siteSettingsUpdate }
|
|
2046
|
+
: {})
|
|
1920
2047
|
});
|
|
1921
2048
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1922
2049
|
res.end(JSON.stringify({ success: true }));
|
|
@@ -1928,6 +2055,25 @@ export function registerAdminRoutes(deps) {
|
|
|
1928
2055
|
return;
|
|
1929
2056
|
}
|
|
1930
2057
|
const body = await parseJsonBody(req);
|
|
2058
|
+
const pricingPageTitle = typeof body?.pricingPageTitle === 'string'
|
|
2059
|
+
? body.pricingPageTitle.trim()
|
|
2060
|
+
: undefined;
|
|
2061
|
+
const pricingPageDescription = typeof body?.pricingPageDescription === 'string'
|
|
2062
|
+
? body.pricingPageDescription.trim()
|
|
2063
|
+
: undefined;
|
|
2064
|
+
const siteTheme = body?.siteTheme === 'light' || body?.siteTheme === 'dark'
|
|
2065
|
+
? body.siteTheme
|
|
2066
|
+
: undefined;
|
|
2067
|
+
if (pricingPageTitle !== undefined && pricingPageTitle.length > 120) {
|
|
2068
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
2069
|
+
res.end(JSON.stringify({ error: 'Pricing page title must be 120 characters or fewer.', code: 'PRICING_PAGE_TITLE_TOO_LONG' }));
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
if (pricingPageDescription !== undefined && pricingPageDescription.length > 280) {
|
|
2073
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
2074
|
+
res.end(JSON.stringify({ error: 'Pricing page description must be 280 characters or fewer.', code: 'PRICING_PAGE_DESCRIPTION_TOO_LONG' }));
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
1931
2077
|
core.updateGlobalSettings({
|
|
1932
2078
|
site: {
|
|
1933
2079
|
...(typeof body?.showRunTaskforceLocally === 'boolean'
|
|
@@ -1935,6 +2081,18 @@ export function registerAdminRoutes(deps) {
|
|
|
1935
2081
|
: {}),
|
|
1936
2082
|
...(typeof body?.pricingPageEnabled === 'boolean'
|
|
1937
2083
|
? { pricingPageEnabled: body.pricingPageEnabled }
|
|
2084
|
+
: {}),
|
|
2085
|
+
...(typeof body?.animatedBackgroundEnabled === 'boolean'
|
|
2086
|
+
? { animatedBackgroundEnabled: body.animatedBackgroundEnabled }
|
|
2087
|
+
: {}),
|
|
2088
|
+
...(siteTheme !== undefined
|
|
2089
|
+
? { siteTheme }
|
|
2090
|
+
: {}),
|
|
2091
|
+
...(pricingPageTitle !== undefined
|
|
2092
|
+
? { pricingPageTitle }
|
|
2093
|
+
: {}),
|
|
2094
|
+
...(pricingPageDescription !== undefined
|
|
2095
|
+
? { pricingPageDescription }
|
|
1938
2096
|
: {})
|
|
1939
2097
|
}
|
|
1940
2098
|
});
|
|
@@ -2509,6 +2667,26 @@ export function registerAdminRoutes(deps) {
|
|
|
2509
2667
|
res.end(JSON.stringify({ success: false, error: error.message, code: error?.code || 'MCP_TOKEN_REVOKE_FAILED' }));
|
|
2510
2668
|
}
|
|
2511
2669
|
});
|
|
2670
|
+
addRoute('DELETE', '/api/taskforce/settings/mcp/tokens/:id', async (req, res, params) => {
|
|
2671
|
+
if (!ensureAppAccess(req, res))
|
|
2672
|
+
return;
|
|
2673
|
+
const userId = resolveAuthenticatedUserId(req);
|
|
2674
|
+
if (!userId || userId === 'anonymous') {
|
|
2675
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
2676
|
+
res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
try {
|
|
2680
|
+
const deleted = core.deleteMcpAccessToken({ actorUserId: userId, workspaceId: requestWorkspaceId(req), tokenId: params.id });
|
|
2681
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2682
|
+
res.end(JSON.stringify({ success: true, deleted }));
|
|
2683
|
+
}
|
|
2684
|
+
catch (error) {
|
|
2685
|
+
const statusCode = resolveErrorStatusCode(error);
|
|
2686
|
+
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
2687
|
+
res.end(JSON.stringify({ success: false, error: error.message, code: error?.code || 'MCP_TOKEN_DELETE_FAILED' }));
|
|
2688
|
+
}
|
|
2689
|
+
});
|
|
2512
2690
|
addRoute('POST', '/api/taskforce/settings/mcp/tokens/:id/regenerate', async (req, res, params) => {
|
|
2513
2691
|
if (!ensureAppAccess(req, res))
|
|
2514
2692
|
return;
|
|
@@ -40,6 +40,57 @@ export function registerAnnotatedAttachmentRoutes(deps) {
|
|
|
40
40
|
});
|
|
41
41
|
return false;
|
|
42
42
|
};
|
|
43
|
+
addRoute('GET', '/api/taskforce/annotated-attachments/images', async (req, res) => {
|
|
44
|
+
const workspaceId = requestWorkspaceId(req);
|
|
45
|
+
if (!ensureAnnotatedAttachmentAccess(req, res, workspaceId))
|
|
46
|
+
return;
|
|
47
|
+
try {
|
|
48
|
+
const assetStore = core.getWorkspaceAssetStore();
|
|
49
|
+
const imageAssets = assetStore
|
|
50
|
+
.listAllByWorkspace(workspaceId, { kind: 'image' })
|
|
51
|
+
.filter((entry) => !entry.deletedAt);
|
|
52
|
+
const images = imageAssets.map((asset) => {
|
|
53
|
+
const activeLinks = assetStore
|
|
54
|
+
.listLinksForAsset(asset.assetId, workspaceId)
|
|
55
|
+
.filter((entry) => !entry.deletedAt)
|
|
56
|
+
.filter((entry) => entry.targetType !== 'task' || (!!entry.taskId && !!core.getTask(entry.taskId, workspaceId)));
|
|
57
|
+
const activeTaskLinks = activeLinks
|
|
58
|
+
.filter((entry) => entry.targetType === 'task' && !!entry.taskId)
|
|
59
|
+
.filter((entry) => !!core.getTask(entry.taskId, workspaceId));
|
|
60
|
+
const firstTaskLink = activeTaskLinks[0] || null;
|
|
61
|
+
const linkedTask = firstTaskLink?.taskId
|
|
62
|
+
? core.getTask(firstTaskLink.taskId, workspaceId)
|
|
63
|
+
: null;
|
|
64
|
+
const sessions = core.listAnnotatedAttachmentSessions({
|
|
65
|
+
workspaceId,
|
|
66
|
+
imageAssetId: asset.assetId,
|
|
67
|
+
});
|
|
68
|
+
const displayName = String(asset.logicalName || asset.originalFilename || asset.assetId).trim() || asset.assetId;
|
|
69
|
+
return {
|
|
70
|
+
assetId: asset.assetId,
|
|
71
|
+
referenceNumber: typeof asset.referenceNumber === 'number' ? asset.referenceNumber : null,
|
|
72
|
+
imageReferenceLabel: getImageReferenceLabel(asset) || null,
|
|
73
|
+
path: `/api/taskforce/context/${encodeURIComponent(asset.storageKey)}`,
|
|
74
|
+
displayName,
|
|
75
|
+
originalFilename: asset.originalFilename || null,
|
|
76
|
+
mimeType: asset.mimeType,
|
|
77
|
+
sizeBytes: asset.sizeBytes,
|
|
78
|
+
updatedAt: asset.updatedAt || null,
|
|
79
|
+
attachmentCount: activeTaskLinks.length,
|
|
80
|
+
sessionCount: sessions.length,
|
|
81
|
+
taskId: firstTaskLink?.taskId || null,
|
|
82
|
+
taskReferenceLabel: linkedTask?.referenceLabel || null,
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
86
|
+
writeJson(res, 200, { images });
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
writeJson(res, resolveErrorStatusCode(error, 400), {
|
|
90
|
+
error: String(error?.message || 'Failed to list annotated attachment images.'),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
43
94
|
addRoute('GET', '/api/taskforce/annotated-attachments/images/:reference', async (req, res, params) => {
|
|
44
95
|
const workspaceId = requestWorkspaceId(req);
|
|
45
96
|
if (!ensureAnnotatedAttachmentAccess(req, res, workspaceId))
|
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
* GET /api/taskforce/auth/workspace-members
|
|
39
39
|
* GET /api/taskforce/workspace/assignee-options
|
|
40
40
|
* POST /api/taskforce/workspace/ai-profiles/avatar
|
|
41
|
+
* POST /api/taskforce/workspace/ai-profiles/avatar/upload
|
|
42
|
+
* POST /api/taskforce/workspace/ai-profiles/surface-type
|
|
41
43
|
* GET /api/taskforce/admin/users
|
|
42
44
|
* POST /api/taskforce/admin/users/invite
|
|
43
45
|
* PATCH /api/taskforce/admin/users/:id/role
|
|
@@ -52,6 +54,7 @@ import type { TaskforceCore } from '../../core/Taskforce.js';
|
|
|
52
54
|
import { type RouteHandler, type RouteContext } from './primitives.js';
|
|
53
55
|
import type { ResolvedObjectStorageConfig } from '../../storage/objectStorage.js';
|
|
54
56
|
import { ObjectStorageClient } from '../../storage/objectStorageClient.js';
|
|
57
|
+
import type { WorkspaceAssetStore } from '../../storage/workspaceAssetStore.js';
|
|
55
58
|
export interface AuthRouteDependencies {
|
|
56
59
|
addRoute: (method: string, path: string, handler: RouteHandler) => void;
|
|
57
60
|
core: TaskforceCore;
|
|
@@ -73,5 +76,13 @@ export interface AuthRouteDependencies {
|
|
|
73
76
|
};
|
|
74
77
|
resolveEffectiveObjectStorageConfig: () => ResolvedObjectStorageConfig;
|
|
75
78
|
getObjectStorageClient: () => ObjectStorageClient | null;
|
|
79
|
+
workspaceAssetStore: WorkspaceAssetStore | null;
|
|
80
|
+
ensureWithinDir: (candidatePath: string, rootDir: string) => string | null;
|
|
81
|
+
markAssetDeleted: (pathValue: string) => unknown;
|
|
82
|
+
appendStorageTelemetry: (event: string, payload?: {
|
|
83
|
+
path?: string;
|
|
84
|
+
status?: string;
|
|
85
|
+
provider?: string;
|
|
86
|
+
}) => void;
|
|
76
87
|
}
|
|
77
88
|
export declare function registerAuthRoutes(deps: AuthRouteDependencies): void;
|