@hauptsache.net/clickup-mcp 1.1.0 → 1.2.0
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 +6 -1
- package/dist/cli.js +1 -0
- package/dist/clickup-text.d.ts +5 -2
- package/dist/clickup-text.d.ts.map +1 -1
- package/dist/clickup-text.js +129 -61
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/shared/config.d.ts +1 -0
- package/dist/shared/config.d.ts.map +1 -1
- package/dist/shared/config.js +1 -0
- package/dist/shared/image-processing.d.ts +13 -0
- package/dist/shared/image-processing.d.ts.map +1 -0
- package/dist/shared/image-processing.js +126 -0
- package/dist/shared/types.d.ts +7 -1
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/utils.d.ts +62 -11
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +281 -63
- package/dist/tools/list-tools.d.ts.map +1 -1
- package/dist/tools/list-tools.js +34 -143
- package/dist/tools/search-tools.d.ts +1 -1
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +79 -33
- package/dist/tools/space-tools.d.ts.map +1 -1
- package/dist/tools/space-tools.js +160 -31
- package/dist/tools/task-tools.d.ts +1 -2
- package/dist/tools/task-tools.d.ts.map +1 -1
- package/dist/tools/task-tools.js +92 -380
- package/dist/tools/task-write-tools.d.ts +3 -0
- package/dist/tools/task-write-tools.d.ts.map +1 -0
- package/dist/tools/task-write-tools.js +375 -0
- package/dist/tools/time-tools.d.ts.map +1 -1
- package/dist/tools/time-tools.js +191 -111
- package/package.json +14 -4
package/dist/shared/utils.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ContentBlock } from "./types";
|
|
2
1
|
import Fuse from 'fuse.js';
|
|
3
2
|
/**
|
|
4
3
|
* Checks if a string looks like a valid ClickUp task ID
|
|
@@ -7,24 +6,76 @@ import Fuse from 'fuse.js';
|
|
|
7
6
|
export declare function isTaskId(str: string): boolean;
|
|
8
7
|
/**
|
|
9
8
|
* Get current authenticated user information from ClickUp API
|
|
10
|
-
* Caches the
|
|
9
|
+
* Caches the promise to prevent race conditions on concurrent calls
|
|
11
10
|
*/
|
|
12
11
|
export declare function getCurrentUser(): Promise<any>;
|
|
13
|
-
|
|
14
|
-
* Limits the number of images in the content array, replacing excess images with text placeholders
|
|
15
|
-
* Prioritizes keeping the most recent images (assumes content is ordered with newest items last)
|
|
16
|
-
*
|
|
17
|
-
* @param content Array of content blocks that may contain images
|
|
18
|
-
* @param maxImages Maximum number of images to keep
|
|
19
|
-
* @returns Modified content array with limited images
|
|
20
|
-
*/
|
|
21
|
-
export declare function limitImages(content: ContentBlock[], maxImages: number): ContentBlock[];
|
|
12
|
+
export { downloadImages } from "./image-processing";
|
|
22
13
|
/**
|
|
23
14
|
* Function to get space details, using a cache to avoid redundant fetches
|
|
24
15
|
*/
|
|
25
16
|
export declare function getSpaceDetails(spaceId: string): Promise<any>;
|
|
26
17
|
/**
|
|
27
18
|
* Get or create a task search index with specified filters
|
|
19
|
+
* Caches promises to prevent race conditions on concurrent calls
|
|
28
20
|
*/
|
|
29
21
|
export declare function getTaskSearchIndex(space_ids?: string[], list_ids?: string[], assignees?: string[]): Promise<Fuse<any> | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Generate a ClickUp task URL from a task ID
|
|
24
|
+
*/
|
|
25
|
+
export declare function generateTaskUrl(taskId: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generate a ClickUp list URL from a list ID
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateListUrl(listId: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Generate a ClickUp space URL from a space ID
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateSpaceUrl(spaceId: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Generate a ClickUp folder URL from a folder ID
|
|
36
|
+
*/
|
|
37
|
+
export declare function generateFolderUrl(folderId: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Format a ClickUp task link as markdown
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatTaskLink(taskId: string, taskName?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Format a ClickUp list link as markdown
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatListLink(listId: string, listName?: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Format a ClickUp space link as markdown
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatSpaceLink(spaceId: string, spaceName?: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Extract task ID from a ClickUp URL
|
|
52
|
+
*/
|
|
53
|
+
export declare function extractTaskIdFromUrl(url: string): string | null;
|
|
54
|
+
/**
|
|
55
|
+
* Validate if a string is a valid ClickUp URL
|
|
56
|
+
*/
|
|
57
|
+
export declare function isClickUpUrl(url: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Format a prominent link section for responses
|
|
60
|
+
*/
|
|
61
|
+
export declare function formatLinksSection(links: {
|
|
62
|
+
text: string;
|
|
63
|
+
url: string;
|
|
64
|
+
}[]): string;
|
|
65
|
+
/**
|
|
66
|
+
* Get or refresh the space search index
|
|
67
|
+
* Caches promise to prevent race conditions on concurrent calls
|
|
68
|
+
*/
|
|
69
|
+
export declare function getSpaceSearchIndex(): Promise<Fuse<any> | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Get lists and folders for a specific space with caching
|
|
72
|
+
*/
|
|
73
|
+
export declare function getSpaceContent(spaceId: string): Promise<{
|
|
74
|
+
lists: any[];
|
|
75
|
+
folders: any[];
|
|
76
|
+
}>;
|
|
77
|
+
/**
|
|
78
|
+
* Gets all team members from ClickUp API with caching
|
|
79
|
+
*/
|
|
80
|
+
export declare function getAllTeamMembers(): Promise<string[]>;
|
|
30
81
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/shared/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/shared/utils.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,SAAS,CAAC;AAI3B;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG7C;AAKD;;;GAGG;AACH,wBAAsB,cAAc,iBA6BnC;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CA0B7D;AAKD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,CAAC,EAAE,MAAM,EAAE,EACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,EACnB,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CA+B3B;AAiED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAI3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,MAAM,CAKjF;AAKD;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CA+CrE;AAKD;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,EAAE,CAAA;CAAE,CAAC,CAgEhG;AAKD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CA+C3D"}
|
package/dist/shared/utils.js
CHANGED
|
@@ -3,11 +3,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.downloadImages = void 0;
|
|
6
7
|
exports.isTaskId = isTaskId;
|
|
7
8
|
exports.getCurrentUser = getCurrentUser;
|
|
8
|
-
exports.limitImages = limitImages;
|
|
9
9
|
exports.getSpaceDetails = getSpaceDetails;
|
|
10
10
|
exports.getTaskSearchIndex = getTaskSearchIndex;
|
|
11
|
+
exports.generateTaskUrl = generateTaskUrl;
|
|
12
|
+
exports.generateListUrl = generateListUrl;
|
|
13
|
+
exports.generateSpaceUrl = generateSpaceUrl;
|
|
14
|
+
exports.generateFolderUrl = generateFolderUrl;
|
|
15
|
+
exports.formatTaskLink = formatTaskLink;
|
|
16
|
+
exports.formatListLink = formatListLink;
|
|
17
|
+
exports.formatSpaceLink = formatSpaceLink;
|
|
18
|
+
exports.extractTaskIdFromUrl = extractTaskIdFromUrl;
|
|
19
|
+
exports.isClickUpUrl = isClickUpUrl;
|
|
20
|
+
exports.formatLinksSection = formatLinksSection;
|
|
21
|
+
exports.getSpaceSearchIndex = getSpaceSearchIndex;
|
|
22
|
+
exports.getSpaceContent = getSpaceContent;
|
|
23
|
+
exports.getAllTeamMembers = getAllTeamMembers;
|
|
11
24
|
const config_1 = require("./config");
|
|
12
25
|
const fuse_js_1 = __importDefault(require("fuse.js"));
|
|
13
26
|
const GLOBAL_REFRESH_INTERVAL = 60000; // 60 seconds - that is the rate limit time frame
|
|
@@ -19,62 +32,39 @@ function isTaskId(str) {
|
|
|
19
32
|
// Task IDs are 6-9 characters long and contain only alphanumeric characters
|
|
20
33
|
return /^[a-z0-9]{6,9}$/i.test(str);
|
|
21
34
|
}
|
|
22
|
-
// Cache for current user info to avoid repeated API calls
|
|
23
|
-
let
|
|
35
|
+
// Cache for current user info to avoid repeated API calls and race conditions
|
|
36
|
+
let cachedUserPromise = null;
|
|
24
37
|
/**
|
|
25
38
|
* Get current authenticated user information from ClickUp API
|
|
26
|
-
* Caches the
|
|
39
|
+
* Caches the promise to prevent race conditions on concurrent calls
|
|
27
40
|
*/
|
|
28
41
|
async function getCurrentUser() {
|
|
29
|
-
// Return cached
|
|
30
|
-
if (
|
|
31
|
-
return
|
|
42
|
+
// Return cached promise if available
|
|
43
|
+
if (cachedUserPromise) {
|
|
44
|
+
return cachedUserPromise;
|
|
32
45
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
cachedUserData = await userResponse.json();
|
|
41
|
-
return cachedUserData;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Limits the number of images in the content array, replacing excess images with text placeholders
|
|
45
|
-
* Prioritizes keeping the most recent images (assumes content is ordered with newest items last)
|
|
46
|
-
*
|
|
47
|
-
* @param content Array of content blocks that may contain images
|
|
48
|
-
* @param maxImages Maximum number of images to keep
|
|
49
|
-
* @returns Modified content array with limited images
|
|
50
|
-
*/
|
|
51
|
-
function limitImages(content, maxImages) {
|
|
52
|
-
// Count how many images we have
|
|
53
|
-
const imageIndices = [];
|
|
54
|
-
// Find all image blocks
|
|
55
|
-
content.forEach((block, index) => {
|
|
56
|
-
if (block.type === "image") {
|
|
57
|
-
imageIndices.push(index);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
// If we have fewer images than the limit, return the original content
|
|
61
|
-
if (imageIndices.length <= maxImages) {
|
|
62
|
-
return content;
|
|
63
|
-
}
|
|
64
|
-
// Determine which images to keep (the most recent ones)
|
|
65
|
-
// We want to keep the last 'maxImages' images
|
|
66
|
-
const imagesToRemove = imageIndices.slice(0, imageIndices.length - maxImages);
|
|
67
|
-
// Create a new content array with excess images replaced by text
|
|
68
|
-
return content.map((block, index) => {
|
|
69
|
-
if (block.type === "image" && imagesToRemove.includes(index)) {
|
|
70
|
-
return {
|
|
71
|
-
type: "text",
|
|
72
|
-
text: "[Image removed due to size limitations. Only the most recent images are shown.]",
|
|
73
|
-
};
|
|
46
|
+
// Create the fetch promise
|
|
47
|
+
const fetchPromise = (async () => {
|
|
48
|
+
const userResponse = await fetch("https://api.clickup.com/api/v2/user", {
|
|
49
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
50
|
+
});
|
|
51
|
+
if (!userResponse.ok) {
|
|
52
|
+
throw new Error(`Error fetching user info: ${userResponse.status} ${userResponse.statusText}`);
|
|
74
53
|
}
|
|
75
|
-
return
|
|
76
|
-
});
|
|
54
|
+
return await userResponse.json();
|
|
55
|
+
})();
|
|
56
|
+
// Cache the promise
|
|
57
|
+
cachedUserPromise = fetchPromise;
|
|
58
|
+
// Auto-cleanup after 60 seconds
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
cachedUserPromise = null;
|
|
61
|
+
console.error(`Auto-cleaned user data cache`);
|
|
62
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
63
|
+
return fetchPromise;
|
|
77
64
|
}
|
|
65
|
+
// Re-export image processing functions for backward compatibility
|
|
66
|
+
var image_processing_1 = require("./image-processing");
|
|
67
|
+
Object.defineProperty(exports, "downloadImages", { enumerable: true, get: function () { return image_processing_1.downloadImages; } });
|
|
78
68
|
const spaceCache = new Map(); // Global cache for space details promises
|
|
79
69
|
/**
|
|
80
70
|
* Function to get space details, using a cache to avoid redundant fetches
|
|
@@ -101,10 +91,11 @@ function getSpaceDetails(spaceId) {
|
|
|
101
91
|
spaceCache.set(spaceId, fetchPromise);
|
|
102
92
|
return fetchPromise;
|
|
103
93
|
}
|
|
104
|
-
// Task search index management
|
|
94
|
+
// Task search index management - cache promises to prevent race conditions
|
|
105
95
|
const taskIndices = new Map();
|
|
106
96
|
/**
|
|
107
97
|
* Get or create a task search index with specified filters
|
|
98
|
+
* Caches promises to prevent race conditions on concurrent calls
|
|
108
99
|
*/
|
|
109
100
|
async function getTaskSearchIndex(space_ids, list_ids, assignees) {
|
|
110
101
|
// Create cache key from sorted filter arrays
|
|
@@ -113,23 +104,26 @@ async function getTaskSearchIndex(space_ids, list_ids, assignees) {
|
|
|
113
104
|
list_ids: list_ids?.sort(),
|
|
114
105
|
assignees: assignees?.sort()
|
|
115
106
|
});
|
|
116
|
-
// Check for existing valid index
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
119
|
-
return
|
|
107
|
+
// Check for existing valid index promise
|
|
108
|
+
const cachedPromise = taskIndices.get(key);
|
|
109
|
+
if (cachedPromise) {
|
|
110
|
+
return cachedPromise;
|
|
120
111
|
}
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
112
|
+
// Create the fetch promise
|
|
113
|
+
const fetchPromise = (async () => {
|
|
114
|
+
console.error(`Refreshing task index for filters: ${key}`);
|
|
115
|
+
const tasks = await fetchTasks(space_ids, list_ids, assignees);
|
|
116
|
+
const index = createFuseIndex(tasks);
|
|
117
|
+
console.error(`Task index created with ${tasks.length} tasks`);
|
|
118
|
+
return index;
|
|
119
|
+
})();
|
|
120
|
+
// Store promise with auto-cleanup
|
|
121
|
+
taskIndices.set(key, fetchPromise);
|
|
127
122
|
setTimeout(() => {
|
|
128
123
|
taskIndices.delete(key);
|
|
129
124
|
console.error(`Auto-cleaned index for filters: ${key}`);
|
|
130
125
|
}, GLOBAL_REFRESH_INTERVAL);
|
|
131
|
-
|
|
132
|
-
return index;
|
|
126
|
+
return fetchPromise;
|
|
133
127
|
}
|
|
134
128
|
/**
|
|
135
129
|
* Fetch tasks using team endpoint with dynamic filters
|
|
@@ -183,3 +177,227 @@ function createFuseIndex(tasks) {
|
|
|
183
177
|
minMatchCharLength: 2,
|
|
184
178
|
});
|
|
185
179
|
}
|
|
180
|
+
// ===== LINK UTILITIES =====
|
|
181
|
+
/**
|
|
182
|
+
* Generate a ClickUp task URL from a task ID
|
|
183
|
+
*/
|
|
184
|
+
function generateTaskUrl(taskId) {
|
|
185
|
+
return `https://app.clickup.com/t/${taskId}`;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Generate a ClickUp list URL from a list ID
|
|
189
|
+
*/
|
|
190
|
+
function generateListUrl(listId) {
|
|
191
|
+
return `https://app.clickup.com/v/l/${listId}`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Generate a ClickUp space URL from a space ID
|
|
195
|
+
*/
|
|
196
|
+
function generateSpaceUrl(spaceId) {
|
|
197
|
+
return `https://app.clickup.com/v/s/${spaceId}`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Generate a ClickUp folder URL from a folder ID
|
|
201
|
+
*/
|
|
202
|
+
function generateFolderUrl(folderId) {
|
|
203
|
+
return `https://app.clickup.com/v/f/${folderId}`;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Format a ClickUp task link as markdown
|
|
207
|
+
*/
|
|
208
|
+
function formatTaskLink(taskId, taskName) {
|
|
209
|
+
const url = generateTaskUrl(taskId);
|
|
210
|
+
const displayText = taskName ? `${taskName} (${taskId})` : taskId;
|
|
211
|
+
return `[${displayText}](${url})`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Format a ClickUp list link as markdown
|
|
215
|
+
*/
|
|
216
|
+
function formatListLink(listId, listName) {
|
|
217
|
+
const url = generateListUrl(listId);
|
|
218
|
+
const displayText = listName ? `${listName} (${listId})` : listId;
|
|
219
|
+
return `[${displayText}](${url})`;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Format a ClickUp space link as markdown
|
|
223
|
+
*/
|
|
224
|
+
function formatSpaceLink(spaceId, spaceName) {
|
|
225
|
+
const url = generateSpaceUrl(spaceId);
|
|
226
|
+
const displayText = spaceName ? `${spaceName} (${spaceId})` : spaceId;
|
|
227
|
+
return `[${displayText}](${url})`;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Extract task ID from a ClickUp URL
|
|
231
|
+
*/
|
|
232
|
+
function extractTaskIdFromUrl(url) {
|
|
233
|
+
const match = url.match(/https?:\/\/app\.clickup\.com\/t\/([a-z0-9]{6,9})/i);
|
|
234
|
+
return match ? match[1] : null;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Validate if a string is a valid ClickUp URL
|
|
238
|
+
*/
|
|
239
|
+
function isClickUpUrl(url) {
|
|
240
|
+
return /^https?:\/\/app\.clickup\.com\//.test(url);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Format a prominent link section for responses
|
|
244
|
+
*/
|
|
245
|
+
function formatLinksSection(links) {
|
|
246
|
+
if (links.length === 0)
|
|
247
|
+
return '';
|
|
248
|
+
const linkLines = links.map(link => `🔗 [${link.text}](${link.url})`);
|
|
249
|
+
return `\n\n**📌 Quick Links:**\n${linkLines.join('\n')}`;
|
|
250
|
+
}
|
|
251
|
+
// Space search index cache - cache promise to prevent race conditions
|
|
252
|
+
let spaceSearchIndexPromise = null;
|
|
253
|
+
/**
|
|
254
|
+
* Get or refresh the space search index
|
|
255
|
+
* Caches promise to prevent race conditions on concurrent calls
|
|
256
|
+
*/
|
|
257
|
+
async function getSpaceSearchIndex() {
|
|
258
|
+
// Return cached promise if available
|
|
259
|
+
if (spaceSearchIndexPromise) {
|
|
260
|
+
return spaceSearchIndexPromise;
|
|
261
|
+
}
|
|
262
|
+
// Create the fetch promise
|
|
263
|
+
const fetchPromise = (async () => {
|
|
264
|
+
try {
|
|
265
|
+
const url = `https://api.clickup.com/api/v2/team/${config_1.CONFIG.teamId}/space`;
|
|
266
|
+
const response = await fetch(url, {
|
|
267
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
268
|
+
});
|
|
269
|
+
if (!response.ok) {
|
|
270
|
+
throw new Error(`Error fetching spaces: ${response.status} ${response.statusText}`);
|
|
271
|
+
}
|
|
272
|
+
const data = await response.json();
|
|
273
|
+
const spacesData = data.spaces || [];
|
|
274
|
+
// Create Fuse search index
|
|
275
|
+
return new fuse_js_1.default(spacesData, {
|
|
276
|
+
keys: [
|
|
277
|
+
{ name: 'name', weight: 0.7 },
|
|
278
|
+
{ name: 'id', weight: 0.6 }
|
|
279
|
+
],
|
|
280
|
+
includeScore: true,
|
|
281
|
+
threshold: 0.4,
|
|
282
|
+
minMatchCharLength: 1,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
console.error('Error creating space search index:', error);
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
})();
|
|
290
|
+
// Cache the promise
|
|
291
|
+
spaceSearchIndexPromise = fetchPromise;
|
|
292
|
+
// Auto-cleanup after 60 seconds
|
|
293
|
+
setTimeout(() => {
|
|
294
|
+
spaceSearchIndexPromise = null;
|
|
295
|
+
console.error('Auto-cleaned space search index');
|
|
296
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
297
|
+
return fetchPromise;
|
|
298
|
+
}
|
|
299
|
+
const listCache = new Map(); // Cache for space lists/folders
|
|
300
|
+
/**
|
|
301
|
+
* Get lists and folders for a specific space with caching
|
|
302
|
+
*/
|
|
303
|
+
async function getSpaceContent(spaceId) {
|
|
304
|
+
const cacheKey = `space-content-${spaceId}`;
|
|
305
|
+
// Check cache first
|
|
306
|
+
const cachedContent = listCache.get(cacheKey);
|
|
307
|
+
if (cachedContent) {
|
|
308
|
+
return cachedContent;
|
|
309
|
+
}
|
|
310
|
+
// Fetch content with parallel requests
|
|
311
|
+
const fetchPromise = (async () => {
|
|
312
|
+
try {
|
|
313
|
+
const [foldersResponse, listsResponse] = await Promise.all([
|
|
314
|
+
fetch(`https://api.clickup.com/api/v2/space/${spaceId}/folder`, {
|
|
315
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
316
|
+
}),
|
|
317
|
+
fetch(`https://api.clickup.com/api/v2/space/${spaceId}/list`, {
|
|
318
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
319
|
+
})
|
|
320
|
+
]);
|
|
321
|
+
const folders = foldersResponse.ok ?
|
|
322
|
+
(await foldersResponse.json()).folders || [] : [];
|
|
323
|
+
const lists = listsResponse.ok ?
|
|
324
|
+
(await listsResponse.json()).lists || [] : [];
|
|
325
|
+
// For each folder, also fetch its lists
|
|
326
|
+
const folderListPromises = folders.map(async (folder) => {
|
|
327
|
+
try {
|
|
328
|
+
const folderListResponse = await fetch(`https://api.clickup.com/api/v2/folder/${folder.id}/list`, { headers: { Authorization: config_1.CONFIG.apiKey } });
|
|
329
|
+
if (folderListResponse.ok) {
|
|
330
|
+
const folderListData = await folderListResponse.json();
|
|
331
|
+
folder.lists = folderListData.lists || [];
|
|
332
|
+
}
|
|
333
|
+
return folder;
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
console.error(`Error fetching lists for folder ${folder.id}:`, error);
|
|
337
|
+
folder.lists = [];
|
|
338
|
+
return folder;
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
const foldersWithLists = await Promise.all(folderListPromises);
|
|
342
|
+
return { lists, folders: foldersWithLists };
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
console.error(`Error fetching space content for ${spaceId}:`, error);
|
|
346
|
+
return { lists: [], folders: [] };
|
|
347
|
+
}
|
|
348
|
+
})();
|
|
349
|
+
// Cache the promise
|
|
350
|
+
listCache.set(cacheKey, fetchPromise);
|
|
351
|
+
// Auto-cleanup after 60 seconds
|
|
352
|
+
setTimeout(() => {
|
|
353
|
+
listCache.delete(cacheKey);
|
|
354
|
+
console.error(`Auto-cleaned space content cache for ${spaceId}`);
|
|
355
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
356
|
+
return fetchPromise;
|
|
357
|
+
}
|
|
358
|
+
// Cache for team members to avoid repeated API calls and race conditions
|
|
359
|
+
let cachedTeamMembersPromise = null;
|
|
360
|
+
/**
|
|
361
|
+
* Gets all team members from ClickUp API with caching
|
|
362
|
+
*/
|
|
363
|
+
async function getAllTeamMembers() {
|
|
364
|
+
// Return cached promise if available
|
|
365
|
+
if (cachedTeamMembersPromise) {
|
|
366
|
+
return cachedTeamMembersPromise;
|
|
367
|
+
}
|
|
368
|
+
// Create the fetch promise
|
|
369
|
+
const fetchPromise = (async () => {
|
|
370
|
+
try {
|
|
371
|
+
const response = await fetch(`https://api.clickup.com/api/v2/team`, {
|
|
372
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
373
|
+
});
|
|
374
|
+
if (!response.ok) {
|
|
375
|
+
console.error(`Error fetching teams: ${response.status} ${response.statusText}`);
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
const data = await response.json();
|
|
379
|
+
if (!data.teams || !Array.isArray(data.teams)) {
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
// Find the team that matches our configured team ID and extract all user IDs
|
|
383
|
+
const currentTeam = data.teams.find((team) => team.id === config_1.CONFIG.teamId);
|
|
384
|
+
if (!currentTeam || !currentTeam.members || !Array.isArray(currentTeam.members)) {
|
|
385
|
+
console.error(`Team ${config_1.CONFIG.teamId} not found or has no members`);
|
|
386
|
+
return [];
|
|
387
|
+
}
|
|
388
|
+
return currentTeam.members.map((member) => member.user?.id).filter(Boolean);
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
console.error('Error fetching team members:', error);
|
|
392
|
+
return [];
|
|
393
|
+
}
|
|
394
|
+
})();
|
|
395
|
+
// Cache the promise
|
|
396
|
+
cachedTeamMembersPromise = fetchPromise;
|
|
397
|
+
// Auto-cleanup after 60 seconds
|
|
398
|
+
setTimeout(() => {
|
|
399
|
+
cachedTeamMembersPromise = null;
|
|
400
|
+
console.error(`Auto-cleaned team members cache`);
|
|
401
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
402
|
+
return fetchPromise;
|
|
403
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-tools.d.ts","sourceRoot":"","sources":["../../src/tools/list-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"list-tools.d.ts","sourceRoot":"","sources":["../../src/tools/list-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QA+GtD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAyEvD"}
|