@taskforcehq/taskforce 0.3.302 → 0.3.303
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 +11 -0
- package/dist/TaskforceCore.js +59 -9
- package/dist/TaskforceCore.test.js +717 -14
- package/dist/components/task/TaskKanban.test.js +17 -0
- package/dist/components/views/PlansPage.d.ts +3 -1
- package/dist/components/views/PlansPage.js +110 -51
- package/dist/components/views/PlansPage.test.d.ts +1 -0
- package/dist/components/views/PlansPage.test.js +100 -0
- package/dist/components/views/StandaloneLayout.d.ts +1 -0
- package/dist/components/views/StandaloneLayout.js +106 -151
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +8 -3
- package/dist/components/views/standalone/modals/AccountHubModal.js +16 -2
- package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
- package/dist/core/EntitlementsPolicy.test.js +2 -2
- package/dist/core/GlobalSettingsService.js +78 -18
- package/dist/core/PlanEntitlementService.d.ts +29 -5
- package/dist/core/PlanEntitlementService.js +297 -219
- package/dist/core/SignupMonetizationSettings.test.js +173 -49
- package/dist/core/SystemAdmin.test.js +186 -96
- package/dist/core/Taskforce.d.ts +51 -10
- package/dist/core/Taskforce.js +218 -49
- package/dist/core/Taskforce.mcpAuth.test.js +0 -2
- package/dist/core/UserProfileAvatarDrafts.test.js +12 -3
- package/dist/core/types.d.ts +11 -12
- package/dist/hooks/useSyncOrchestrator.js +17 -3
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +97 -5
- package/dist/hooks/useTaskforce.d.ts +3 -0
- package/dist/hooks/useTaskforce.js +9 -4
- package/dist/mcp/runtime.js +90 -29
- package/dist/mcp/runtime.test.js +272 -81
- package/dist/migrations/billingSchemaParity.test.js +3 -0
- package/dist/migrations/taskSchemaMigrations.js +15 -0
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.js +2 -0
- package/dist/server/index.test.js +2 -1
- package/dist/server/mockStripe.d.ts +1 -0
- package/dist/server/mockStripe.js +2 -0
- package/dist/server/routes/admin.js +80 -23
- package/dist/server/routes/auth.d.ts +1 -0
- package/dist/server/routes/auth.js +194 -53
- package/dist/server/routes/billing.js +506 -23
- package/dist/server/routes/billing.test.js +937 -45
- package/dist/server/routes/shared.js +5 -3
- package/dist/server/routes/sync.integration.test.js +160 -0
- package/dist/server/routes/sync.js +15 -5
- package/dist/server/routes.js +3 -3
- package/dist/server/routes.test.js +287 -76
- package/dist/sync/cloudSyncApi.d.ts +1 -0
- package/dist/sync/contentSyncState.d.ts +10 -0
- package/dist/sync/contentSyncState.js +277 -16
- package/dist/sync/syncService.d.ts +1 -0
- package/dist/sync/syncService.js +21 -8
- package/dist/sync/workspacePullFeed.d.ts +10 -0
- package/dist/sync/workspacePullFeed.js +111 -63
- package/dist/sync/workspacePullFeed.test.js +112 -0
- package/dist/ui/assets/{AgentsModule-JG5jc3he.js → AgentsModule-CpsTmrIW.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-Dvi02Y7q.js → AnnotatedAttachmentWorkspace-C_TmXZwA.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-iys3zfca.js → DocumentWorkspace-C3GyzrWm.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-DJvLJI3A.js → InitiativesModule-4-Rh2K2n.js} +1 -1
- package/dist/ui/assets/PlansPage-BP7AYOqr.js +1 -0
- package/dist/ui/assets/{TaskSettings-Bqd4cCzW.js → TaskSettings-BOC5F6Ag.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-BguHeQaV.js → WorkflowsModule-CGk9s3NJ.js} +1 -1
- package/dist/ui/assets/index-CLIMgR6g.js +6 -0
- package/dist/ui/assets/index-DneETxcu.css +1 -0
- package/dist/ui/assets/{vendor-icons-C8EED3Oh.js → vendor-icons-pWocEipT.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
- package/dist/utils/accountProfileSummaryCache.js +97 -0
- package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
- package/dist/utils/accountProfileSummaryCache.test.js +63 -0
- package/package.json +3 -3
- package/dist/ui/assets/PlansPage-BMPlpFpw.js +0 -1
- package/dist/ui/assets/index-MXeWoebC.css +0 -1
- package/dist/ui/assets/index-YJ4k5QNa.js +0 -6
- package/dist/utils/billingStatusCache.d.ts +0 -21
- package/dist/utils/billingStatusCache.js +0 -70
package/dist/mcp/runtime.js
CHANGED
|
@@ -1664,7 +1664,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1664
1664
|
console.error(`[Taskforce MCP] Failed to send resources/updated for ${uri}: ${error.message}`);
|
|
1665
1665
|
}
|
|
1666
1666
|
}
|
|
1667
|
-
|
|
1667
|
+
function collectResourceMutationUris(options) {
|
|
1668
1668
|
const uris = new Set();
|
|
1669
1669
|
if (options.includeConfig)
|
|
1670
1670
|
uris.add(RESOURCE_CONFIG_URI);
|
|
@@ -1691,10 +1691,39 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1691
1691
|
uris.add(workstreamUriFor(workstreamId));
|
|
1692
1692
|
}
|
|
1693
1693
|
}
|
|
1694
|
+
return uris;
|
|
1695
|
+
}
|
|
1696
|
+
const pendingResourceInvalidations = new Set();
|
|
1697
|
+
let resourceInvalidationFlushHandle = null;
|
|
1698
|
+
function scheduleResourceInvalidationFlush() {
|
|
1699
|
+
if (resourceInvalidationFlushHandle)
|
|
1700
|
+
return;
|
|
1701
|
+
resourceInvalidationFlushHandle = setTimeout(() => {
|
|
1702
|
+
resourceInvalidationFlushHandle = null;
|
|
1703
|
+
void flushScheduledResourceInvalidations();
|
|
1704
|
+
}, 0);
|
|
1705
|
+
}
|
|
1706
|
+
async function flushScheduledResourceInvalidations() {
|
|
1707
|
+
if (pendingResourceInvalidations.size === 0)
|
|
1708
|
+
return;
|
|
1709
|
+
const uris = Array.from(pendingResourceInvalidations);
|
|
1710
|
+
pendingResourceInvalidations.clear();
|
|
1694
1711
|
for (const uri of uris) {
|
|
1695
1712
|
await notifyResourceUpdatedIfSubscribed(uri);
|
|
1696
1713
|
}
|
|
1697
1714
|
}
|
|
1715
|
+
function scheduleResourceMutation(options) {
|
|
1716
|
+
const uris = collectResourceMutationUris(options);
|
|
1717
|
+
if (uris.size === 0)
|
|
1718
|
+
return;
|
|
1719
|
+
for (const uri of uris) {
|
|
1720
|
+
pendingResourceInvalidations.add(uri);
|
|
1721
|
+
}
|
|
1722
|
+
scheduleResourceInvalidationFlush();
|
|
1723
|
+
}
|
|
1724
|
+
async function notifyResourceMutation(options) {
|
|
1725
|
+
scheduleResourceMutation(options);
|
|
1726
|
+
}
|
|
1698
1727
|
function buildConfigResourcePayload() {
|
|
1699
1728
|
return {
|
|
1700
1729
|
project: {
|
|
@@ -1716,13 +1745,26 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1716
1745
|
};
|
|
1717
1746
|
}
|
|
1718
1747
|
function buildTasksResourcePayload() {
|
|
1719
|
-
const tasks = core.listTasks(ACTIVE_WORKSPACE_ID).tasks
|
|
1748
|
+
const tasks = core.listTasks(ACTIVE_WORKSPACE_ID).tasks
|
|
1749
|
+
.map((task) => buildPlanningTaskSummary(task));
|
|
1720
1750
|
return {
|
|
1721
1751
|
total: tasks.length,
|
|
1722
1752
|
tasks,
|
|
1723
1753
|
};
|
|
1724
1754
|
}
|
|
1725
|
-
function
|
|
1755
|
+
function buildPlanningTaskSummary(task) {
|
|
1756
|
+
const { description, comments, taskEvents, activity, createdByActor, assigneeActor, attachments, checklistItems, ...rest } = task;
|
|
1757
|
+
return rest;
|
|
1758
|
+
}
|
|
1759
|
+
function buildPlanningWorkstreamSummary(workstream) {
|
|
1760
|
+
const { description, taskIds, ...rest } = workstream;
|
|
1761
|
+
return rest;
|
|
1762
|
+
}
|
|
1763
|
+
function buildPlanningInitiativeSummary(initiative) {
|
|
1764
|
+
const { description, ...rest } = initiative;
|
|
1765
|
+
return rest;
|
|
1766
|
+
}
|
|
1767
|
+
function buildHistoricalWorkspaceTaskSnapshot() {
|
|
1726
1768
|
const activeTasks = core.listTasks(ACTIVE_WORKSPACE_ID).tasks;
|
|
1727
1769
|
const archivedTasks = core.listArchive(ACTIVE_WORKSPACE_ID).archived;
|
|
1728
1770
|
const byId = new Map();
|
|
@@ -1730,40 +1772,70 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1730
1772
|
byId.set(task.id, task);
|
|
1731
1773
|
for (const task of archivedTasks)
|
|
1732
1774
|
byId.set(task.id, task);
|
|
1733
|
-
|
|
1775
|
+
const tasks = Array.from(byId.values());
|
|
1776
|
+
const tasksByWorkstreamId = new Map();
|
|
1777
|
+
for (const task of tasks) {
|
|
1778
|
+
const normalizedWorkstreamId = String(task.workstreamId || '').trim();
|
|
1779
|
+
if (!normalizedWorkstreamId)
|
|
1780
|
+
continue;
|
|
1781
|
+
const current = tasksByWorkstreamId.get(normalizedWorkstreamId) || [];
|
|
1782
|
+
current.push(buildPlanningTaskSummary(task));
|
|
1783
|
+
tasksByWorkstreamId.set(normalizedWorkstreamId, current);
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
tasks,
|
|
1787
|
+
tasksByWorkstreamId,
|
|
1788
|
+
};
|
|
1734
1789
|
}
|
|
1735
|
-
function buildInitiativeResourcePayload(initiative) {
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1790
|
+
function buildInitiativeResourcePayload(initiative, options) {
|
|
1791
|
+
const tasksByWorkstreamId = options?.tasksByWorkstreamId || buildHistoricalWorkspaceTaskSnapshot().tasksByWorkstreamId;
|
|
1792
|
+
const workstreams = options?.workstreamsByInitiativeId?.get(initiative.id)
|
|
1793
|
+
|| core.listWorkstreams(ACTIVE_WORKSPACE_ID)
|
|
1794
|
+
.filter((workstream) => workstream.initiativeId === initiative.id);
|
|
1795
|
+
const tasks = workstreams.flatMap((workstream) => tasksByWorkstreamId.get(workstream.id) || []);
|
|
1740
1796
|
return {
|
|
1741
|
-
initiative,
|
|
1797
|
+
initiative: buildPlanningInitiativeSummary(initiative),
|
|
1742
1798
|
workstreamCount: workstreams.length,
|
|
1743
1799
|
taskCount: tasks.length,
|
|
1744
|
-
workstreams,
|
|
1800
|
+
workstreams: workstreams.map((workstream) => buildPlanningWorkstreamSummary(workstream)),
|
|
1745
1801
|
};
|
|
1746
1802
|
}
|
|
1747
|
-
function buildWorkstreamResourcePayload(workstream) {
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1803
|
+
function buildWorkstreamResourcePayload(workstream, options) {
|
|
1804
|
+
const tasksByWorkstreamId = options?.tasksByWorkstreamId || buildHistoricalWorkspaceTaskSnapshot().tasksByWorkstreamId;
|
|
1805
|
+
const tasks = tasksByWorkstreamId.get(workstream.id)
|
|
1806
|
+
|| [];
|
|
1750
1807
|
return {
|
|
1751
|
-
workstream,
|
|
1808
|
+
workstream: buildPlanningWorkstreamSummary(workstream),
|
|
1752
1809
|
taskCount: tasks.length,
|
|
1753
1810
|
tasks,
|
|
1754
1811
|
};
|
|
1755
1812
|
}
|
|
1756
1813
|
function buildInitiativesResourcePayload() {
|
|
1814
|
+
const workstreams = core.listWorkstreams(ACTIVE_WORKSPACE_ID);
|
|
1815
|
+
const workstreamsByInitiativeId = new Map();
|
|
1816
|
+
for (const workstream of workstreams) {
|
|
1817
|
+
const normalizedInitiativeId = String(workstream.initiativeId || '').trim();
|
|
1818
|
+
if (!normalizedInitiativeId)
|
|
1819
|
+
continue;
|
|
1820
|
+
const current = workstreamsByInitiativeId.get(normalizedInitiativeId) || [];
|
|
1821
|
+
current.push(workstream);
|
|
1822
|
+
workstreamsByInitiativeId.set(normalizedInitiativeId, current);
|
|
1823
|
+
}
|
|
1824
|
+
const { tasksByWorkstreamId } = buildHistoricalWorkspaceTaskSnapshot();
|
|
1757
1825
|
const initiatives = core.listInitiatives(ACTIVE_WORKSPACE_ID)
|
|
1758
|
-
.map((initiative) => buildInitiativeResourcePayload(initiative
|
|
1826
|
+
.map((initiative) => buildInitiativeResourcePayload(initiative, {
|
|
1827
|
+
workstreamsByInitiativeId,
|
|
1828
|
+
tasksByWorkstreamId,
|
|
1829
|
+
}));
|
|
1759
1830
|
return {
|
|
1760
1831
|
total: initiatives.length,
|
|
1761
1832
|
initiatives,
|
|
1762
1833
|
};
|
|
1763
1834
|
}
|
|
1764
1835
|
function buildWorkstreamsResourcePayload() {
|
|
1836
|
+
const { tasksByWorkstreamId } = buildHistoricalWorkspaceTaskSnapshot();
|
|
1765
1837
|
const workstreams = core.listWorkstreams(ACTIVE_WORKSPACE_ID)
|
|
1766
|
-
.map((workstream) => buildWorkstreamResourcePayload(workstream));
|
|
1838
|
+
.map((workstream) => buildWorkstreamResourcePayload(workstream, { tasksByWorkstreamId }));
|
|
1767
1839
|
return {
|
|
1768
1840
|
total: workstreams.length,
|
|
1769
1841
|
workstreams,
|
|
@@ -3230,18 +3302,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3230
3302
|
}
|
|
3231
3303
|
}
|
|
3232
3304
|
};
|
|
3233
|
-
}).filter((tool) =>
|
|
3234
|
-
if (!hasPermission(TOOL_PERMISSIONS[tool.name]))
|
|
3235
|
-
return false;
|
|
3236
|
-
const taskforceToolsEnv = process.env.TASKFORCE_TOOLS;
|
|
3237
|
-
if (taskforceToolsEnv) {
|
|
3238
|
-
const enabledTools = taskforceToolsEnv.split(',').map(t => t.trim()).filter(Boolean);
|
|
3239
|
-
if (enabledTools.length > 0 && !enabledTools.includes(tool.name)) {
|
|
3240
|
-
return false;
|
|
3241
|
-
}
|
|
3242
|
-
}
|
|
3243
|
-
return true;
|
|
3244
|
-
});
|
|
3305
|
+
}).filter((tool) => hasPermission(TOOL_PERMISSIONS[tool.name]));
|
|
3245
3306
|
return {
|
|
3246
3307
|
tools,
|
|
3247
3308
|
};
|
package/dist/mcp/runtime.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import path from 'path';
|
|
@@ -7,61 +7,8 @@ import { MCP_TOOL_REGISTRY } from './toolRegistry';
|
|
|
7
7
|
import { TaskforceCore, TaskforceRuleError } from '../core/Taskforce';
|
|
8
8
|
import { WorkspaceAssetStore } from '../storage/workspaceAssetStore';
|
|
9
9
|
import { ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY, APP_GATE_DEFINITIONS, } from '../runtime/appGates';
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
const core = new TaskforceCore({
|
|
13
|
-
projectRoot,
|
|
14
|
-
storagePath: path.join(projectRoot, '.taskforce')
|
|
15
|
-
});
|
|
16
|
-
try {
|
|
17
|
-
Object.defineProperty(core, 'getAccountAccessStatus', {
|
|
18
|
-
configurable: true,
|
|
19
|
-
value: () => ({
|
|
20
|
-
gate: 'ok',
|
|
21
|
-
hasEntitlement: true,
|
|
22
|
-
canAccessApp: true,
|
|
23
|
-
canAccessPlans: true,
|
|
24
|
-
signupMonetizationStrategy: 'none',
|
|
25
|
-
planSelectionRequired: false,
|
|
26
|
-
message: null,
|
|
27
|
-
entitlement: null
|
|
28
|
-
})
|
|
29
|
-
});
|
|
30
|
-
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
31
|
-
core.ensureBootstrapUserAccess({
|
|
32
|
-
userId: 'owner-1',
|
|
33
|
-
workspaceId: 'workspace-1',
|
|
34
|
-
role: 'owner',
|
|
35
|
-
email: 'owner-1@example.com'
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
finally {
|
|
39
|
-
core.close();
|
|
40
|
-
}
|
|
41
|
-
return projectRoot;
|
|
42
|
-
}
|
|
43
|
-
async function listTools(projectRoot, runtimeMode) {
|
|
44
|
-
const runtime = createTaskforceMcpServer({
|
|
45
|
-
projectRoot,
|
|
46
|
-
runtimeMode,
|
|
47
|
-
logStartup: false,
|
|
48
|
-
auth: runtimeMode === 'cloud'
|
|
49
|
-
? { scopes: ['tasks:read', 'tasks:write', 'workspace:read'] }
|
|
50
|
-
: undefined
|
|
51
|
-
});
|
|
52
|
-
const handler = runtime.server._requestHandlers.get('tools/list');
|
|
53
|
-
if (!handler) {
|
|
54
|
-
throw new Error('tools/list handler not registered');
|
|
55
|
-
}
|
|
56
|
-
return await handler({
|
|
57
|
-
jsonrpc: '2.0',
|
|
58
|
-
id: 'test-tools-list',
|
|
59
|
-
method: 'tools/list',
|
|
60
|
-
params: {}
|
|
61
|
-
}, {});
|
|
62
|
-
}
|
|
63
|
-
async function callTool(projectRoot, runtimeMode, name, args, options) {
|
|
64
|
-
const runtime = createTaskforceMcpServer({
|
|
10
|
+
function createRuntime(projectRoot, runtimeMode, options) {
|
|
11
|
+
return createTaskforceMcpServer({
|
|
65
12
|
projectRoot,
|
|
66
13
|
runtimeMode,
|
|
67
14
|
workspaceId: options?.workspaceId,
|
|
@@ -83,6 +30,8 @@ async function callTool(projectRoot, runtimeMode, name, args, options) {
|
|
|
83
30
|
}
|
|
84
31
|
: undefined
|
|
85
32
|
});
|
|
33
|
+
}
|
|
34
|
+
async function callToolWithRuntime(runtime, name, args, options) {
|
|
86
35
|
const handler = runtime.server._requestHandlers.get('tools/call');
|
|
87
36
|
if (!handler) {
|
|
88
37
|
throw new Error('tools/call handler not registered');
|
|
@@ -112,20 +61,71 @@ async function callTool(projectRoot, runtimeMode, name, args, options) {
|
|
|
112
61
|
}
|
|
113
62
|
}, {});
|
|
114
63
|
}
|
|
115
|
-
async function
|
|
116
|
-
const
|
|
64
|
+
async function subscribeResource(runtime, uri) {
|
|
65
|
+
const handler = runtime.server._requestHandlers.get('resources/subscribe');
|
|
66
|
+
if (!handler) {
|
|
67
|
+
throw new Error('resources/subscribe handler not registered');
|
|
68
|
+
}
|
|
69
|
+
await handler({
|
|
70
|
+
jsonrpc: '2.0',
|
|
71
|
+
id: `test-resources-subscribe-${uri}`,
|
|
72
|
+
method: 'resources/subscribe',
|
|
73
|
+
params: { uri }
|
|
74
|
+
}, {});
|
|
75
|
+
}
|
|
76
|
+
function createProjectRoot() {
|
|
77
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'taskforce-mcp-runtime-'));
|
|
78
|
+
const core = new TaskforceCore({
|
|
117
79
|
projectRoot,
|
|
118
|
-
|
|
119
|
-
workspaceId: options?.workspaceId,
|
|
120
|
-
logStartup: false,
|
|
121
|
-
auth: runtimeMode === 'cloud'
|
|
122
|
-
? {
|
|
123
|
-
scopes: ['tasks:read', 'tasks:write', 'workspace:read'],
|
|
124
|
-
userId: options?.authUserId || 'owner-1',
|
|
125
|
-
actorType: 'oauth'
|
|
126
|
-
}
|
|
127
|
-
: undefined
|
|
80
|
+
storagePath: path.join(projectRoot, '.taskforce')
|
|
128
81
|
});
|
|
82
|
+
try {
|
|
83
|
+
Object.defineProperty(core, 'getAccountAccessStatus', {
|
|
84
|
+
configurable: true,
|
|
85
|
+
value: () => ({
|
|
86
|
+
gate: 'ok',
|
|
87
|
+
hasEntitlement: true,
|
|
88
|
+
canAccessApp: true,
|
|
89
|
+
canAccessPlans: true,
|
|
90
|
+
planSelectionRequired: false,
|
|
91
|
+
message: null,
|
|
92
|
+
entitlement: null
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
96
|
+
core.ensureBootstrapUserAccess({
|
|
97
|
+
userId: 'owner-1',
|
|
98
|
+
workspaceId: 'workspace-1',
|
|
99
|
+
role: 'owner',
|
|
100
|
+
email: 'owner-1@example.com'
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
core.close();
|
|
105
|
+
}
|
|
106
|
+
return projectRoot;
|
|
107
|
+
}
|
|
108
|
+
async function listTools(projectRoot, runtimeMode) {
|
|
109
|
+
const runtime = createRuntime(projectRoot, runtimeMode);
|
|
110
|
+
const handler = runtime.server._requestHandlers.get('tools/list');
|
|
111
|
+
if (!handler) {
|
|
112
|
+
throw new Error('tools/list handler not registered');
|
|
113
|
+
}
|
|
114
|
+
return await handler({
|
|
115
|
+
jsonrpc: '2.0',
|
|
116
|
+
id: 'test-tools-list',
|
|
117
|
+
method: 'tools/list',
|
|
118
|
+
params: {}
|
|
119
|
+
}, {});
|
|
120
|
+
}
|
|
121
|
+
async function callTool(projectRoot, runtimeMode, name, args, options) {
|
|
122
|
+
const runtime = createRuntime(projectRoot, runtimeMode, options);
|
|
123
|
+
return await callToolWithRuntime(runtime, name, args, {
|
|
124
|
+
resolveProfile: options?.resolveProfile,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async function readResource(projectRoot, runtimeMode, uri, options) {
|
|
128
|
+
const runtime = createRuntime(projectRoot, runtimeMode, options);
|
|
129
129
|
const handler = runtime.server._requestHandlers.get('resources/read');
|
|
130
130
|
if (!handler) {
|
|
131
131
|
throw new Error('resources/read handler not registered');
|
|
@@ -138,19 +138,7 @@ async function readResource(projectRoot, runtimeMode, uri, options) {
|
|
|
138
138
|
}, {});
|
|
139
139
|
}
|
|
140
140
|
async function listResources(projectRoot, runtimeMode, options) {
|
|
141
|
-
const runtime =
|
|
142
|
-
projectRoot,
|
|
143
|
-
runtimeMode,
|
|
144
|
-
workspaceId: options?.workspaceId,
|
|
145
|
-
logStartup: false,
|
|
146
|
-
auth: runtimeMode === 'cloud'
|
|
147
|
-
? {
|
|
148
|
-
scopes: ['tasks:read', 'tasks:write', 'workspace:read'],
|
|
149
|
-
userId: options?.authUserId || 'owner-1',
|
|
150
|
-
actorType: 'oauth'
|
|
151
|
-
}
|
|
152
|
-
: undefined
|
|
153
|
-
});
|
|
141
|
+
const runtime = createRuntime(projectRoot, runtimeMode, options);
|
|
154
142
|
const handler = runtime.server._requestHandlers.get('resources/list');
|
|
155
143
|
if (!handler) {
|
|
156
144
|
throw new Error('resources/list handler not registered');
|
|
@@ -304,6 +292,29 @@ describe('Taskforce MCP tool descriptions', () => {
|
|
|
304
292
|
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
305
293
|
}
|
|
306
294
|
});
|
|
295
|
+
it('does not let TASKFORCE_TOOLS prune the runtime tool catalog', async () => {
|
|
296
|
+
const projectRoot = createProjectRoot();
|
|
297
|
+
const previousValue = process.env.TASKFORCE_TOOLS;
|
|
298
|
+
process.env.TASKFORCE_TOOLS = 'get_task';
|
|
299
|
+
try {
|
|
300
|
+
const result = await listTools(projectRoot, 'cloud');
|
|
301
|
+
const runtimeToolNames = result.tools.map((tool) => tool.name).sort();
|
|
302
|
+
const registryToolNames = Object.entries(MCP_TOOL_REGISTRY)
|
|
303
|
+
.filter(([, entry]) => entry.permission.kind !== 'local-only' && entry.permission.kind !== 'session-only')
|
|
304
|
+
.map(([name]) => name)
|
|
305
|
+
.sort();
|
|
306
|
+
expect(runtimeToolNames).toEqual(registryToolNames);
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
if (previousValue === undefined) {
|
|
310
|
+
delete process.env.TASKFORCE_TOOLS;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
process.env.TASKFORCE_TOOLS = previousValue;
|
|
314
|
+
}
|
|
315
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
316
|
+
}
|
|
317
|
+
});
|
|
307
318
|
it('includes local-only guidance in local runtime tool descriptions', async () => {
|
|
308
319
|
const projectRoot = createProjectRoot();
|
|
309
320
|
try {
|
|
@@ -1500,6 +1511,150 @@ describe('Taskforce MCP tool descriptions', () => {
|
|
|
1500
1511
|
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
1501
1512
|
}
|
|
1502
1513
|
});
|
|
1514
|
+
it('builds workstream listings from one historical task snapshot and returns task summaries', async () => {
|
|
1515
|
+
const projectRoot = createProjectRoot();
|
|
1516
|
+
const core = new TaskforceCore({
|
|
1517
|
+
projectRoot,
|
|
1518
|
+
storagePath: path.join(projectRoot, '.taskforce')
|
|
1519
|
+
});
|
|
1520
|
+
try {
|
|
1521
|
+
const category = core.getCategories()[0]?.value;
|
|
1522
|
+
const type = core.getTypes()[0]?.value;
|
|
1523
|
+
const firstWorkstream = core.createWorkstream({ title: 'Workstream one' }, 'workspace-1');
|
|
1524
|
+
const secondWorkstream = core.createWorkstream({ title: 'Workstream two' }, 'workspace-1');
|
|
1525
|
+
core.createTask({
|
|
1526
|
+
title: 'Linked task one',
|
|
1527
|
+
category,
|
|
1528
|
+
type,
|
|
1529
|
+
workstreamId: firstWorkstream.id,
|
|
1530
|
+
}, { workspaceId: 'workspace-1', actorRef: 'owner-1' });
|
|
1531
|
+
core.createTask({
|
|
1532
|
+
title: 'Linked task two',
|
|
1533
|
+
category,
|
|
1534
|
+
type,
|
|
1535
|
+
workstreamId: secondWorkstream.id,
|
|
1536
|
+
}, { workspaceId: 'workspace-1', actorRef: 'owner-1' });
|
|
1537
|
+
const archivedTask = core.createTask({
|
|
1538
|
+
title: 'Archived linked task',
|
|
1539
|
+
category,
|
|
1540
|
+
type,
|
|
1541
|
+
workstreamId: firstWorkstream.id,
|
|
1542
|
+
status: 'done',
|
|
1543
|
+
}, { workspaceId: 'workspace-1', actorRef: 'owner-1' });
|
|
1544
|
+
core.archiveTask(archivedTask.id, false, undefined, 'workspace-1', { actorRef: 'owner-1' });
|
|
1545
|
+
}
|
|
1546
|
+
finally {
|
|
1547
|
+
core.close();
|
|
1548
|
+
}
|
|
1549
|
+
const listTasksSpy = vi.spyOn(TaskforceCore.prototype, 'listTasks');
|
|
1550
|
+
const listArchiveSpy = vi.spyOn(TaskforceCore.prototype, 'listArchive');
|
|
1551
|
+
try {
|
|
1552
|
+
const beforeListTasksCalls = listTasksSpy.mock.calls.length;
|
|
1553
|
+
const beforeListArchiveCalls = listArchiveSpy.mock.calls.length;
|
|
1554
|
+
const payload = parseToolJson(await callTool(projectRoot, 'local', 'list_workstreams', {}));
|
|
1555
|
+
const firstWorkstreamPayload = payload.workstreams.find((entry) => entry.workstream.title === 'Workstream one');
|
|
1556
|
+
const linkedTask = firstWorkstreamPayload?.tasks.find((task) => task.title === 'Archived linked task');
|
|
1557
|
+
expect(listTasksSpy.mock.calls.length - beforeListTasksCalls).toBe(1);
|
|
1558
|
+
expect(listArchiveSpy.mock.calls.length - beforeListArchiveCalls).toBe(1);
|
|
1559
|
+
expect(linkedTask).toMatchObject({
|
|
1560
|
+
title: 'Archived linked task',
|
|
1561
|
+
workstreamId: firstWorkstreamPayload?.workstream.id,
|
|
1562
|
+
status: 'done',
|
|
1563
|
+
});
|
|
1564
|
+
expect(linkedTask.description).toBeUndefined();
|
|
1565
|
+
expect(linkedTask.comments).toBeUndefined();
|
|
1566
|
+
expect(linkedTask.activity).toBeUndefined();
|
|
1567
|
+
expect(linkedTask.attachments).toBeUndefined();
|
|
1568
|
+
expect(linkedTask.checklistItems).toBeUndefined();
|
|
1569
|
+
}
|
|
1570
|
+
finally {
|
|
1571
|
+
listTasksSpy.mockRestore();
|
|
1572
|
+
listArchiveSpy.mockRestore();
|
|
1573
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
it('queues bulk resource invalidations instead of awaiting them in the write path', async () => {
|
|
1577
|
+
const projectRoot = createProjectRoot();
|
|
1578
|
+
const runtime = createRuntime(projectRoot, 'local');
|
|
1579
|
+
const resourceUri = `taskforce://${path.basename(projectRoot)}/tasks`;
|
|
1580
|
+
const sendResourceUpdatedSpy = vi
|
|
1581
|
+
.spyOn(runtime.server, 'sendResourceUpdated')
|
|
1582
|
+
.mockImplementation(async () => {
|
|
1583
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
1584
|
+
});
|
|
1585
|
+
try {
|
|
1586
|
+
await subscribeResource(runtime, resourceUri);
|
|
1587
|
+
const created = parseToolJson(await callToolWithRuntime(runtime, 'create_task', {
|
|
1588
|
+
title: 'Queued invalidation task'
|
|
1589
|
+
}));
|
|
1590
|
+
expect(created.title).toBe('Queued invalidation task');
|
|
1591
|
+
expect(sendResourceUpdatedSpy).not.toHaveBeenCalled();
|
|
1592
|
+
await new Promise((resolve) => setTimeout(resolve, 40));
|
|
1593
|
+
expect(sendResourceUpdatedSpy).toHaveBeenCalledTimes(1);
|
|
1594
|
+
expect(sendResourceUpdatedSpy).toHaveBeenCalledWith({ uri: resourceUri });
|
|
1595
|
+
}
|
|
1596
|
+
finally {
|
|
1597
|
+
sendResourceUpdatedSpy.mockRestore();
|
|
1598
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
it('reads the bulk tasks resource as summary-only rows', async () => {
|
|
1602
|
+
const projectRoot = createProjectRoot();
|
|
1603
|
+
try {
|
|
1604
|
+
const created = parseToolJson(await callTool(projectRoot, 'local', 'create_task', {
|
|
1605
|
+
title: 'Verbose task',
|
|
1606
|
+
description: 'This should stay off the bulk tasks resource'
|
|
1607
|
+
}));
|
|
1608
|
+
await callTool(projectRoot, 'local', 'add_comment', {
|
|
1609
|
+
id: created.referenceLabel,
|
|
1610
|
+
text: 'This comment should stay off the bulk tasks resource',
|
|
1611
|
+
author: 'ai-profile-019d15c5-6ad3-7bf0-9b9e-fa9c366c3ff4'
|
|
1612
|
+
});
|
|
1613
|
+
const taskResource = await readResource(projectRoot, 'cloud', `taskforce://${path.basename(projectRoot)}/tasks`, {
|
|
1614
|
+
workspaceId: 'workspace-1',
|
|
1615
|
+
authUserId: 'owner-1'
|
|
1616
|
+
});
|
|
1617
|
+
const payload = JSON.parse(String(taskResource.contents[0]?.text || '{}'));
|
|
1618
|
+
const resourceTask = payload.tasks.find((task) => task.referenceLabel === created.referenceLabel);
|
|
1619
|
+
expect(resourceTask).toMatchObject({
|
|
1620
|
+
referenceLabel: created.referenceLabel,
|
|
1621
|
+
title: 'Verbose task',
|
|
1622
|
+
});
|
|
1623
|
+
expect(resourceTask.description).toBeUndefined();
|
|
1624
|
+
expect(resourceTask.comments).toBeUndefined();
|
|
1625
|
+
expect(resourceTask.activity).toBeUndefined();
|
|
1626
|
+
expect(resourceTask.attachments).toBeUndefined();
|
|
1627
|
+
expect(resourceTask.checklistItems).toBeUndefined();
|
|
1628
|
+
}
|
|
1629
|
+
finally {
|
|
1630
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
it('reads initiative listings with summary initiative and workstream records only', async () => {
|
|
1634
|
+
const projectRoot = createProjectRoot();
|
|
1635
|
+
try {
|
|
1636
|
+
const initiative = parseToolJson(await callTool(projectRoot, 'local', 'create_initiative', {
|
|
1637
|
+
title: 'Summary initiative',
|
|
1638
|
+
description: 'Verbose initiative details should stay off the bulk listing'
|
|
1639
|
+
}));
|
|
1640
|
+
await callTool(projectRoot, 'local', 'create_workstream', {
|
|
1641
|
+
title: 'Summary workstream',
|
|
1642
|
+
description: 'Verbose workstream details should stay off the bulk listing',
|
|
1643
|
+
initiativeId: initiative.initiative.referenceLabel
|
|
1644
|
+
});
|
|
1645
|
+
const initiatives = parseToolJson(await callTool(projectRoot, 'local', 'list_initiatives', {}));
|
|
1646
|
+
const listedInitiative = initiatives.initiatives.find((entry) => entry.initiative.referenceLabel === initiative.initiative.referenceLabel);
|
|
1647
|
+
const listedWorkstream = listedInitiative?.workstreams.find((entry) => entry.title === 'Summary workstream');
|
|
1648
|
+
expect(listedInitiative).toBeTruthy();
|
|
1649
|
+
expect(listedInitiative.initiative.description).toBeUndefined();
|
|
1650
|
+
expect(listedWorkstream).toBeTruthy();
|
|
1651
|
+
expect(listedWorkstream.description).toBeUndefined();
|
|
1652
|
+
expect(listedWorkstream.taskIds).toBeUndefined();
|
|
1653
|
+
}
|
|
1654
|
+
finally {
|
|
1655
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1503
1658
|
it('creates MCP tasks as unassigned when no explicit assignee is provided', async () => {
|
|
1504
1659
|
const projectRoot = createProjectRoot();
|
|
1505
1660
|
try {
|
|
@@ -1541,6 +1696,42 @@ describe('Taskforce MCP tool descriptions', () => {
|
|
|
1541
1696
|
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
1542
1697
|
}
|
|
1543
1698
|
});
|
|
1699
|
+
it('returns summary-shaped task entries from the shared tasks resource', async () => {
|
|
1700
|
+
const projectRoot = createProjectRoot();
|
|
1701
|
+
try {
|
|
1702
|
+
const core = new TaskforceCore({
|
|
1703
|
+
projectRoot,
|
|
1704
|
+
storagePath: path.join(projectRoot, '.taskforce')
|
|
1705
|
+
});
|
|
1706
|
+
core.createTask({
|
|
1707
|
+
title: 'Resource summary task',
|
|
1708
|
+
description: 'Verbose task description',
|
|
1709
|
+
comments: [{ id: 'comment-1', author: 'user', text: 'Hello', timestamp: '2026-04-13T01:00:00.000Z' }],
|
|
1710
|
+
attachments: [{ name: 'note.md', path: 'notes/note.md' }],
|
|
1711
|
+
checklistItems: [{ id: 'item-1', taskId: 'temp', title: 'Check me', isCompleted: false, order: 0, createdAt: '2026-04-13T01:00:00.000Z' }],
|
|
1712
|
+
}, {
|
|
1713
|
+
workspaceId: 'workspace-1',
|
|
1714
|
+
actorRef: 'owner-1'
|
|
1715
|
+
});
|
|
1716
|
+
core.close();
|
|
1717
|
+
const tasksResource = await readResource(projectRoot, 'cloud', `taskforce://${path.basename(projectRoot)}/tasks`, {
|
|
1718
|
+
workspaceId: 'workspace-1',
|
|
1719
|
+
authUserId: 'owner-1'
|
|
1720
|
+
});
|
|
1721
|
+
const payload = JSON.parse(String(tasksResource.contents[0]?.text || '{}'));
|
|
1722
|
+
const task = payload.tasks.find((entry) => entry.title === 'Resource summary task');
|
|
1723
|
+
expect(task).toBeDefined();
|
|
1724
|
+
expect(task.description).toBeUndefined();
|
|
1725
|
+
expect(task.comments).toBeUndefined();
|
|
1726
|
+
expect(task.taskEvents).toBeUndefined();
|
|
1727
|
+
expect(task.activity).toBeUndefined();
|
|
1728
|
+
expect(task.attachments).toBeUndefined();
|
|
1729
|
+
expect(task.checklistItems).toBeUndefined();
|
|
1730
|
+
}
|
|
1731
|
+
finally {
|
|
1732
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1544
1735
|
it('persists save_task_attachment through canonical task attachments', async () => {
|
|
1545
1736
|
const projectRoot = createProjectRoot();
|
|
1546
1737
|
try {
|
|
@@ -25,6 +25,7 @@ describe('billing schema parity', () => {
|
|
|
25
25
|
expect(names.has('user_billing')).toBe(true);
|
|
26
26
|
expect(names.has('stripe_webhook_events')).toBe(true);
|
|
27
27
|
expect(names.has('stripe_webhook_failures')).toBe(true);
|
|
28
|
+
expect(names.has('billing_checkout_attempts')).toBe(true);
|
|
28
29
|
expect(names.has('billing_config')).toBe(true);
|
|
29
30
|
expect(names.has('billing_price_mappings')).toBe(true);
|
|
30
31
|
}
|
|
@@ -101,9 +102,11 @@ describe('billing schema parity', () => {
|
|
|
101
102
|
expect(sql).toContain('CREATE TABLE IF NOT EXISTS user_billing');
|
|
102
103
|
expect(sql).toContain('CREATE TABLE IF NOT EXISTS stripe_webhook_events');
|
|
103
104
|
expect(sql).toContain('CREATE TABLE IF NOT EXISTS stripe_webhook_failures');
|
|
105
|
+
expect(sql).toContain('CREATE TABLE IF NOT EXISTS billing_checkout_attempts');
|
|
104
106
|
expect(sql).toContain('CREATE TABLE IF NOT EXISTS billing_config');
|
|
105
107
|
expect(sql).toContain('CREATE TABLE IF NOT EXISTS billing_price_mappings');
|
|
106
108
|
expect(sql).toContain('CREATE INDEX IF NOT EXISTS idx_stripe_webhook_failures_purge_after');
|
|
109
|
+
expect(sql).toContain('CREATE INDEX IF NOT EXISTS idx_billing_checkout_attempts_user_status');
|
|
107
110
|
expect(sql).toContain('CREATE INDEX IF NOT EXISTS idx_billing_price_mappings_plan');
|
|
108
111
|
expect(sql).toContain('CREATE UNIQUE INDEX IF NOT EXISTS idx_billing_price_mappings_active_interval');
|
|
109
112
|
});
|
|
@@ -493,6 +493,18 @@ export function ensureBillingSchema(db) {
|
|
|
493
493
|
created_at TEXT NOT NULL
|
|
494
494
|
);
|
|
495
495
|
|
|
496
|
+
CREATE TABLE IF NOT EXISTS billing_checkout_attempts (
|
|
497
|
+
session_id TEXT PRIMARY KEY,
|
|
498
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
499
|
+
user_id TEXT NOT NULL,
|
|
500
|
+
plan_version_id TEXT NOT NULL,
|
|
501
|
+
billing_interval TEXT,
|
|
502
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
503
|
+
expires_at TEXT,
|
|
504
|
+
created_at TEXT NOT NULL,
|
|
505
|
+
updated_at TEXT NOT NULL
|
|
506
|
+
);
|
|
507
|
+
|
|
496
508
|
CREATE TABLE IF NOT EXISTS billing_config (
|
|
497
509
|
tenant_id TEXT PRIMARY KEY,
|
|
498
510
|
config_json TEXT NOT NULL DEFAULT '{}',
|
|
@@ -520,6 +532,8 @@ export function ensureBillingSchema(db) {
|
|
|
520
532
|
CREATE INDEX IF NOT EXISTS idx_stripe_webhook_failures_tenant ON stripe_webhook_failures(tenant_id);
|
|
521
533
|
CREATE INDEX IF NOT EXISTS idx_stripe_webhook_failures_event ON stripe_webhook_failures(event_id);
|
|
522
534
|
CREATE INDEX IF NOT EXISTS idx_stripe_webhook_failures_purge_after ON stripe_webhook_failures(purge_after);
|
|
535
|
+
CREATE INDEX IF NOT EXISTS idx_billing_checkout_attempts_user_status ON billing_checkout_attempts(tenant_id, user_id, status, created_at);
|
|
536
|
+
CREATE INDEX IF NOT EXISTS idx_billing_checkout_attempts_plan_status ON billing_checkout_attempts(tenant_id, plan_version_id, status, created_at);
|
|
523
537
|
CREATE INDEX IF NOT EXISTS idx_billing_price_mappings_plan ON billing_price_mappings(tenant_id, plan_version_id);
|
|
524
538
|
CREATE INDEX IF NOT EXISTS idx_billing_price_mappings_active ON billing_price_mappings(tenant_id, active);
|
|
525
539
|
`);
|
|
@@ -723,6 +737,7 @@ export function ensureEntitlementsSchema(db) {
|
|
|
723
737
|
updated_by TEXT,
|
|
724
738
|
created_at TEXT NOT NULL,
|
|
725
739
|
updated_at TEXT NOT NULL,
|
|
740
|
+
FOREIGN KEY (account_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
726
741
|
FOREIGN KEY (plan_version_id) REFERENCES plan_versions(plan_version_id)
|
|
727
742
|
);
|
|
728
743
|
|
|
@@ -22,6 +22,7 @@ describe('proxy cookie rewrite helpers', () => {
|
|
|
22
22
|
expect(shouldProxyCloudPath('/api/taskforce/public/site-config-script')).toBe(true);
|
|
23
23
|
expect(shouldProxyCloudPath('/api/taskforce/version')).toBe(true);
|
|
24
24
|
expect(shouldProxyCloudPath('/api/taskforce/account/entitlements')).toBe(true);
|
|
25
|
+
expect(shouldProxyCloudPath('/api/taskforce/account/profile-summary')).toBe(true);
|
|
25
26
|
expect(shouldProxyCloudPath('/api/taskforce/account/team-management-access')).toBe(true);
|
|
26
27
|
expect(shouldProxyCloudPath('/api/taskforce/admin/mcp-audit-logs')).toBe(true);
|
|
27
28
|
expect(shouldProxyCloudPath('/api/taskforce/billing/status')).toBe(true);
|
package/dist/server/index.js
CHANGED
|
@@ -109,6 +109,8 @@ export function shouldProxyCloudPath(pathname) {
|
|
|
109
109
|
|| pathname === PUBLIC_SITE_CONFIG_SCRIPT_PATH
|
|
110
110
|
|| pathname === '/api/taskforce/version'
|
|
111
111
|
|| pathname === '/api/taskforce/account/entitlements'
|
|
112
|
+
|| pathname === '/api/taskforce/account/profile-summary'
|
|
113
|
+
|| pathname === '/api/taskforce/account/team-management-access'
|
|
112
114
|
|| pathname.startsWith('/api/taskforce/admin/')
|
|
113
115
|
|| pathname.startsWith('/api/taskforce/billing/')
|
|
114
116
|
|| pathname.startsWith('/api/taskforce/auth/')
|
|
@@ -86,7 +86,8 @@ describe('shouldProxyCloudPath', () => {
|
|
|
86
86
|
expect(shouldProxyCloudPath('/api/taskforce/admin/users')).toBe(true);
|
|
87
87
|
expect(shouldProxyCloudPath('/api/taskforce/billing/plans')).toBe(true);
|
|
88
88
|
expect(shouldProxyCloudPath('/api/taskforce/auth/login')).toBe(true);
|
|
89
|
-
expect(shouldProxyCloudPath('/api/taskforce/account/
|
|
89
|
+
expect(shouldProxyCloudPath('/api/taskforce/account/profile-summary')).toBe(true);
|
|
90
|
+
expect(shouldProxyCloudPath('/api/taskforce/account/team-management-access')).toBe(true);
|
|
90
91
|
expect(shouldProxyCloudPath('/api/taskforce/public/site-config')).toBe(true);
|
|
91
92
|
expect(shouldProxyCloudPath('/api/taskforce/public/site-config-script')).toBe(true);
|
|
92
93
|
});
|