@taskforcehq/taskforce 0.3.306 → 0.3.308
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.d.ts +20 -15
- package/dist/components/views/PlanComparisonPage.js +221 -82
- package/dist/components/views/PlanComparisonPage.test.js +268 -14
- package/dist/components/views/PlansPage.js +16 -10
- package/dist/components/views/PlansPage.test.js +4 -2
- 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 +30 -0
- package/dist/core/GlobalSettingsService.test.d.ts +1 -0
- package/dist/core/GlobalSettingsService.test.js +45 -0
- package/dist/core/McpTokenService.d.ts +12 -0
- package/dist/core/McpTokenService.js +44 -1
- package/dist/core/PlanEntitlementService.d.ts +20 -0
- package/dist/core/PlanEntitlementService.js +215 -19
- package/dist/core/PlanFeatureCatalog.test.js +55 -0
- 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 +204 -26
- package/dist/core/Taskforce.ids.test.js +58 -0
- package/dist/core/Taskforce.js +781 -71
- package/dist/core/Taskforce.mcpAuth.test.js +109 -0
- package/dist/core/types.d.ts +10 -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 +58 -15
- 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 +328 -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 +5 -0
- package/dist/server/routes/authSupport.js +13 -1
- package/dist/server/routes/billing.d.ts +1 -0
- package/dist/server/routes/billing.js +116 -63
- package/dist/server/routes/billing.test.js +162 -11
- 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 +972 -23
- 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/agent-logos/ChatGPT.png +0 -0
- package/dist/ui/agent-logos/Gemini CLI.jpeg +0 -0
- package/dist/ui/agent-logos/antigravity.jpeg +0 -0
- package/dist/ui/agent-logos/claude-code.jpeg +0 -0
- package/dist/ui/agent-logos/codex.jpeg +0 -0
- package/dist/ui/agent-logos/cursor.png +0 -0
- package/dist/ui/agent-logos/openclaw.jpeg +0 -0
- package/dist/ui/agent-logos/windsurf.png +0 -0
- package/dist/ui/assets/AgentsModule-BS4Pi_nC.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CCckzWYZ.js +3 -0
- package/dist/ui/assets/ContextAttachmentManager-CfG_ER7C.js +3 -0
- package/dist/ui/assets/DocumentWorkspace-BVaWPA25.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
- package/dist/ui/assets/EntityActivityTimeline-DSj0ThaK.js +1 -0
- package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
- package/dist/ui/assets/{InitiativesModule-9E6ParrY.js → InitiativesModule-AxMkWtxH.js} +1 -1
- package/dist/ui/assets/PlansPage-B_z-D6Nh.css +1 -0
- package/dist/ui/assets/PlansPage-Ndt7mYfo.js +1 -0
- package/dist/ui/assets/TaskContextUpload-Dw4rTF4i.js +1 -0
- package/dist/ui/assets/TaskSettings-f2ga42_V.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BA7jcEpH.js → WorkflowsModule-E4UL3mov.js} +4 -4
- package/dist/ui/assets/documentReferences-BOUcJm6-.js +1 -0
- package/dist/ui/assets/index-BvAbqx5Q.css +1 -0
- package/dist/ui/assets/index-MAHKvFqp.js +4 -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/og-image.png +0 -0
- 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
|
@@ -2,6 +2,7 @@ import type { DatabaseAdapter } from '../storage/databaseAdapter.js';
|
|
|
2
2
|
export type AttachmentLinkInput = string | {
|
|
3
3
|
path: string;
|
|
4
4
|
fsPath?: string;
|
|
5
|
+
assetId?: string | null;
|
|
5
6
|
caption?: string;
|
|
6
7
|
displayName?: string;
|
|
7
8
|
originalFilename?: string;
|
|
@@ -12,4 +13,6 @@ export interface AttachmentLinkServiceDeps {
|
|
|
12
13
|
tenantId: string;
|
|
13
14
|
normalizeWorkspaceId: (workspaceId: string) => string;
|
|
14
15
|
}
|
|
16
|
+
export type AttachmentLinkTargetType = 'task' | 'initiative' | 'workstream';
|
|
17
|
+
export declare function syncCanonicalAttachmentLinksForTargetService(deps: AttachmentLinkServiceDeps, targetType: AttachmentLinkTargetType, targetId: string, workspaceId: string, attachments: AttachmentLinkInput[]): void;
|
|
15
18
|
export declare function syncCanonicalTaskAttachmentLinksService(deps: AttachmentLinkServiceDeps, taskId: string, workspaceId: string, attachments: AttachmentLinkInput[]): void;
|
|
@@ -63,6 +63,25 @@ function normalizeAttachmentStorageKey(pathValue, fsPathValue) {
|
|
|
63
63
|
}
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
|
+
function extractAttachmentAssetId(attachment) {
|
|
67
|
+
const explicitAssetId = typeof attachment === 'string'
|
|
68
|
+
? ''
|
|
69
|
+
: String(attachment.assetId || '').trim();
|
|
70
|
+
if (explicitAssetId)
|
|
71
|
+
return explicitAssetId;
|
|
72
|
+
const pathValue = typeof attachment === 'string'
|
|
73
|
+
? attachment.trim()
|
|
74
|
+
: String(attachment.path || '').trim();
|
|
75
|
+
const match = pathValue.match(/^\/api\/taskforce\/documents\/([^/?#]+)\/content(?:[?#].*)?$/);
|
|
76
|
+
if (!match)
|
|
77
|
+
return null;
|
|
78
|
+
try {
|
|
79
|
+
return decodeURIComponent(match[1]).trim() || null;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return match[1].trim() || null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
66
85
|
function ensureExternalReferenceAsset(deps, storageKeyRaw, workspaceId, attachment) {
|
|
67
86
|
const storageKey = String(storageKeyRaw || '').trim();
|
|
68
87
|
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
@@ -124,25 +143,31 @@ function ensureExternalReferenceAsset(deps, storageKeyRaw, workspaceId, attachme
|
|
|
124
143
|
`).run(assetId, deps.tenantId, normalizedWorkspaceId, 'file', logicalName, logicalName, mimeType, 'external', storageKey, urlHash, Buffer.byteLength(storageKey, 'utf8'), null, null, 1, 'clean', null, null, null, timestamp, timestamp);
|
|
125
144
|
return assetId;
|
|
126
145
|
}
|
|
127
|
-
export function
|
|
128
|
-
const
|
|
146
|
+
export function syncCanonicalAttachmentLinksForTargetService(deps, targetType, targetId, workspaceId, attachments) {
|
|
147
|
+
const normalizedTargetId = String(targetId || '').trim();
|
|
129
148
|
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
130
|
-
if (!
|
|
149
|
+
if (!normalizedTargetId)
|
|
131
150
|
return;
|
|
132
151
|
const supportsTargetType = supportsWorkspaceAssetLinkTargetType(deps.db);
|
|
152
|
+
const safeTargetType = targetType === 'initiative' || targetType === 'workstream'
|
|
153
|
+
? targetType
|
|
154
|
+
: 'task';
|
|
155
|
+
const nowIso = new Date().toISOString();
|
|
133
156
|
if (supportsTargetType) {
|
|
134
157
|
deps.db.prepare(`
|
|
135
158
|
UPDATE workspace_asset_links
|
|
136
159
|
SET deleted_at = ?, updated_at = ?
|
|
137
|
-
WHERE tenant_id = ? AND workspace_id = ? AND target_type =
|
|
138
|
-
`).run(
|
|
160
|
+
WHERE tenant_id = ? AND workspace_id = ? AND target_type = ? AND task_id = ? AND deleted_at IS NULL
|
|
161
|
+
`).run(nowIso, nowIso, deps.tenantId, normalizedWorkspaceId, safeTargetType, normalizedTargetId);
|
|
139
162
|
}
|
|
140
163
|
else {
|
|
164
|
+
if (safeTargetType !== 'task')
|
|
165
|
+
return;
|
|
141
166
|
deps.db.prepare(`
|
|
142
167
|
UPDATE workspace_asset_links
|
|
143
168
|
SET deleted_at = ?, updated_at = ?
|
|
144
169
|
WHERE tenant_id = ? AND workspace_id = ? AND task_id = ? AND deleted_at IS NULL
|
|
145
|
-
`).run(
|
|
170
|
+
`).run(nowIso, nowIso, deps.tenantId, normalizedWorkspaceId, normalizedTargetId);
|
|
146
171
|
}
|
|
147
172
|
const selectAsset = deps.db.prepare(`
|
|
148
173
|
SELECT asset_id, kind
|
|
@@ -150,11 +175,17 @@ export function syncCanonicalTaskAttachmentLinksService(deps, taskId, workspaceI
|
|
|
150
175
|
WHERE tenant_id = ? AND workspace_id = ? AND storage_key = ? AND deleted_at IS NULL
|
|
151
176
|
LIMIT 1
|
|
152
177
|
`);
|
|
178
|
+
const selectAssetById = deps.db.prepare(`
|
|
179
|
+
SELECT asset_id, kind
|
|
180
|
+
FROM workspace_assets
|
|
181
|
+
WHERE tenant_id = ? AND workspace_id = ? AND asset_id = ? AND deleted_at IS NULL
|
|
182
|
+
LIMIT 1
|
|
183
|
+
`);
|
|
153
184
|
const upsertLink = supportsTargetType
|
|
154
185
|
? deps.db.prepare(`
|
|
155
186
|
INSERT INTO workspace_asset_links (
|
|
156
187
|
tenant_id, workspace_id, asset_id, target_type, task_id, role, display_name, sort_order, created_at, updated_at, deleted_at
|
|
157
|
-
) VALUES (?, ?, ?,
|
|
188
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)
|
|
158
189
|
ON CONFLICT (tenant_id, workspace_id, asset_id, task_id, role) DO UPDATE SET
|
|
159
190
|
target_type = excluded.target_type,
|
|
160
191
|
display_name = excluded.display_name,
|
|
@@ -174,16 +205,27 @@ export function syncCanonicalTaskAttachmentLinksService(deps, taskId, workspaceI
|
|
|
174
205
|
`);
|
|
175
206
|
attachments.forEach((attachment, index) => {
|
|
176
207
|
const storageKey = normalizeAttachmentStorageKey(typeof attachment === 'string' ? attachment : attachment.path, typeof attachment === 'string' ? null : attachment.fsPath);
|
|
177
|
-
|
|
208
|
+
const attachmentAssetId = extractAttachmentAssetId(attachment);
|
|
209
|
+
if (!storageKey && !attachmentAssetId)
|
|
178
210
|
return;
|
|
179
|
-
const row =
|
|
211
|
+
const row = (storageKey
|
|
212
|
+
? selectAsset.get(deps.tenantId, normalizedWorkspaceId, storageKey)
|
|
213
|
+
: null)
|
|
214
|
+
|| (attachmentAssetId
|
|
215
|
+
? selectAssetById.get(deps.tenantId, normalizedWorkspaceId, attachmentAssetId)
|
|
216
|
+
: null);
|
|
217
|
+
const isExternalStorageKey = typeof storageKey === 'string' && /^https?:\/\//i.test(storageKey);
|
|
218
|
+
const hasExplicitFileStorageKey = typeof storageKey === 'string'
|
|
219
|
+
&& typeof attachment !== 'string'
|
|
220
|
+
&& typeof attachment.fsPath === 'string'
|
|
221
|
+
&& attachment.fsPath.trim().length > 0;
|
|
180
222
|
const assetId = String(row?.asset_id || '').trim()
|
|
181
|
-
|| (
|
|
223
|
+
|| (isExternalStorageKey || hasExplicitFileStorageKey
|
|
182
224
|
? ensureExternalReferenceAsset(deps, storageKey, normalizedWorkspaceId, attachment)
|
|
183
225
|
: '');
|
|
184
226
|
if (!assetId)
|
|
185
227
|
return;
|
|
186
|
-
const role =
|
|
228
|
+
const role = isExternalStorageKey
|
|
187
229
|
? 'reference'
|
|
188
230
|
: String(row?.kind || '').trim() === 'image'
|
|
189
231
|
? 'image'
|
|
@@ -192,8 +234,11 @@ export function syncCanonicalTaskAttachmentLinksService(deps, taskId, workspaceI
|
|
|
192
234
|
? null
|
|
193
235
|
: (typeof attachment === 'string'
|
|
194
236
|
? null
|
|
195
|
-
: buildAttachmentDisplayName(attachment,
|
|
237
|
+
: buildAttachmentDisplayName(attachment, isExternalStorageKey || !storageKey ? null : path.basename(storageKey)));
|
|
196
238
|
const timestamp = typeof attachment === 'string' ? new Date().toISOString() : (attachment.timestamp || new Date().toISOString());
|
|
197
|
-
upsertLink.run(deps.tenantId, normalizedWorkspaceId, assetId,
|
|
239
|
+
upsertLink.run(deps.tenantId, normalizedWorkspaceId, assetId, safeTargetType, normalizedTargetId, role, displayName, index, timestamp, timestamp);
|
|
198
240
|
});
|
|
199
241
|
}
|
|
242
|
+
export function syncCanonicalTaskAttachmentLinksService(deps, taskId, workspaceId, attachments) {
|
|
243
|
+
syncCanonicalAttachmentLinksForTargetService(deps, 'task', taskId, workspaceId, attachments);
|
|
244
|
+
}
|
|
@@ -194,6 +194,21 @@ export function getGlobalSettingsService(deps) {
|
|
|
194
194
|
: {}),
|
|
195
195
|
...(typeof global.site.pricingPageEnabled === 'boolean'
|
|
196
196
|
? { pricingPageEnabled: global.site.pricingPageEnabled }
|
|
197
|
+
: {}),
|
|
198
|
+
...(typeof global.site.animatedBackgroundEnabled === 'boolean'
|
|
199
|
+
? { animatedBackgroundEnabled: global.site.animatedBackgroundEnabled }
|
|
200
|
+
: {}),
|
|
201
|
+
...(global.site.siteTheme === 'light' || global.site.siteTheme === 'dark'
|
|
202
|
+
? { siteTheme: global.site.siteTheme }
|
|
203
|
+
: {}),
|
|
204
|
+
...(typeof global.site.pricingPageTitle === 'string'
|
|
205
|
+
? { pricingPageTitle: global.site.pricingPageTitle.trim() }
|
|
206
|
+
: {}),
|
|
207
|
+
...(typeof global.site.pricingPageDescription === 'string'
|
|
208
|
+
? { pricingPageDescription: global.site.pricingPageDescription.trim() }
|
|
209
|
+
: {}),
|
|
210
|
+
...(typeof global.site.pricingPageShowPublicDescriptions === 'boolean'
|
|
211
|
+
? { pricingPageShowPublicDescriptions: global.site.pricingPageShowPublicDescriptions }
|
|
197
212
|
: {})
|
|
198
213
|
}
|
|
199
214
|
}
|
|
@@ -407,6 +422,21 @@ export function updateGlobalSettingsService(deps, settings) {
|
|
|
407
422
|
: {}),
|
|
408
423
|
...(typeof incoming.pricingPageEnabled === 'boolean'
|
|
409
424
|
? { pricingPageEnabled: incoming.pricingPageEnabled }
|
|
425
|
+
: {}),
|
|
426
|
+
...(typeof incoming.animatedBackgroundEnabled === 'boolean'
|
|
427
|
+
? { animatedBackgroundEnabled: incoming.animatedBackgroundEnabled }
|
|
428
|
+
: {}),
|
|
429
|
+
...(incoming.siteTheme === 'light' || incoming.siteTheme === 'dark'
|
|
430
|
+
? { siteTheme: incoming.siteTheme }
|
|
431
|
+
: {}),
|
|
432
|
+
...(typeof incoming.pricingPageTitle === 'string'
|
|
433
|
+
? { pricingPageTitle: incoming.pricingPageTitle.trim() }
|
|
434
|
+
: {}),
|
|
435
|
+
...(typeof incoming.pricingPageDescription === 'string'
|
|
436
|
+
? { pricingPageDescription: incoming.pricingPageDescription.trim() }
|
|
437
|
+
: {}),
|
|
438
|
+
...(typeof incoming.pricingPageShowPublicDescriptions === 'boolean'
|
|
439
|
+
? { pricingPageShowPublicDescriptions: incoming.pricingPageShowPublicDescriptions }
|
|
410
440
|
: {})
|
|
411
441
|
};
|
|
412
442
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { TaskforceCore } from './Taskforce';
|
|
6
|
+
const roots = [];
|
|
7
|
+
function makeProjectRoot() {
|
|
8
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'taskforce-global-settings-'));
|
|
9
|
+
roots.push(root);
|
|
10
|
+
return root;
|
|
11
|
+
}
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
while (roots.length > 0) {
|
|
14
|
+
const root = roots.pop();
|
|
15
|
+
if (root)
|
|
16
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
describe('TaskforceCore global site settings', () => {
|
|
20
|
+
it('persists pricing page title and description', () => {
|
|
21
|
+
const projectRoot = makeProjectRoot();
|
|
22
|
+
const storagePath = '.taskforce';
|
|
23
|
+
const core = new TaskforceCore({ projectRoot, storagePath });
|
|
24
|
+
core.updateGlobalSettings({
|
|
25
|
+
site: {
|
|
26
|
+
pricingPageTitle: 'Choose Your Plan',
|
|
27
|
+
pricingPageDescription: 'Compare plans and pick the right fit.',
|
|
28
|
+
pricingPageShowPublicDescriptions: false
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
expect(core.getGlobalSettings().site).toMatchObject({
|
|
32
|
+
pricingPageTitle: 'Choose Your Plan',
|
|
33
|
+
pricingPageDescription: 'Compare plans and pick the right fit.',
|
|
34
|
+
pricingPageShowPublicDescriptions: false
|
|
35
|
+
});
|
|
36
|
+
core.close();
|
|
37
|
+
const reopened = new TaskforceCore({ projectRoot, storagePath });
|
|
38
|
+
expect(reopened.getGlobalSettings().site).toMatchObject({
|
|
39
|
+
pricingPageTitle: 'Choose Your Plan',
|
|
40
|
+
pricingPageDescription: 'Compare plans and pick the right fit.',
|
|
41
|
+
pricingPageShowPublicDescriptions: false
|
|
42
|
+
});
|
|
43
|
+
reopened.close();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -60,6 +60,11 @@ export declare class McpTokenService {
|
|
|
60
60
|
workspaceId: string;
|
|
61
61
|
tokenId: string;
|
|
62
62
|
}): McpAccessTokenRecord;
|
|
63
|
+
deleteMcpAccessToken(input: {
|
|
64
|
+
actorUserId: string;
|
|
65
|
+
workspaceId: string;
|
|
66
|
+
tokenId: string;
|
|
67
|
+
}): boolean;
|
|
63
68
|
regenerateMcpAccessToken(input: {
|
|
64
69
|
actorUserId: string;
|
|
65
70
|
workspaceId: string;
|
|
@@ -111,6 +116,13 @@ export declare class McpTokenService {
|
|
|
111
116
|
grantId: string;
|
|
112
117
|
}): McpOAuthConnectorGrantRecord;
|
|
113
118
|
authenticateMcpOAuthAccessToken(token: string, workspaceId?: string): McpOAuthAccessTokenAuthResult | null;
|
|
119
|
+
bindMcpOAuthConnectorGrantAiProfile(input: {
|
|
120
|
+
grantId: string;
|
|
121
|
+
workspaceId: string;
|
|
122
|
+
profileId: string;
|
|
123
|
+
profileToken: string;
|
|
124
|
+
profileName: string;
|
|
125
|
+
}): boolean;
|
|
114
126
|
recordMcpOAuthConnectorGrantUsage(input: {
|
|
115
127
|
grantId: string;
|
|
116
128
|
workspaceId: string;
|
|
@@ -518,6 +518,23 @@ export class McpTokenService {
|
|
|
518
518
|
}
|
|
519
519
|
return this.rowToMcpAccessTokenRecord(updated);
|
|
520
520
|
}
|
|
521
|
+
deleteMcpAccessToken(input) {
|
|
522
|
+
this.getActiveWorkspaceMemberForMcp(input.actorUserId, input.workspaceId);
|
|
523
|
+
const row = this.getMcpAccessTokenRow(input.tokenId, input.workspaceId);
|
|
524
|
+
if (!row) {
|
|
525
|
+
throw new TaskforceRuleError('MCP token not found', 404, 'MCP_TOKEN_NOT_FOUND');
|
|
526
|
+
}
|
|
527
|
+
this.assertCanManageOwnMcpAccessToken(input.actorUserId, row);
|
|
528
|
+
const status = this.resolveMcpAccessTokenStatus(row);
|
|
529
|
+
if (status === 'active') {
|
|
530
|
+
throw new TaskforceRuleError('Active MCP tokens must be revoked before deletion', 400, 'MCP_TOKEN_DELETE_ACTIVE');
|
|
531
|
+
}
|
|
532
|
+
const result = this.db.prepare(`
|
|
533
|
+
DELETE FROM mcp_access_tokens
|
|
534
|
+
WHERE tenant_id = ? AND workspace_id = ? AND id = ?
|
|
535
|
+
`).run(this.tenantId, input.workspaceId, row.id);
|
|
536
|
+
return Number(result.changes || 0) > 0;
|
|
537
|
+
}
|
|
521
538
|
regenerateMcpAccessToken(input) {
|
|
522
539
|
this.getActiveWorkspaceMemberForMcp(input.actorUserId, input.workspaceId);
|
|
523
540
|
const existing = this.getMcpAccessTokenRow(input.tokenId, input.workspaceId);
|
|
@@ -872,7 +889,8 @@ export class McpTokenService {
|
|
|
872
889
|
if (!parsed)
|
|
873
890
|
return null;
|
|
874
891
|
const row = this.db.prepare(`
|
|
875
|
-
SELECT id, provider, client_id, user_id, workspace_id, scopes_json, token_hash, status, revoked_at
|
|
892
|
+
SELECT id, provider, client_id, user_id, workspace_id, scopes_json, token_hash, status, revoked_at,
|
|
893
|
+
ai_profile_id, ai_profile_token, ai_profile_name
|
|
876
894
|
FROM mcp_oauth_connector_grants
|
|
877
895
|
WHERE tenant_id = ?
|
|
878
896
|
AND token_prefix = ?
|
|
@@ -902,8 +920,33 @@ export class McpTokenService {
|
|
|
902
920
|
provider: this.normalizeMcpOAuthProvider(row.provider),
|
|
903
921
|
scopes,
|
|
904
922
|
status: this.resolveMcpOAuthGrantStatus(row),
|
|
923
|
+
aiProfileId: row?.ai_profile_id ? String(row.ai_profile_id) : null,
|
|
924
|
+
aiProfileToken: row?.ai_profile_token ? String(row.ai_profile_token) : null,
|
|
925
|
+
aiProfileName: row?.ai_profile_name ? String(row.ai_profile_name) : null,
|
|
905
926
|
};
|
|
906
927
|
}
|
|
928
|
+
bindMcpOAuthConnectorGrantAiProfile(input) {
|
|
929
|
+
const grantId = String(input.grantId || '').trim();
|
|
930
|
+
const workspaceId = String(input.workspaceId || '').trim();
|
|
931
|
+
const profileId = String(input.profileId || '').trim();
|
|
932
|
+
const profileToken = String(input.profileToken || '').trim();
|
|
933
|
+
const profileName = String(input.profileName || '').trim();
|
|
934
|
+
if (!grantId || !workspaceId || !profileId || !profileToken || !profileName)
|
|
935
|
+
return false;
|
|
936
|
+
const now = new Date().toISOString();
|
|
937
|
+
const result = this.db.prepare(`
|
|
938
|
+
UPDATE mcp_oauth_connector_grants
|
|
939
|
+
SET ai_profile_id = ?,
|
|
940
|
+
ai_profile_token = ?,
|
|
941
|
+
ai_profile_name = ?,
|
|
942
|
+
updated_at = ?
|
|
943
|
+
WHERE tenant_id = ?
|
|
944
|
+
AND workspace_id = ?
|
|
945
|
+
AND id = ?
|
|
946
|
+
AND status = 'active'
|
|
947
|
+
`).run(profileId, profileToken, profileName, now, this.tenantId, workspaceId, grantId);
|
|
948
|
+
return Number(result?.changes || 0) > 0;
|
|
949
|
+
}
|
|
907
950
|
recordMcpOAuthConnectorGrantUsage(input) {
|
|
908
951
|
const grantId = String(input.grantId || '').trim();
|
|
909
952
|
const workspaceId = String(input.workspaceId || '').trim();
|
|
@@ -20,6 +20,14 @@ export declare class PlanEntitlementService {
|
|
|
20
20
|
private normalizeOptionalCatalogMarketingCopy;
|
|
21
21
|
private canonicalizePlanFeatureKey;
|
|
22
22
|
private normalizeOptionalCatalogDisplayOrder;
|
|
23
|
+
private normalizeOptionalCatalogDescriptionVisible;
|
|
24
|
+
private isBuiltInPlanFeatureKey;
|
|
25
|
+
private normalizeRequiredCatalogLabel;
|
|
26
|
+
private slugifyCustomPlanFeatureLabel;
|
|
27
|
+
private featureKeyExists;
|
|
28
|
+
private generateUniqueCustomPlanFeatureKey;
|
|
29
|
+
private listCustomPlanFeatureCatalogEntries;
|
|
30
|
+
private getCustomPlanFeatureCatalogEntry;
|
|
23
31
|
private listPlanFeatureCatalogOverrideMap;
|
|
24
32
|
normalizeEntitlementState(value: unknown): EntitlementState;
|
|
25
33
|
private normalizeFeatureAccess;
|
|
@@ -171,10 +179,22 @@ export declare class PlanEntitlementService {
|
|
|
171
179
|
};
|
|
172
180
|
listPlanCatalog(): PlanCatalogSnapshot[];
|
|
173
181
|
listPlanFeatureCatalog(): PlanFeatureCatalogItem[];
|
|
182
|
+
createCustomPlanFeatureCatalogEntry(input: {
|
|
183
|
+
label: string;
|
|
184
|
+
description?: string | null;
|
|
185
|
+
publicLabel?: string | null;
|
|
186
|
+
publicDescription?: string | null;
|
|
187
|
+
publicDescriptionVisible?: boolean | null;
|
|
188
|
+
publicDisplayOrder?: number | null;
|
|
189
|
+
}): PlanFeatureCatalogItem;
|
|
190
|
+
deleteCustomPlanFeatureCatalogEntry(featureKeyRaw: unknown): void;
|
|
174
191
|
updatePlanFeatureCatalogMarketingCopy(input: {
|
|
175
192
|
featureKey: string;
|
|
193
|
+
label?: string | null;
|
|
194
|
+
description?: string | null;
|
|
176
195
|
publicLabel?: string | null;
|
|
177
196
|
publicDescription?: string | null;
|
|
197
|
+
publicDescriptionVisible?: boolean | null;
|
|
178
198
|
publicDisplayOrder?: number | null;
|
|
179
199
|
}): PlanFeatureCatalogItem;
|
|
180
200
|
listPlanVersions(planId: string): PlanVersionSnapshot[];
|