@taskforcehq/taskforce 0.3.301 → 0.3.302
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/dist/Taskforce.module.css +26 -2
- package/dist/TaskforceCore.filter-persistence.test.js +1 -1
- package/dist/TaskforceCore.js +13 -24
- package/dist/TaskforceCore.test.js +167 -152
- package/dist/components/features/TaskSettings.js +14 -82
- package/dist/components/features/TaskSettings.test.js +33 -70
- package/dist/components/features/settings/TaxonomyLibraryManager.js +8 -2
- package/dist/components/features/settings/TaxonomyLibraryManager.test.js +1 -1
- package/dist/components/task/TaskActionHeader.js +6 -3
- package/dist/components/task/TaskCard.js +6 -3
- package/dist/components/task/TaskForm.js +2 -2
- package/dist/components/task/TaskForm.test.js +41 -2
- package/dist/components/ui/ReferenceBadgeButton.js +1 -1
- package/dist/components/views/PlansPage.js +9 -8
- package/dist/components/views/StandaloneLayout.js +466 -204
- package/dist/components/views/panels/PlanningDrawer.js +18 -1
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +43 -0
- package/dist/components/views/standalone/modals/AccountHubModal.js +22 -0
- package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.js +8 -0
- package/dist/components/views/standalone/modals/EditProfileModal.d.ts +26 -0
- package/dist/components/views/standalone/modals/EditProfileModal.js +10 -0
- package/dist/components/views/standalone/modals/HelpModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/HelpModal.js +8 -0
- package/dist/components/views/standalone/modals/ScheduleWarningModal.d.ts +13 -0
- package/dist/components/views/standalone/modals/ScheduleWarningModal.js +6 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +61 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.js +38 -0
- package/dist/components/views/standalone/modals/TeamManagementModal.d.ts +64 -0
- package/dist/components/views/standalone/modals/TeamManagementModal.js +10 -0
- package/dist/components/views/standalone/modals/types.d.ts +2 -0
- package/dist/components/views/standalone/modals/types.js +1 -0
- package/dist/components/views/teamManagementAccess.d.ts +1 -1
- package/dist/components/views/teamManagementAccess.js +2 -2
- package/dist/components/views/teamManagementAccess.test.js +3 -3
- package/dist/config/deployment.js +2 -2
- package/dist/config/deployment.test.js +5 -3
- package/dist/core/AiProfileService.js +7 -2
- package/dist/core/AiProfiles.test.js +19 -0
- package/dist/core/SyncReconciliationService.d.ts +2 -0
- package/dist/core/SyncReconciliationService.js +3 -0
- package/dist/core/TaskAuditTimeline.test.js +40 -0
- package/dist/core/Taskforce.d.ts +49 -0
- package/dist/core/Taskforce.ids.test.js +133 -0
- package/dist/core/Taskforce.js +241 -78
- package/dist/core/UserProfileAvatarDrafts.test.d.ts +1 -0
- package/dist/core/UserProfileAvatarDrafts.test.js +102 -0
- package/dist/core/taskReferencePolicy.d.ts +28 -0
- package/dist/core/taskReferencePolicy.js +189 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/hooks/tasks/useTaskFormActions.js +3 -0
- package/dist/hooks/useTaskData.js +3 -2
- package/dist/hooks/useTaskData.test.js +66 -0
- package/dist/hooks/useTaskforce.d.ts +5 -0
- package/dist/hooks/useTaskforce.js +63 -15
- package/dist/hooks/useTaskforce.sync-behavior.test.js +185 -6
- package/dist/mcp/aiProfileBootstrap.d.ts +29 -0
- package/dist/mcp/aiProfileBootstrap.js +80 -0
- package/dist/mcp/aiProfileBootstrap.test.d.ts +1 -0
- package/dist/mcp/aiProfileBootstrap.test.js +68 -0
- package/dist/mcp/clientIntegrations.d.ts +2 -8
- package/dist/mcp/clientIntegrations.js +21 -40
- package/dist/mcp/clientIntegrations.test.js +28 -2
- package/dist/mcp/clientProfileDefaults.d.ts +12 -0
- package/dist/mcp/clientProfileDefaults.js +37 -0
- package/dist/mcp/runtime.d.ts +10 -7
- package/dist/mcp/runtime.js +41 -19
- package/dist/mcp/runtime.test.js +49 -2
- package/dist/migrations/taskSchemaMigrations.js +53 -1
- package/dist/qaOpen.test.d.ts +1 -0
- package/dist/qaOpen.test.js +56 -0
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +15 -1
- package/dist/server/index.test.js +14 -1
- package/dist/server/routes/admin.js +25 -22
- package/dist/server/routes/auth.d.ts +5 -0
- package/dist/server/routes/auth.js +350 -16
- package/dist/server/routes/billing.js +11 -1
- package/dist/server/routes/billing.test.js +32 -2
- package/dist/server/routes/documents.js +6 -1
- package/dist/server/routes/shared.d.ts +1 -1
- package/dist/server/routes/shared.js +11 -3
- package/dist/server/routes/sync.integration.test.js +130 -0
- package/dist/server/routes/sync.js +226 -1924
- package/dist/server/routes/tasks.js +18 -4
- package/dist/server/routes.js +26 -19
- package/dist/server/routes.test.js +352 -6
- package/dist/shared/runtimeContract.d.ts +1 -1
- package/dist/sync/collaborationSyncPayload.d.ts +17 -0
- package/dist/sync/collaborationSyncPayload.js +131 -0
- package/dist/sync/collaborationSyncPayload.test.d.ts +1 -0
- package/dist/sync/collaborationSyncPayload.test.js +104 -0
- package/dist/sync/collaborationSyncState.d.ts +15 -0
- package/dist/sync/collaborationSyncState.js +60 -0
- package/dist/sync/contentSyncPayload.d.ts +45 -0
- package/dist/sync/contentSyncPayload.js +104 -0
- package/dist/sync/contentSyncPayload.test.d.ts +1 -0
- package/dist/sync/contentSyncPayload.test.js +104 -0
- package/dist/sync/contentSyncState.d.ts +88 -0
- package/dist/sync/contentSyncState.js +743 -0
- package/dist/sync/planningSyncPayload.d.ts +9 -0
- package/dist/sync/planningSyncPayload.js +82 -0
- package/dist/sync/planningSyncPayload.test.d.ts +1 -0
- package/dist/sync/planningSyncPayload.test.js +68 -0
- package/dist/sync/syncApplyHandlers.d.ts +83 -0
- package/dist/sync/syncApplyHandlers.js +445 -0
- package/dist/sync/taskSyncPayload.d.ts +33 -0
- package/dist/sync/taskSyncPayload.js +126 -0
- package/dist/sync/taskSyncPayload.test.d.ts +1 -0
- package/dist/sync/taskSyncPayload.test.js +54 -0
- package/dist/sync/workspacePullFeed.d.ts +28 -0
- package/dist/sync/workspacePullFeed.js +301 -0
- package/dist/sync/workspacePullFeed.test.d.ts +1 -0
- package/dist/sync/workspacePullFeed.test.js +166 -0
- package/dist/sync/workspaceSyncModel.d.ts +4 -1
- package/dist/sync/workspaceSyncModel.js +9 -2
- package/dist/sync/workspaceSyncModel.test.js +29 -0
- package/dist/test/setup.js +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/ui/assets/{AgentsModule-Bdq3T1Ts.js → AgentsModule-JG5jc3he.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-jKUVo-PJ.js → AnnotatedAttachmentWorkspace-Dvi02Y7q.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-DuQa-uIC.js → DocumentWorkspace-iys3zfca.js} +2 -2
- package/dist/ui/assets/{InitiativesModule-C8Pi2VTk.js → InitiativesModule-DJvLJI3A.js} +1 -1
- package/dist/ui/assets/PlansPage-BMPlpFpw.js +1 -0
- package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +8 -0
- package/dist/ui/assets/{TaskSettings-BwB9NtmJ.css → TaskSettings-CnIBL_Eb.css} +1 -1
- package/dist/ui/assets/{WorkflowsModule-b6TLuHwJ.js → WorkflowsModule-BguHeQaV.js} +1 -1
- package/dist/ui/assets/index-MXeWoebC.css +1 -0
- package/dist/ui/assets/index-YJ4k5QNa.js +6 -0
- package/dist/ui/assets/{vendor-icons-QZyhEwgT.js → vendor-icons-C8EED3Oh.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/billingStatusCache.d.ts +21 -0
- package/dist/utils/billingStatusCache.js +70 -0
- package/dist/utils/taskActivity.js +17 -4
- package/dist/utils/taskActivity.test.js +27 -0
- package/dist/utils/taskNormalization.js +10 -2
- package/dist/utils/taskReferences.d.ts +11 -1
- package/dist/utils/taskReferences.js +33 -1
- package/package.json +2 -1
- package/scripts/qa-open-lib.mjs +175 -0
- package/scripts/qa-open-lib.mjs.d.ts +49 -0
- package/scripts/qa-open.mjs +352 -0
- package/dist/ui/assets/PlansPage-B6BDECSi.js +0 -1
- package/dist/ui/assets/TaskSettings-DUJgRTPU.js +0 -8
- package/dist/ui/assets/index-BiiF1cLQ.css +0 -1
- package/dist/ui/assets/index-xBWLPOEb.js +0 -6
|
@@ -87,6 +87,7 @@ const archivedTaskRecord = (id, title) => ({
|
|
|
87
87
|
describe('useTaskforce sync behavior', () => {
|
|
88
88
|
beforeEach(() => {
|
|
89
89
|
localStorage.clear();
|
|
90
|
+
sessionStorage.clear();
|
|
90
91
|
});
|
|
91
92
|
afterEach(() => {
|
|
92
93
|
vi.useRealTimers();
|
|
@@ -97,7 +98,7 @@ describe('useTaskforce sync behavior', () => {
|
|
|
97
98
|
it('merges a server-returned task into the active collection immediately', async () => {
|
|
98
99
|
vi.stubGlobal('fetch', vi.fn((url, init) => {
|
|
99
100
|
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
100
|
-
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: '
|
|
101
|
+
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: 'cloud' } });
|
|
101
102
|
}
|
|
102
103
|
if (url.includes('/api/taskforce/auth/session')) {
|
|
103
104
|
return jsonResponse({
|
|
@@ -188,7 +189,7 @@ describe('useTaskforce sync behavior', () => {
|
|
|
188
189
|
it('keeps the dawn theme when settings re-fetch configuration', async () => {
|
|
189
190
|
const fetchMock = vi.fn((url) => {
|
|
190
191
|
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
191
|
-
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: '
|
|
192
|
+
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: 'cloud' } });
|
|
192
193
|
}
|
|
193
194
|
if (url.includes('/api/taskforce/auth/session')) {
|
|
194
195
|
return jsonResponse({
|
|
@@ -261,7 +262,7 @@ describe('useTaskforce sync behavior', () => {
|
|
|
261
262
|
it('merges a server-returned archived task immediately instead of dropping it between collections', async () => {
|
|
262
263
|
vi.stubGlobal('fetch', vi.fn((url, init) => {
|
|
263
264
|
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
264
|
-
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: '
|
|
265
|
+
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: 'cloud' } });
|
|
265
266
|
}
|
|
266
267
|
if (url.includes('/api/taskforce/ui-state?key=app')) {
|
|
267
268
|
return jsonResponse({ success: true, state: {} });
|
|
@@ -339,7 +340,7 @@ describe('useTaskforce sync behavior', () => {
|
|
|
339
340
|
it('merges a server-returned completed task immediately when toggling done', async () => {
|
|
340
341
|
vi.stubGlobal('fetch', vi.fn((url, init) => {
|
|
341
342
|
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
342
|
-
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: '
|
|
343
|
+
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: 'cloud' } });
|
|
343
344
|
}
|
|
344
345
|
if (url.includes('/api/taskforce/ui-state?key=app')) {
|
|
345
346
|
return jsonResponse({ success: true, state: {} });
|
|
@@ -416,7 +417,7 @@ describe('useTaskforce sync behavior', () => {
|
|
|
416
417
|
vi.stubGlobal('confirm', vi.fn(() => true));
|
|
417
418
|
vi.stubGlobal('fetch', vi.fn((url, init) => {
|
|
418
419
|
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
419
|
-
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: '
|
|
420
|
+
return jsonResponse({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: 'cloud' } });
|
|
420
421
|
}
|
|
421
422
|
if (url.includes('/api/taskforce/ui-state?key=app')) {
|
|
422
423
|
return jsonResponse({ success: true, state: {} });
|
|
@@ -503,7 +504,7 @@ describe('useTaskforce sync behavior', () => {
|
|
|
503
504
|
config: {
|
|
504
505
|
runtimeMode: 'local',
|
|
505
506
|
workspaceMode: 'single-local',
|
|
506
|
-
authSource: '
|
|
507
|
+
authSource: 'cloud',
|
|
507
508
|
baseUrl: 'http://localhost:5174',
|
|
508
509
|
apiBaseUrl: 'http://localhost:5174',
|
|
509
510
|
cloudAuthBaseUrl: 'http://localhost:5174'
|
|
@@ -918,6 +919,184 @@ describe('useTaskforce sync behavior', () => {
|
|
|
918
919
|
expect(result.current.assigneeOptions.some((option) => option.value === 'ai-profile-codex' && option.label === 'Codex')).toBe(true);
|
|
919
920
|
});
|
|
920
921
|
});
|
|
922
|
+
it('keeps the local hydrated workspace role when workspace-members temporarily fails', async () => {
|
|
923
|
+
let workspaceMembersCalls = 0;
|
|
924
|
+
vi.stubGlobal('fetch', vi.fn((url) => {
|
|
925
|
+
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
926
|
+
return jsonResponse({
|
|
927
|
+
config: {
|
|
928
|
+
runtimeMode: 'local',
|
|
929
|
+
workspaceMode: 'single-local',
|
|
930
|
+
authSource: 'cloud',
|
|
931
|
+
workspaceSwitchingEnabled: false
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
if (url.includes('/api/taskforce/auth/session')) {
|
|
936
|
+
return jsonResponse({
|
|
937
|
+
authenticated: true,
|
|
938
|
+
authRequiredForApi: true,
|
|
939
|
+
betaAccess: true,
|
|
940
|
+
userId: 'user-1',
|
|
941
|
+
email: 'user@example.com',
|
|
942
|
+
workspaceId: 'workspace-cloud-1'
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
if (url.includes('/api/taskforce/ui-state?key=app'))
|
|
946
|
+
return jsonResponse({ success: true, state: {} });
|
|
947
|
+
if (url.includes('/api/taskforce/ui-state?key=workspace-sync')) {
|
|
948
|
+
return jsonResponse({
|
|
949
|
+
success: true,
|
|
950
|
+
state: { version: 2, enabled: false, phase: 'idle', pullCursor: null }
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
if (url.includes('/api/taskforce/config')) {
|
|
954
|
+
return jsonResponse({
|
|
955
|
+
projectName: 'Local Project',
|
|
956
|
+
workspaceId: 'workspace-cloud-1',
|
|
957
|
+
runtimeMode: 'local',
|
|
958
|
+
authRequiredForApi: true,
|
|
959
|
+
userId: 'user-1',
|
|
960
|
+
setupState: {
|
|
961
|
+
globalSetupState: 'complete',
|
|
962
|
+
workspaceSetupState: 'complete',
|
|
963
|
+
workspaceId: 'workspace-cloud-1'
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
if (url.includes('/api/taskforce/global-settings'))
|
|
968
|
+
return jsonResponse({});
|
|
969
|
+
if (url.includes('/api/taskforce/categories'))
|
|
970
|
+
return jsonResponse({ categories: [{ value: 'general', label: 'General' }] });
|
|
971
|
+
if (url.includes('/api/taskforce/types'))
|
|
972
|
+
return jsonResponse({ types: [] });
|
|
973
|
+
if (url.includes('/api/taskforce/priorities'))
|
|
974
|
+
return jsonResponse({ priorities: [] });
|
|
975
|
+
if (url.includes('/api/taskforce/approaches'))
|
|
976
|
+
return jsonResponse({ approaches: [] });
|
|
977
|
+
if (url.includes('/api/taskforce/specialists'))
|
|
978
|
+
return jsonResponse({ specialists: [] });
|
|
979
|
+
if (url.includes('/api/taskforce/tasks'))
|
|
980
|
+
return jsonResponse({ tasks: [activeTask] });
|
|
981
|
+
if (url.includes('/api/taskforce/archive'))
|
|
982
|
+
return jsonResponse({ archived: [] });
|
|
983
|
+
if (url.includes('/api/taskforce/workspace/assignee-options')) {
|
|
984
|
+
return jsonResponse({
|
|
985
|
+
assignees: [
|
|
986
|
+
{ value: 'member-1', label: 'Member One', icon: 'User', color: '#22c55e', kind: 'member', userId: 'member-1', email: 'member1@example.com' }
|
|
987
|
+
]
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
if (url.includes('/api/taskforce/auth/workspace-members')) {
|
|
991
|
+
workspaceMembersCalls += 1;
|
|
992
|
+
if (workspaceMembersCalls === 1) {
|
|
993
|
+
return jsonResponse({
|
|
994
|
+
users: [
|
|
995
|
+
{
|
|
996
|
+
userId: 'user-1',
|
|
997
|
+
email: 'user@example.com',
|
|
998
|
+
displayName: 'Signed In Owner',
|
|
999
|
+
role: 'owner',
|
|
1000
|
+
status: 'active',
|
|
1001
|
+
disabled: false
|
|
1002
|
+
}
|
|
1003
|
+
]
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
return Promise.reject(new Error('temporary workspace-members failure'));
|
|
1007
|
+
}
|
|
1008
|
+
if (url.includes('/api/taskforce/data-version'))
|
|
1009
|
+
return jsonResponse({ dataVersion: 1 });
|
|
1010
|
+
return jsonResponse({});
|
|
1011
|
+
}));
|
|
1012
|
+
const { result } = renderHook(() => useTaskforce({ config: { cloudAuthBaseUrl: 'https://cloud.example.com' } }), { wrapper });
|
|
1013
|
+
await waitFor(() => {
|
|
1014
|
+
expect(result.current.currentWorkspaceRole).toBe('owner');
|
|
1015
|
+
});
|
|
1016
|
+
await act(async () => {
|
|
1017
|
+
await result.current.fetchAssigneeOptions();
|
|
1018
|
+
});
|
|
1019
|
+
expect(result.current.currentWorkspaceRole).toBe('owner');
|
|
1020
|
+
});
|
|
1021
|
+
it('seeds local auth state from the persisted loopback actor while localhost session bootstrap is in flight', async () => {
|
|
1022
|
+
sessionStorage.setItem('taskforce.localMutationActorUserId.v1', 'user-1');
|
|
1023
|
+
const fetchMock = vi.fn((url) => {
|
|
1024
|
+
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
1025
|
+
return jsonResponse({
|
|
1026
|
+
config: {
|
|
1027
|
+
runtimeMode: 'local',
|
|
1028
|
+
workspaceMode: 'single-local',
|
|
1029
|
+
authSource: 'cloud',
|
|
1030
|
+
workspaceSwitchingEnabled: false,
|
|
1031
|
+
cloudAuthBaseUrl: 'https://app.taskforcehq.ai',
|
|
1032
|
+
cloudAuthViaLocalProxy: true
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
if (url.includes('/api/taskforce/auth/session')) {
|
|
1037
|
+
return jsonResponse({
|
|
1038
|
+
authenticated: true,
|
|
1039
|
+
authRequiredForApi: true,
|
|
1040
|
+
betaAccess: true,
|
|
1041
|
+
userId: 'user-1',
|
|
1042
|
+
email: 'user@example.com',
|
|
1043
|
+
workspaceId: 'workspace-cloud-1'
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
if (url.includes('/api/taskforce/ui-state?key=app'))
|
|
1047
|
+
return jsonResponse({ success: true, state: {} });
|
|
1048
|
+
if (url.includes('/api/taskforce/ui-state?key=workspace-sync')) {
|
|
1049
|
+
return jsonResponse({
|
|
1050
|
+
success: true,
|
|
1051
|
+
state: { version: 2, enabled: false, phase: 'idle', pullCursor: null }
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
if (url.includes('/api/taskforce/config')) {
|
|
1055
|
+
return jsonResponse({
|
|
1056
|
+
projectName: 'Local Project',
|
|
1057
|
+
workspaceId: 'workspace-cloud-1',
|
|
1058
|
+
runtimeMode: 'local',
|
|
1059
|
+
setupState: {
|
|
1060
|
+
globalSetupState: 'complete',
|
|
1061
|
+
workspaceSetupState: 'complete',
|
|
1062
|
+
workspaceId: 'workspace-cloud-1'
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
if (url.includes('/api/taskforce/global-settings'))
|
|
1067
|
+
return jsonResponse({});
|
|
1068
|
+
if (url.includes('/api/taskforce/categories'))
|
|
1069
|
+
return jsonResponse({ categories: [{ value: 'general', label: 'General' }] });
|
|
1070
|
+
if (url.includes('/api/taskforce/types'))
|
|
1071
|
+
return jsonResponse({ types: [] });
|
|
1072
|
+
if (url.includes('/api/taskforce/priorities'))
|
|
1073
|
+
return jsonResponse({ priorities: [] });
|
|
1074
|
+
if (url.includes('/api/taskforce/approaches'))
|
|
1075
|
+
return jsonResponse({ approaches: [] });
|
|
1076
|
+
if (url.includes('/api/taskforce/specialists'))
|
|
1077
|
+
return jsonResponse({ specialists: [] });
|
|
1078
|
+
if (url.includes('/api/taskforce/tasks'))
|
|
1079
|
+
return jsonResponse({ tasks: [activeTask] });
|
|
1080
|
+
if (url.includes('/api/taskforce/archive'))
|
|
1081
|
+
return jsonResponse({ archived: [] });
|
|
1082
|
+
if (url.includes('/api/taskforce/workspace/assignee-options'))
|
|
1083
|
+
return jsonResponse({ assignees: [] });
|
|
1084
|
+
if (url.includes('/api/taskforce/data-version'))
|
|
1085
|
+
return jsonResponse({ dataVersion: 1 });
|
|
1086
|
+
return jsonResponse({});
|
|
1087
|
+
});
|
|
1088
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
1089
|
+
const { result } = renderHook(() => useTaskforce({ config: { cloudAuthBaseUrl: 'https://app.taskforcehq.ai' } }), { wrapper });
|
|
1090
|
+
expect(result.current.isAuthenticated).toBe(true);
|
|
1091
|
+
expect(result.current.authSessionResolved).toBe(true);
|
|
1092
|
+
await waitFor(() => {
|
|
1093
|
+
expect(fetchMock.mock.calls.some(([url]) => String(url).includes('/api/taskforce/auth/session'))).toBe(true);
|
|
1094
|
+
});
|
|
1095
|
+
await waitFor(() => {
|
|
1096
|
+
expect(result.current.isAuthenticated).toBe(true);
|
|
1097
|
+
expect(result.current.authSessionResolved).toBe(true);
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
921
1100
|
it('enables cloud realtime refresh for authenticated sessions and refreshes open task comments from signals', async () => {
|
|
922
1101
|
vi.stubGlobal('WebSocket', MockWebSocket);
|
|
923
1102
|
let invalidationTaskFetchCount = 0;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'http';
|
|
2
|
+
import type { ResolveAiProfileInput } from '../core/Taskforce.js';
|
|
3
|
+
import { type AiProfileSurfaceType } from '../shared/aiProfileSurfaceType.js';
|
|
4
|
+
export interface AiProfileBootstrapConfig {
|
|
5
|
+
aiProfileToken?: string | null;
|
|
6
|
+
aiProfileName?: string | null;
|
|
7
|
+
aiProfileIcon?: string | null;
|
|
8
|
+
aiProfileColor?: string | null;
|
|
9
|
+
aiProfileDescription?: string | null;
|
|
10
|
+
aiProfileRole?: string | null;
|
|
11
|
+
aiProfileProvider?: string | null;
|
|
12
|
+
aiProfileModel?: string | null;
|
|
13
|
+
aiProfileSurfaceType?: AiProfileSurfaceType | null;
|
|
14
|
+
}
|
|
15
|
+
export declare const AI_PROFILE_BOOTSTRAP_HEADER_NAMES: {
|
|
16
|
+
readonly aiProfileToken: "X-Taskforce-AI-Profile-Token";
|
|
17
|
+
readonly aiProfileName: "X-Taskforce-AI-Name";
|
|
18
|
+
readonly aiProfileIcon: "X-Taskforce-AI-Icon";
|
|
19
|
+
readonly aiProfileColor: "X-Taskforce-AI-Color";
|
|
20
|
+
readonly aiProfileDescription: "X-Taskforce-AI-Description";
|
|
21
|
+
readonly aiProfileRole: "X-Taskforce-AI-Role";
|
|
22
|
+
readonly aiProfileProvider: "X-Taskforce-AI-Provider";
|
|
23
|
+
readonly aiProfileModel: "X-Taskforce-AI-Model";
|
|
24
|
+
readonly aiProfileSurfaceType: "X-Taskforce-AI-Surface-Type";
|
|
25
|
+
};
|
|
26
|
+
export declare function normalizeAiProfileBootstrapConfig(input: AiProfileBootstrapConfig): AiProfileBootstrapConfig;
|
|
27
|
+
export declare function buildAiProfileBootstrapHeaders(input: AiProfileBootstrapConfig): Record<string, string>;
|
|
28
|
+
export declare function parseAiProfileBootstrapHeaders(headers: IncomingHttpHeaders | Record<string, string | string[] | undefined>): Required<AiProfileBootstrapConfig>;
|
|
29
|
+
export declare function buildResolveAiProfileInputFromBootstrap(workspaceId: string, bootstrap: AiProfileBootstrapConfig, includeToken?: boolean): Pick<ResolveAiProfileInput, 'workspaceId' | 'name' | 'profileToken' | 'icon' | 'color' | 'description' | 'role' | 'provider' | 'model' | 'surfaceType'> | null;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { parseAiProfileSurfaceType } from '../shared/aiProfileSurfaceType.js';
|
|
2
|
+
export const AI_PROFILE_BOOTSTRAP_HEADER_NAMES = {
|
|
3
|
+
aiProfileToken: 'X-Taskforce-AI-Profile-Token',
|
|
4
|
+
aiProfileName: 'X-Taskforce-AI-Name',
|
|
5
|
+
aiProfileIcon: 'X-Taskforce-AI-Icon',
|
|
6
|
+
aiProfileColor: 'X-Taskforce-AI-Color',
|
|
7
|
+
aiProfileDescription: 'X-Taskforce-AI-Description',
|
|
8
|
+
aiProfileRole: 'X-Taskforce-AI-Role',
|
|
9
|
+
aiProfileProvider: 'X-Taskforce-AI-Provider',
|
|
10
|
+
aiProfileModel: 'X-Taskforce-AI-Model',
|
|
11
|
+
aiProfileSurfaceType: 'X-Taskforce-AI-Surface-Type',
|
|
12
|
+
};
|
|
13
|
+
function normalizeBootstrapValue(value) {
|
|
14
|
+
const normalized = String(value || '').trim();
|
|
15
|
+
return normalized || null;
|
|
16
|
+
}
|
|
17
|
+
function normalizeBootstrapSurfaceType(value) {
|
|
18
|
+
if (value === null || value === undefined)
|
|
19
|
+
return null;
|
|
20
|
+
return parseAiProfileSurfaceType(value);
|
|
21
|
+
}
|
|
22
|
+
export function normalizeAiProfileBootstrapConfig(input) {
|
|
23
|
+
return {
|
|
24
|
+
aiProfileToken: normalizeBootstrapValue(input.aiProfileToken),
|
|
25
|
+
aiProfileName: normalizeBootstrapValue(input.aiProfileName),
|
|
26
|
+
aiProfileIcon: normalizeBootstrapValue(input.aiProfileIcon),
|
|
27
|
+
aiProfileColor: normalizeBootstrapValue(input.aiProfileColor),
|
|
28
|
+
aiProfileDescription: normalizeBootstrapValue(input.aiProfileDescription),
|
|
29
|
+
aiProfileRole: normalizeBootstrapValue(input.aiProfileRole),
|
|
30
|
+
aiProfileProvider: normalizeBootstrapValue(input.aiProfileProvider),
|
|
31
|
+
aiProfileModel: normalizeBootstrapValue(input.aiProfileModel),
|
|
32
|
+
aiProfileSurfaceType: normalizeBootstrapSurfaceType(input.aiProfileSurfaceType),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function buildAiProfileBootstrapHeaders(input) {
|
|
36
|
+
const normalized = normalizeAiProfileBootstrapConfig(input);
|
|
37
|
+
const headers = {};
|
|
38
|
+
for (const [key, headerName] of Object.entries(AI_PROFILE_BOOTSTRAP_HEADER_NAMES)) {
|
|
39
|
+
const value = normalized[key];
|
|
40
|
+
if (value)
|
|
41
|
+
headers[headerName] = value;
|
|
42
|
+
}
|
|
43
|
+
return headers;
|
|
44
|
+
}
|
|
45
|
+
export function parseAiProfileBootstrapHeaders(headers) {
|
|
46
|
+
const readHeader = (headerName) => {
|
|
47
|
+
const value = headers[headerName] ?? headers[headerName.toLowerCase()];
|
|
48
|
+
if (Array.isArray(value))
|
|
49
|
+
return normalizeBootstrapValue(value[0]);
|
|
50
|
+
return normalizeBootstrapValue(value);
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
aiProfileToken: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileToken),
|
|
54
|
+
aiProfileName: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileName),
|
|
55
|
+
aiProfileIcon: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileIcon),
|
|
56
|
+
aiProfileColor: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileColor),
|
|
57
|
+
aiProfileDescription: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileDescription),
|
|
58
|
+
aiProfileRole: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileRole),
|
|
59
|
+
aiProfileProvider: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileProvider),
|
|
60
|
+
aiProfileModel: readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileModel),
|
|
61
|
+
aiProfileSurfaceType: normalizeBootstrapSurfaceType(readHeader(AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileSurfaceType)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function buildResolveAiProfileInputFromBootstrap(workspaceId, bootstrap, includeToken = true) {
|
|
65
|
+
const normalized = normalizeAiProfileBootstrapConfig(bootstrap);
|
|
66
|
+
if (!normalized.aiProfileName)
|
|
67
|
+
return null;
|
|
68
|
+
return {
|
|
69
|
+
workspaceId,
|
|
70
|
+
name: normalized.aiProfileName,
|
|
71
|
+
...(includeToken && normalized.aiProfileToken ? { profileToken: normalized.aiProfileToken } : {}),
|
|
72
|
+
...(normalized.aiProfileIcon ? { icon: normalized.aiProfileIcon } : {}),
|
|
73
|
+
...(normalized.aiProfileColor ? { color: normalized.aiProfileColor } : {}),
|
|
74
|
+
...(normalized.aiProfileDescription ? { description: normalized.aiProfileDescription } : {}),
|
|
75
|
+
...(normalized.aiProfileRole ? { role: normalized.aiProfileRole } : {}),
|
|
76
|
+
...(normalized.aiProfileProvider ? { provider: normalized.aiProfileProvider } : {}),
|
|
77
|
+
...(normalized.aiProfileModel ? { model: normalized.aiProfileModel } : {}),
|
|
78
|
+
...(normalized.aiProfileSurfaceType ? { surfaceType: normalized.aiProfileSurfaceType } : {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { AI_PROFILE_BOOTSTRAP_HEADER_NAMES, buildAiProfileBootstrapHeaders, buildResolveAiProfileInputFromBootstrap, parseAiProfileBootstrapHeaders, } from './aiProfileBootstrap';
|
|
3
|
+
describe('aiProfileBootstrap', () => {
|
|
4
|
+
it('builds and parses bootstrap headers from a shared contract', () => {
|
|
5
|
+
const headers = buildAiProfileBootstrapHeaders({
|
|
6
|
+
aiProfileName: 'Automation Codex',
|
|
7
|
+
aiProfileToken: 'tfp_automation_codex',
|
|
8
|
+
aiProfileIcon: 'Code2',
|
|
9
|
+
aiProfileColor: '#10a37f',
|
|
10
|
+
aiProfileDescription: 'OpenAI Codex coding agent connected through MCP.',
|
|
11
|
+
aiProfileRole: 'Coding Agent',
|
|
12
|
+
aiProfileProvider: 'OpenAI',
|
|
13
|
+
aiProfileModel: 'Codex',
|
|
14
|
+
aiProfileSurfaceType: 'coding_tool',
|
|
15
|
+
});
|
|
16
|
+
expect(headers).toEqual({
|
|
17
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileName]: 'Automation Codex',
|
|
18
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileToken]: 'tfp_automation_codex',
|
|
19
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileIcon]: 'Code2',
|
|
20
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileColor]: '#10a37f',
|
|
21
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileDescription]: 'OpenAI Codex coding agent connected through MCP.',
|
|
22
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileRole]: 'Coding Agent',
|
|
23
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileProvider]: 'OpenAI',
|
|
24
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileModel]: 'Codex',
|
|
25
|
+
[AI_PROFILE_BOOTSTRAP_HEADER_NAMES.aiProfileSurfaceType]: 'coding_tool',
|
|
26
|
+
});
|
|
27
|
+
expect(parseAiProfileBootstrapHeaders(headers)).toEqual({
|
|
28
|
+
aiProfileName: 'Automation Codex',
|
|
29
|
+
aiProfileToken: 'tfp_automation_codex',
|
|
30
|
+
aiProfileIcon: 'Code2',
|
|
31
|
+
aiProfileColor: '#10a37f',
|
|
32
|
+
aiProfileDescription: 'OpenAI Codex coding agent connected through MCP.',
|
|
33
|
+
aiProfileRole: 'Coding Agent',
|
|
34
|
+
aiProfileProvider: 'OpenAI',
|
|
35
|
+
aiProfileModel: 'Codex',
|
|
36
|
+
aiProfileSurfaceType: 'coding_tool',
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
it('builds resolve_profile input from bootstrap metadata', () => {
|
|
40
|
+
expect(buildResolveAiProfileInputFromBootstrap('workspace-1', {
|
|
41
|
+
aiProfileName: 'Automation Codex',
|
|
42
|
+
aiProfileToken: 'tfp_automation_codex',
|
|
43
|
+
aiProfileIcon: 'Code2',
|
|
44
|
+
aiProfileColor: '#10a37f',
|
|
45
|
+
aiProfileDescription: 'OpenAI Codex coding agent connected through MCP.',
|
|
46
|
+
aiProfileRole: 'Coding Agent',
|
|
47
|
+
aiProfileProvider: 'OpenAI',
|
|
48
|
+
aiProfileModel: 'Codex',
|
|
49
|
+
aiProfileSurfaceType: 'coding_tool',
|
|
50
|
+
})).toEqual({
|
|
51
|
+
workspaceId: 'workspace-1',
|
|
52
|
+
name: 'Automation Codex',
|
|
53
|
+
profileToken: 'tfp_automation_codex',
|
|
54
|
+
icon: 'Code2',
|
|
55
|
+
color: '#10a37f',
|
|
56
|
+
description: 'OpenAI Codex coding agent connected through MCP.',
|
|
57
|
+
role: 'Coding Agent',
|
|
58
|
+
provider: 'OpenAI',
|
|
59
|
+
model: 'Codex',
|
|
60
|
+
surfaceType: 'coding_tool',
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
it('returns null resolve input when no bootstrap name is present', () => {
|
|
64
|
+
expect(buildResolveAiProfileInputFromBootstrap('workspace-1', {
|
|
65
|
+
aiProfileToken: 'tfp_automation_codex',
|
|
66
|
+
})).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
+
import { type AiProfileBootstrapConfig } from './aiProfileBootstrap.js';
|
|
1
2
|
export type McpClientId = 'cursor' | 'cline' | 'vscode' | 'windsurf' | 'antigravity' | 'openclaw' | 'codex' | 'claude-code' | 'gemini-cli' | 'claude-desktop' | 'chatgpt-desktop';
|
|
2
3
|
export interface McpClientOption {
|
|
3
4
|
id: McpClientId;
|
|
4
5
|
label: string;
|
|
5
6
|
disabled?: boolean;
|
|
6
7
|
}
|
|
7
|
-
export interface BuildCloudMcpClientConfigInput {
|
|
8
|
+
export interface BuildCloudMcpClientConfigInput extends AiProfileBootstrapConfig {
|
|
8
9
|
clientId: McpClientId;
|
|
9
10
|
endpoint: string;
|
|
10
11
|
tokenValue: string;
|
|
11
12
|
serverId: string;
|
|
12
|
-
aiProfileName?: string | null;
|
|
13
|
-
aiProfileToken?: string | null;
|
|
14
|
-
aiProfileDescription?: string | null;
|
|
15
|
-
aiProfileRole?: string | null;
|
|
16
|
-
aiProfileProvider?: string | null;
|
|
17
|
-
aiProfileModel?: string | null;
|
|
18
|
-
aiProfileSurfaceType?: string | null;
|
|
19
13
|
}
|
|
20
14
|
export interface CloudMcpClientIntegration {
|
|
21
15
|
clientId: McpClientId;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildAiProfileBootstrapHeaders } from './aiProfileBootstrap.js';
|
|
1
2
|
export const MCP_CLIENT_OPTIONS = [
|
|
2
3
|
{ id: 'cursor', label: 'Cursor' },
|
|
3
4
|
{ id: 'cline', label: 'Cline' },
|
|
@@ -29,38 +30,18 @@ function buildConnectorDetails(clientId, endpoint) {
|
|
|
29
30
|
return endpoint;
|
|
30
31
|
}
|
|
31
32
|
function buildCloudMcpHeaders(input) {
|
|
32
|
-
|
|
33
|
-
Authorization: `Bearer ${input.tokenValue}
|
|
33
|
+
return {
|
|
34
|
+
Authorization: `Bearer ${input.tokenValue}`,
|
|
35
|
+
...buildAiProfileBootstrapHeaders(input),
|
|
34
36
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
headers['X-Taskforce-AI-Name'] = aiProfileName;
|
|
44
|
-
}
|
|
45
|
-
if (aiProfileToken) {
|
|
46
|
-
headers['X-Taskforce-AI-Profile-Token'] = aiProfileToken;
|
|
47
|
-
}
|
|
48
|
-
if (aiProfileDescription) {
|
|
49
|
-
headers['X-Taskforce-AI-Description'] = aiProfileDescription;
|
|
50
|
-
}
|
|
51
|
-
if (aiProfileRole) {
|
|
52
|
-
headers['X-Taskforce-AI-Role'] = aiProfileRole;
|
|
53
|
-
}
|
|
54
|
-
if (aiProfileProvider) {
|
|
55
|
-
headers['X-Taskforce-AI-Provider'] = aiProfileProvider;
|
|
56
|
-
}
|
|
57
|
-
if (aiProfileModel) {
|
|
58
|
-
headers['X-Taskforce-AI-Model'] = aiProfileModel;
|
|
59
|
-
}
|
|
60
|
-
if (aiProfileSurfaceType) {
|
|
61
|
-
headers['X-Taskforce-AI-Surface-Type'] = aiProfileSurfaceType;
|
|
62
|
-
}
|
|
63
|
-
return headers;
|
|
37
|
+
}
|
|
38
|
+
function buildAutomationBootstrapInstruction(baseInstruction) {
|
|
39
|
+
return `${baseInstruction} Keep the generated AI bootstrap headers intact so Taskforce can bind and restore the same AI profile automatically.`;
|
|
40
|
+
}
|
|
41
|
+
function buildConfigurationInstruction(baseInstruction, options) {
|
|
42
|
+
if (options?.usesGeneratedConfig === false)
|
|
43
|
+
return baseInstruction;
|
|
44
|
+
return buildAutomationBootstrapInstruction(`${baseInstruction} Use the generated config exactly as shown.`);
|
|
64
45
|
}
|
|
65
46
|
export function buildCloudMcpClientIntegration(input) {
|
|
66
47
|
const { clientId, endpoint, tokenValue, serverId } = input;
|
|
@@ -81,7 +62,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
81
62
|
headers
|
|
82
63
|
},
|
|
83
64
|
instructionLabel: 'Cursor Configuration Instructions',
|
|
84
|
-
instructionText: 'Open Cursor settings, find MCP servers, and paste this config.
|
|
65
|
+
instructionText: buildConfigurationInstruction('Open Cursor settings, find MCP servers, and paste this config.')
|
|
85
66
|
};
|
|
86
67
|
case 'cline':
|
|
87
68
|
return {
|
|
@@ -94,7 +75,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
94
75
|
headers
|
|
95
76
|
},
|
|
96
77
|
instructionLabel: 'Cline Configuration Instructions',
|
|
97
|
-
instructionText: 'Open Cline MCP Servers, add a remote server, or paste this into the Cline MCP config with Streamable HTTP selected.
|
|
78
|
+
instructionText: buildConfigurationInstruction('Open Cline MCP Servers, add a remote server, or paste this into the Cline MCP config with Streamable HTTP selected.')
|
|
98
79
|
};
|
|
99
80
|
case 'vscode':
|
|
100
81
|
return {
|
|
@@ -107,7 +88,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
107
88
|
headers
|
|
108
89
|
},
|
|
109
90
|
instructionLabel: 'VS Code Configuration Instructions',
|
|
110
|
-
instructionText: 'Open .vscode/mcp.json or MCP: Open User Configuration and add this server under the servers object.
|
|
91
|
+
instructionText: buildConfigurationInstruction('Open .vscode/mcp.json or MCP: Open User Configuration and add this server under the servers object.')
|
|
111
92
|
};
|
|
112
93
|
case 'windsurf':
|
|
113
94
|
return {
|
|
@@ -119,7 +100,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
119
100
|
headers
|
|
120
101
|
},
|
|
121
102
|
instructionLabel: 'Windsurf Configuration Instructions',
|
|
122
|
-
instructionText: 'Open Windsurf MCP settings, add a server, and paste this config.
|
|
103
|
+
instructionText: buildConfigurationInstruction('Open Windsurf MCP settings, add a server, and paste this config.')
|
|
123
104
|
};
|
|
124
105
|
case 'antigravity':
|
|
125
106
|
return {
|
|
@@ -131,7 +112,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
131
112
|
headers
|
|
132
113
|
},
|
|
133
114
|
instructionLabel: 'Antigravity Configuration Instructions',
|
|
134
|
-
instructionText: 'Open Antigravity MCP settings, view the raw config, and paste this Antigravity-specific configuration.
|
|
115
|
+
instructionText: buildConfigurationInstruction('Open Antigravity MCP settings, view the raw config, and paste this Antigravity-specific configuration.')
|
|
135
116
|
};
|
|
136
117
|
case 'openclaw':
|
|
137
118
|
return {
|
|
@@ -145,7 +126,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
145
126
|
headers
|
|
146
127
|
},
|
|
147
128
|
instructionLabel: 'OpenClaw Configuration Instructions',
|
|
148
|
-
instructionText: 'Open ~/.openclaw/openclaw.json and add this server under mcp.servers.
|
|
129
|
+
instructionText: buildConfigurationInstruction('Open ~/.openclaw/openclaw.json and add this server under mcp.servers.')
|
|
149
130
|
};
|
|
150
131
|
case 'codex':
|
|
151
132
|
return {
|
|
@@ -157,7 +138,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
157
138
|
`http_headers = { ${Object.entries(headers).map(([key, value]) => `${key} = "${value}"`).join(', ')} }`
|
|
158
139
|
].join('\n'),
|
|
159
140
|
instructionLabel: 'Codex Configuration Instructions',
|
|
160
|
-
instructionText: 'Open ~/.codex/config.toml and add this Codex MCP entry.
|
|
141
|
+
instructionText: buildConfigurationInstruction('Open ~/.codex/config.toml and add this Codex MCP entry.')
|
|
161
142
|
};
|
|
162
143
|
case 'claude-code':
|
|
163
144
|
return {
|
|
@@ -165,7 +146,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
165
146
|
kind: 'text',
|
|
166
147
|
renderedText: `claude mcp add --transport http ${serverId} ${endpoint} --header "Authorization: Bearer ${tokenValue}"${extraHeaderFlags}`,
|
|
167
148
|
instructionLabel: 'Claude Code Configuration Instructions',
|
|
168
|
-
instructionText: 'Run this Claude Code command in your terminal to add the remote Taskforce MCP server.
|
|
149
|
+
instructionText: buildConfigurationInstruction('Run this Claude Code command in your terminal to add the remote Taskforce MCP server.')
|
|
169
150
|
};
|
|
170
151
|
case 'gemini-cli':
|
|
171
152
|
return {
|
|
@@ -173,7 +154,7 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
173
154
|
kind: 'text',
|
|
174
155
|
renderedText: `gemini mcp add ${serverId} ${endpoint} --transport http --header "Authorization: Bearer ${tokenValue}"${extraHeaderFlags}`,
|
|
175
156
|
instructionLabel: 'Gemini CLI Configuration Instructions',
|
|
176
|
-
instructionText: 'Run this Gemini CLI command in your terminal to add the remote Taskforce MCP server. Add --scope user if you want it available across all projects.
|
|
157
|
+
instructionText: buildConfigurationInstruction('Run this Gemini CLI command in your terminal to add the remote Taskforce MCP server. Add --scope user if you want it available across all projects.')
|
|
177
158
|
};
|
|
178
159
|
case 'claude-desktop':
|
|
179
160
|
return {
|