@catchmexz/fedin-vibe-mcp-server 0.1.8 → 0.1.10
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/common/utils.js +68 -35
- package/dist/index.js +30 -58
- package/dist/operations/codeup/files.js +61 -51
- package/dist/operations/codeup/repositories.js +12 -1
- package/dist/operations/codeup/types.js +3 -1
- package/dist/tool-handlers/code-management.js +4 -4
- package/dist/tool-registry/code-management.js +16 -0
- package/package.json +1 -1
package/dist/common/utils.js
CHANGED
|
@@ -11,7 +11,7 @@ export function getYunxiaoApiBaseUrl() {
|
|
|
11
11
|
}
|
|
12
12
|
export function debug(message, data) {
|
|
13
13
|
if (data !== undefined) {
|
|
14
|
-
console.error(`[DEBUG] ${message}`, typeof data ===
|
|
14
|
+
console.error(`[DEBUG] ${message}`, typeof data === "object" ? JSON.stringify(data, null, 2) : data);
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
17
|
console.error(`[DEBUG] ${message}`);
|
|
@@ -27,7 +27,9 @@ async function parseResponseBody(response) {
|
|
|
27
27
|
export function buildUrl(baseUrl, params) {
|
|
28
28
|
// Handle baseUrl that doesn't have protocol
|
|
29
29
|
const isAbsolute = baseUrl.startsWith("http://") || baseUrl.startsWith("https://");
|
|
30
|
-
const fullBaseUrl = isAbsolute
|
|
30
|
+
const fullBaseUrl = isAbsolute
|
|
31
|
+
? baseUrl
|
|
32
|
+
: `${getYunxiaoApiBaseUrl()}${baseUrl.startsWith("/") ? baseUrl : `/${baseUrl}`}`;
|
|
31
33
|
try {
|
|
32
34
|
const url = new URL(fullBaseUrl);
|
|
33
35
|
Object.entries(params).forEach(([key, value]) => {
|
|
@@ -38,7 +40,7 @@ export function buildUrl(baseUrl, params) {
|
|
|
38
40
|
const result = url.toString();
|
|
39
41
|
console.error(`[DEBUG] Final URL: ${result}`);
|
|
40
42
|
// If we started with a relative URL, return just the path portion
|
|
41
|
-
if (!baseUrl.startsWith(
|
|
43
|
+
if (!baseUrl.startsWith("http")) {
|
|
42
44
|
// Extract the path and query string from the full URL
|
|
43
45
|
const urlObj = new URL(result);
|
|
44
46
|
return urlObj.pathname + urlObj.search;
|
|
@@ -56,7 +58,8 @@ export function buildUrl(baseUrl, params) {
|
|
|
56
58
|
}
|
|
57
59
|
});
|
|
58
60
|
if (queryParts.length > 0) {
|
|
59
|
-
urlWithParams +=
|
|
61
|
+
urlWithParams +=
|
|
62
|
+
(urlWithParams.includes("?") ? "&" : "?") + queryParts.join("&");
|
|
60
63
|
}
|
|
61
64
|
console.error(`[DEBUG] Fallback URL: ${urlWithParams}`);
|
|
62
65
|
return urlWithParams;
|
|
@@ -65,24 +68,25 @@ export function buildUrl(baseUrl, params) {
|
|
|
65
68
|
const USER_AGENT = `modelcontextprotocol/servers/alibabacloud-devops-mcp-server/v${VERSION} ${getUserAgent()}`;
|
|
66
69
|
export async function yunxiaoRequest(urlPath, options = {}) {
|
|
67
70
|
// Check if the URL is already a full URL or a path
|
|
71
|
+
const yunxiao_access_token = "pt-1ntvDyIqJuXDksW1cvnZTKGx_e1815902-148e-4709-a46a-c7b14c003234";
|
|
68
72
|
const isAbsolute = urlPath.startsWith("http://") || urlPath.startsWith("https://");
|
|
69
|
-
let url = isAbsolute
|
|
73
|
+
let url = isAbsolute
|
|
74
|
+
? urlPath
|
|
75
|
+
: `${getYunxiaoApiBaseUrl()}${urlPath.startsWith("/") ? urlPath : `/${urlPath}`}`;
|
|
70
76
|
const requestHeaders = {
|
|
71
|
-
|
|
77
|
+
Accept: "application/json",
|
|
72
78
|
"Content-Type": "application/json",
|
|
73
79
|
"User-Agent": USER_AGENT,
|
|
74
|
-
...options.headers
|
|
80
|
+
...options.headers
|
|
75
81
|
};
|
|
76
|
-
|
|
77
|
-
requestHeaders["x-yunxiao-token"] = process.env.YUNXIAO_ACCESS_TOKEN;
|
|
78
|
-
}
|
|
82
|
+
requestHeaders["x-yunxiao-token"] = yunxiao_access_token;
|
|
79
83
|
debug(`Request: ${options.method} ${url}`);
|
|
80
84
|
debug(`Headers:`, requestHeaders);
|
|
81
85
|
debug(`Body:`, options.body);
|
|
82
86
|
const response = await fetch(url, {
|
|
83
87
|
method: options.method || "GET",
|
|
84
88
|
headers: requestHeaders,
|
|
85
|
-
body: options.body ? JSON.stringify(options.body) : undefined
|
|
89
|
+
body: options.body ? JSON.stringify(options.body) : undefined
|
|
86
90
|
});
|
|
87
91
|
const responseBody = await parseResponseBody(response);
|
|
88
92
|
debug(`Response Body:`, responseBody);
|
|
@@ -127,7 +131,7 @@ export function handleRepositoryIdEncoding(repositoryId) {
|
|
|
127
131
|
*/
|
|
128
132
|
export function floatToIntString(value) {
|
|
129
133
|
// 如果传入的是字符串,先尝试转为浮点数
|
|
130
|
-
if (typeof value ===
|
|
134
|
+
if (typeof value === "string") {
|
|
131
135
|
const floatValue = parseFloat(value);
|
|
132
136
|
if (!isNaN(floatValue)) {
|
|
133
137
|
value = floatValue;
|
|
@@ -137,7 +141,7 @@ export function floatToIntString(value) {
|
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
143
|
// 处理浮点数
|
|
140
|
-
if (typeof value ===
|
|
144
|
+
if (typeof value === "number") {
|
|
141
145
|
const intValue = Math.floor(value + 0.5); // 四舍五入转整数
|
|
142
146
|
return intValue.toString();
|
|
143
147
|
}
|
|
@@ -156,7 +160,7 @@ export function floatToIntString(value) {
|
|
|
156
160
|
* @returns 毫秒时间戳
|
|
157
161
|
*/
|
|
158
162
|
export function convertToTimestamp(time) {
|
|
159
|
-
if (typeof time ===
|
|
163
|
+
if (typeof time === "number") {
|
|
160
164
|
// 如果已经是数字,假设已是时间戳
|
|
161
165
|
return time;
|
|
162
166
|
}
|
|
@@ -164,7 +168,7 @@ export function convertToTimestamp(time) {
|
|
|
164
168
|
// 如果是Date对象,转换为时间戳
|
|
165
169
|
return time.getTime();
|
|
166
170
|
}
|
|
167
|
-
else if (typeof time ===
|
|
171
|
+
else if (typeof time === "string") {
|
|
168
172
|
// 尝试解析日期字符串
|
|
169
173
|
const date = new Date(time);
|
|
170
174
|
if (!isNaN(date.getTime())) {
|
|
@@ -172,7 +176,7 @@ export function convertToTimestamp(time) {
|
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
// 无法转换时返回原值(如果是数字)或当前时间戳
|
|
175
|
-
return typeof time ===
|
|
179
|
+
return typeof time === "number" ? time : Date.now();
|
|
176
180
|
}
|
|
177
181
|
/**
|
|
178
182
|
* Get start of today timestamp
|
|
@@ -200,7 +204,7 @@ export function getEndOfTodayTimestamp() {
|
|
|
200
204
|
* @returns Timestamp for start of the specified day
|
|
201
205
|
*/
|
|
202
206
|
export function getStartOfDayTimestamp(date) {
|
|
203
|
-
const targetDate = typeof date ===
|
|
207
|
+
const targetDate = typeof date === "string" ? new Date(date) : new Date(date);
|
|
204
208
|
targetDate.setHours(0, 0, 0, 0);
|
|
205
209
|
return targetDate.getTime();
|
|
206
210
|
}
|
|
@@ -210,7 +214,7 @@ export function getStartOfDayTimestamp(date) {
|
|
|
210
214
|
* @returns Timestamp for end of the specified day
|
|
211
215
|
*/
|
|
212
216
|
export function getEndOfDayTimestamp(date) {
|
|
213
|
-
const targetDate = typeof date ===
|
|
217
|
+
const targetDate = typeof date === "string" ? new Date(date) : new Date(date);
|
|
214
218
|
targetDate.setHours(23, 59, 59, 999);
|
|
215
219
|
return targetDate.getTime();
|
|
216
220
|
}
|
|
@@ -222,9 +226,11 @@ export function getEndOfDayTimestamp(date) {
|
|
|
222
226
|
export function getStartOfWeekTimestamp(startOnMonday = true) {
|
|
223
227
|
const now = new Date();
|
|
224
228
|
const dayOfWeek = now.getDay(); // 0 is Sunday, 1 is Monday, etc.
|
|
225
|
-
const diff = startOnMonday
|
|
226
|
-
|
|
227
|
-
|
|
229
|
+
const diff = startOnMonday
|
|
230
|
+
? dayOfWeek === 0
|
|
231
|
+
? 6
|
|
232
|
+
: dayOfWeek - 1 // If startOnMonday, set Sunday as day 7
|
|
233
|
+
: dayOfWeek;
|
|
228
234
|
// Set to beginning of the week
|
|
229
235
|
now.setDate(now.getDate() - diff);
|
|
230
236
|
now.setHours(0, 0, 0, 0);
|
|
@@ -238,9 +244,11 @@ export function getStartOfWeekTimestamp(startOnMonday = true) {
|
|
|
238
244
|
export function getEndOfWeekTimestamp(startOnMonday = true) {
|
|
239
245
|
const now = new Date();
|
|
240
246
|
const dayOfWeek = now.getDay(); // 0 is Sunday, 1 is Monday, etc.
|
|
241
|
-
const diff = startOnMonday
|
|
242
|
-
|
|
243
|
-
|
|
247
|
+
const diff = startOnMonday
|
|
248
|
+
? dayOfWeek === 0
|
|
249
|
+
? 0
|
|
250
|
+
: 7 - dayOfWeek // If startOnMonday, set Sunday as day 7
|
|
251
|
+
: 6 - dayOfWeek;
|
|
244
252
|
// Set to end of the week
|
|
245
253
|
now.setDate(now.getDate() + diff);
|
|
246
254
|
now.setHours(23, 59, 59, 999);
|
|
@@ -282,13 +290,13 @@ export function parseDateReference(dateReference) {
|
|
|
282
290
|
}
|
|
283
291
|
const normalizedRef = dateReference.trim().toLowerCase();
|
|
284
292
|
// Today/yesterday
|
|
285
|
-
if (normalizedRef ===
|
|
293
|
+
if (normalizedRef === "today" || normalizedRef === "今天") {
|
|
286
294
|
return {
|
|
287
295
|
startTime: getStartOfTodayTimestamp(),
|
|
288
296
|
endTime: getEndOfTodayTimestamp()
|
|
289
297
|
};
|
|
290
298
|
}
|
|
291
|
-
if (normalizedRef ===
|
|
299
|
+
if (normalizedRef === "yesterday" || normalizedRef === "昨天") {
|
|
292
300
|
const yesterday = new Date();
|
|
293
301
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
294
302
|
return {
|
|
@@ -297,16 +305,20 @@ export function parseDateReference(dateReference) {
|
|
|
297
305
|
};
|
|
298
306
|
}
|
|
299
307
|
// This week/last week
|
|
300
|
-
if (normalizedRef ===
|
|
301
|
-
normalizedRef ===
|
|
302
|
-
normalizedRef ===
|
|
308
|
+
if (normalizedRef === "this week" ||
|
|
309
|
+
normalizedRef === "本周" ||
|
|
310
|
+
normalizedRef === "current week" ||
|
|
311
|
+
normalizedRef === "这周" ||
|
|
312
|
+
normalizedRef === "这个星期") {
|
|
303
313
|
return {
|
|
304
314
|
startTime: getStartOfWeekTimestamp(),
|
|
305
315
|
endTime: getEndOfWeekTimestamp()
|
|
306
316
|
};
|
|
307
317
|
}
|
|
308
|
-
if (normalizedRef ===
|
|
309
|
-
normalizedRef ===
|
|
318
|
+
if (normalizedRef === "last week" ||
|
|
319
|
+
normalizedRef === "上周" ||
|
|
320
|
+
normalizedRef === "上個星期" ||
|
|
321
|
+
normalizedRef === "上个星期") {
|
|
310
322
|
const lastWeekStart = new Date(getStartOfWeekTimestamp());
|
|
311
323
|
lastWeekStart.setDate(lastWeekStart.getDate() - 7);
|
|
312
324
|
const lastWeekEnd = new Date(getEndOfWeekTimestamp());
|
|
@@ -317,15 +329,18 @@ export function parseDateReference(dateReference) {
|
|
|
317
329
|
};
|
|
318
330
|
}
|
|
319
331
|
// This month/last month
|
|
320
|
-
if (normalizedRef ===
|
|
321
|
-
normalizedRef ===
|
|
332
|
+
if (normalizedRef === "this month" ||
|
|
333
|
+
normalizedRef === "本月" ||
|
|
334
|
+
normalizedRef === "current month" ||
|
|
335
|
+
normalizedRef === "这个月") {
|
|
322
336
|
return {
|
|
323
337
|
startTime: getStartOfMonthTimestamp(),
|
|
324
338
|
endTime: getEndOfMonthTimestamp()
|
|
325
339
|
};
|
|
326
340
|
}
|
|
327
|
-
if (normalizedRef ===
|
|
328
|
-
normalizedRef ===
|
|
341
|
+
if (normalizedRef === "last month" ||
|
|
342
|
+
normalizedRef === "上月" ||
|
|
343
|
+
normalizedRef === "上个月") {
|
|
329
344
|
const now = new Date();
|
|
330
345
|
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1);
|
|
331
346
|
// Start of last month
|
|
@@ -345,3 +360,21 @@ export function parseDateReference(dateReference) {
|
|
|
345
360
|
endTime: Date.now()
|
|
346
361
|
};
|
|
347
362
|
}
|
|
363
|
+
export async function fedinQueryRequest(endpoint, query) {
|
|
364
|
+
try {
|
|
365
|
+
const response = await fetch(`https://ai.fedin.cn/${endpoint}`, {
|
|
366
|
+
method: "post",
|
|
367
|
+
body: JSON.stringify({
|
|
368
|
+
query
|
|
369
|
+
}),
|
|
370
|
+
headers: {
|
|
371
|
+
"Content-Type": "application/json"
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
const data = await response.json();
|
|
375
|
+
return data;
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { VERSION } from "./common/version.js";
|
|
|
9
9
|
import { config } from "dotenv";
|
|
10
10
|
import { getAllTools } from "./tool-registry/index.js";
|
|
11
11
|
import { handleToolRequest } from "./tool-handlers/index.js";
|
|
12
|
+
import { fedinQueryRequest } from "./common/utils.js";
|
|
12
13
|
const server = new Server({
|
|
13
14
|
name: "alibabacloud-devops-mcp-server",
|
|
14
15
|
version: VERSION,
|
|
@@ -64,47 +65,10 @@ function formatYunxiaoError(error) {
|
|
|
64
65
|
}
|
|
65
66
|
return message;
|
|
66
67
|
}
|
|
67
|
-
async function
|
|
68
|
-
const query = `
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
ch.pat,
|
|
72
|
-
ch.id as project_id,
|
|
73
|
-
ch.mini_app_id
|
|
74
|
-
FROM
|
|
75
|
-
public.codeup AS c
|
|
76
|
-
INNER JOIN
|
|
77
|
-
public.chat_history AS ch
|
|
78
|
-
ON
|
|
79
|
-
c.chat_id = ch.id
|
|
80
|
-
WHERE
|
|
81
|
-
ch.pat = '${pat}'
|
|
82
|
-
`;
|
|
83
|
-
try {
|
|
84
|
-
const response = await fetch('https://ai.fedin.cn/api/supabase/query', {
|
|
85
|
-
method: "post",
|
|
86
|
-
body: JSON.stringify({
|
|
87
|
-
query
|
|
88
|
-
}),
|
|
89
|
-
headers: {
|
|
90
|
-
'Content-Type': 'application/json',
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
const data = await response.json();
|
|
94
|
-
if (data.length > 0) {
|
|
95
|
-
return {
|
|
96
|
-
repositoryId: data[0].repositoryId,
|
|
97
|
-
projectId: data[0].project_id,
|
|
98
|
-
miniappId: data[0].mini_app_id
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
throw new Error("pat密钥无效:未找到对应的repositoryId");
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
throw new Error("获取repositoryId失败");
|
|
107
|
-
}
|
|
68
|
+
async function checkTokenValid(token) {
|
|
69
|
+
const query = `SELECT * FROM codeup_tokens WHERE token = '${token}'`;
|
|
70
|
+
const response = await fedinQueryRequest("api/supabase/query", query);
|
|
71
|
+
return response.length > 0;
|
|
108
72
|
}
|
|
109
73
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
110
74
|
return {
|
|
@@ -116,25 +80,33 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
116
80
|
if (!request.params.arguments) {
|
|
117
81
|
throw new Error("Arguments are required");
|
|
118
82
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
pat = arg.split("=")?.[1];
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
if (!pat) {
|
|
126
|
-
throw new Error("缺少项目访问令牌project-pat");
|
|
127
|
-
}
|
|
128
|
-
const { repositoryId, projectId, miniappId } = await getRepositoryIdWithPat(pat);
|
|
129
|
-
if (!repositoryId) {
|
|
130
|
-
throw new Error("获取repositoryId失败");
|
|
83
|
+
const fedin_token = process.env.FEDIN_ACCESS_TOKEN;
|
|
84
|
+
if (!fedin_token) {
|
|
85
|
+
throw new Error("无权限访问:缺少访问令牌");
|
|
131
86
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (miniappId) {
|
|
136
|
-
request.params.arguments.miniappId = miniappId;
|
|
87
|
+
const isTokenValid = await checkTokenValid(fedin_token);
|
|
88
|
+
if (!isTokenValid) {
|
|
89
|
+
throw new Error("无权限访问:访问令牌无效");
|
|
137
90
|
}
|
|
91
|
+
// let pat = "";
|
|
92
|
+
// process.argv.forEach(arg => {
|
|
93
|
+
// if (arg.includes("--project-pat")) {
|
|
94
|
+
// pat = arg.split("=")?.[1];
|
|
95
|
+
// }
|
|
96
|
+
// })
|
|
97
|
+
// if (!pat) {
|
|
98
|
+
// throw new Error("缺少项目访问令牌project-pat");
|
|
99
|
+
// }
|
|
100
|
+
// const { repositoryId, projectId, miniappId } = await getRepositoryIdWithPat(pat);
|
|
101
|
+
// if (!repositoryId) {
|
|
102
|
+
// throw new Error("获取repositoryId失败");
|
|
103
|
+
// }
|
|
104
|
+
// request.params.arguments.repositoryId = repositoryId;
|
|
105
|
+
// request.params.arguments.projectId = projectId;
|
|
106
|
+
// // 只有当 miniappId 不为 null 时才注入,避免 Zod 验证错误
|
|
107
|
+
// if (miniappId) {
|
|
108
|
+
// request.params.arguments.miniappId = miniappId;
|
|
109
|
+
// }
|
|
138
110
|
// Delegate to our modular tool handler
|
|
139
111
|
return await handleToolRequest(request);
|
|
140
112
|
}
|
|
@@ -4,6 +4,7 @@ import * as fs from "fs";
|
|
|
4
4
|
import pkg from "fs-extra";
|
|
5
5
|
import * as path from "path";
|
|
6
6
|
import minimatch from "minimatch";
|
|
7
|
+
import { fedinQueryRequest } from "../../common/utils.js";
|
|
7
8
|
const { removeSync } = pkg;
|
|
8
9
|
function readGitignore(projectPath) {
|
|
9
10
|
const gitignorePath = path.join(projectPath, ".gitignore");
|
|
@@ -250,46 +251,8 @@ export async function createFileFunc(organizationId, repositoryId, filePath, con
|
|
|
250
251
|
});
|
|
251
252
|
return CreateFileResponseSchema.parse(response);
|
|
252
253
|
}
|
|
253
|
-
async function
|
|
254
|
-
|
|
255
|
-
SELECT
|
|
256
|
-
ch.id as id,
|
|
257
|
-
ch.custom_domain as custom_domain,
|
|
258
|
-
ch.domain_certificate as domain_certificate
|
|
259
|
-
FROM
|
|
260
|
-
public.chat_history AS ch
|
|
261
|
-
WHERE
|
|
262
|
-
ch.id = '${projectId}'
|
|
263
|
-
`;
|
|
264
|
-
try {
|
|
265
|
-
const response = await fetch("https://ai.fedin.cn/api/supabase/query", {
|
|
266
|
-
method: "post",
|
|
267
|
-
body: JSON.stringify({
|
|
268
|
-
query
|
|
269
|
-
}),
|
|
270
|
-
headers: {
|
|
271
|
-
"Content-Type": "application/json"
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
const data = await response.json();
|
|
275
|
-
if (data.length > 0) {
|
|
276
|
-
return {
|
|
277
|
-
id: data[0].id,
|
|
278
|
-
custom_domain: data[0].custom_domain,
|
|
279
|
-
domain_certificate: data[0].domain_certificate
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
console.error("项目ID无效:未找到对应的项目");
|
|
284
|
-
return {};
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
catch (error) {
|
|
288
|
-
console.error("获取项目信息失败", error);
|
|
289
|
-
return {};
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
export async function pushFilesFunc(organizationId, repositoryId, commitMessage, projectPath, projectId) {
|
|
254
|
+
export async function pushFilesFunc(organizationId, projectId, currntProjectPath, commitMessage) {
|
|
255
|
+
let repositoryId = "";
|
|
293
256
|
// 读取 .gitignore 模式
|
|
294
257
|
// const ignorePatterns = readGitignore(projectPath);
|
|
295
258
|
const ignorePatterns = [
|
|
@@ -306,8 +269,35 @@ export async function pushFilesFunc(organizationId, repositoryId, commitMessage,
|
|
|
306
269
|
"package-lock.json",
|
|
307
270
|
".DS_Store"
|
|
308
271
|
];
|
|
272
|
+
const selectProjectQuery = `SELECT id FROM chat_history WHERE id = '${projectId}'`;
|
|
273
|
+
const projectResult = await fedinQueryRequest("api/supabase/query", selectProjectQuery);
|
|
274
|
+
if (!projectResult.length) {
|
|
275
|
+
throw new Error("当前项目不存在");
|
|
276
|
+
}
|
|
277
|
+
const query = `SELECT ch.id
|
|
278
|
+
FROM chat_history ch
|
|
279
|
+
JOIN codeup_tokens ct ON ch.user_id = ct.user_id
|
|
280
|
+
WHERE ct.token = '${process.env.FEDIN_ACCESS_TOKEN}';`;
|
|
281
|
+
const projects = await fedinQueryRequest("api/supabase/query", query);
|
|
282
|
+
if (!projects.length) {
|
|
283
|
+
throw new Error("token密钥未找到对应项目,请确保密钥的可用性");
|
|
284
|
+
}
|
|
285
|
+
// 判断查到的projects是否存在projectId
|
|
286
|
+
const project = projects.find((project) => project.id === projectId);
|
|
287
|
+
if (!project) {
|
|
288
|
+
throw new Error("无权限访问当前项目仓库");
|
|
289
|
+
}
|
|
290
|
+
const queryCodeUp = `SELECT "repositoryId" FROM codeup WHERE chat_id = '${projectId}'`;
|
|
291
|
+
const codeupDetail = await fedinQueryRequest("api/supabase/query", queryCodeUp);
|
|
292
|
+
if (!codeupDetail.length) {
|
|
293
|
+
throw new Error("当前项目仓库不存在,请先创建仓库");
|
|
294
|
+
}
|
|
295
|
+
repositoryId = codeupDetail[0].repositoryId;
|
|
296
|
+
if (!repositoryId) {
|
|
297
|
+
throw new Error("当前项目仓库不存在,请先到飞钉AI推送项目代码");
|
|
298
|
+
}
|
|
309
299
|
// 获取所有文件
|
|
310
|
-
const files = getAllFiles(
|
|
300
|
+
const files = getAllFiles(currntProjectPath, ignorePatterns);
|
|
311
301
|
// 检测项目类型
|
|
312
302
|
let projectType = "default"; // 默认值
|
|
313
303
|
// 检查是否存在 Next.js 配置文件
|
|
@@ -368,16 +358,8 @@ export async function pushFilesFunc(organizationId, repositoryId, commitMessage,
|
|
|
368
358
|
repoUrl: `https://codeup.aliyun.com/ctrod/fedin-ai-project/${projectId}.git`,
|
|
369
359
|
projectType,
|
|
370
360
|
organizationId: organizationId,
|
|
371
|
-
token:
|
|
361
|
+
token: "pt-1ntvDyIqJuXDksW1cvnZTKGx_e1815902-148e-4709-a46a-c7b14c003234"
|
|
372
362
|
};
|
|
373
|
-
const projectInfo = await getRepositoryDomain(projectId);
|
|
374
|
-
if (projectInfo.custom_domain) {
|
|
375
|
-
pipelineBody.customDomain = projectInfo.custom_domain;
|
|
376
|
-
}
|
|
377
|
-
// 如果配置了证书,添加到请求体中
|
|
378
|
-
if (projectInfo.domain_certificate) {
|
|
379
|
-
pipelineBody.certificate = projectInfo.domain_certificate;
|
|
380
|
-
}
|
|
381
363
|
const pipelineResponse = await fetch("https://ai.fedin.cn/api/alicloud/create-pipeline-run", {
|
|
382
364
|
method: "POST",
|
|
383
365
|
body: JSON.stringify(pipelineBody),
|
|
@@ -409,7 +391,35 @@ export async function pushFilesFunc(organizationId, repositoryId, commitMessage,
|
|
|
409
391
|
* @param repositoryId
|
|
410
392
|
* @param projectPath
|
|
411
393
|
*/
|
|
412
|
-
export async function pullFilesFunc(organizationId,
|
|
394
|
+
export async function pullFilesFunc(organizationId, projectId, currntProjectPath) {
|
|
395
|
+
let repositoryId = "";
|
|
396
|
+
const selectProjectQuery = `SELECT id FROM chat_history WHERE id = '${projectId}'`;
|
|
397
|
+
const projectResult = await fedinQueryRequest("api/supabase/query", selectProjectQuery);
|
|
398
|
+
if (!projectResult.length) {
|
|
399
|
+
throw new Error("当前项目不存在");
|
|
400
|
+
}
|
|
401
|
+
const query = `SELECT ch.id
|
|
402
|
+
FROM chat_history ch
|
|
403
|
+
JOIN codeup_tokens ct ON ch.user_id = ct.user_id
|
|
404
|
+
WHERE ct.token = '${process.env.FEDIN_ACCESS_TOKEN}';`;
|
|
405
|
+
const projects = await fedinQueryRequest("api/supabase/query", query);
|
|
406
|
+
if (!projects.length) {
|
|
407
|
+
throw new Error("token密钥未找到对应项目,请确保密钥的可用性");
|
|
408
|
+
}
|
|
409
|
+
// 判断查到的projects是否存在projectId
|
|
410
|
+
const project = projects.find((project) => project.id === projectId);
|
|
411
|
+
if (!project) {
|
|
412
|
+
throw new Error("无权限访问当前项目仓库");
|
|
413
|
+
}
|
|
414
|
+
const queryCodeUp = `SELECT "repositoryId" FROM codeup WHERE chat_id = '${projectId}'`;
|
|
415
|
+
const codeupDetail = await fedinQueryRequest("api/supabase/query", queryCodeUp);
|
|
416
|
+
if (!codeupDetail.length) {
|
|
417
|
+
throw new Error("当前项目仓库不存在,请先创建仓库");
|
|
418
|
+
}
|
|
419
|
+
repositoryId = codeupDetail[0].repositoryId;
|
|
420
|
+
if (!repositoryId) {
|
|
421
|
+
throw new Error("当前项目仓库不存在,请先到飞钉AI推送项目代码");
|
|
422
|
+
}
|
|
413
423
|
// 拉取之前 需要将当前projectPath路径下的所有代码文件清空
|
|
414
424
|
await clearProjectPath(currntProjectPath);
|
|
415
425
|
const allRemoteFilesPath = await listFilesFunc(organizationId, repositoryId, undefined, "master", "RECURSIVE");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { yunxiaoRequest, buildUrl, handleRepositoryIdEncoding } from "../../common/utils.js";
|
|
2
2
|
import { RepositorySchema } from "./types.js";
|
|
3
|
+
import { fedinQueryRequest } from "../../common/utils.js";
|
|
3
4
|
/**
|
|
4
5
|
* 创建仓库
|
|
5
6
|
* @param organizationId
|
|
@@ -70,5 +71,15 @@ export async function listRepositoriesFunc(organizationId, page, perPage, orderB
|
|
|
70
71
|
if (!Array.isArray(response)) {
|
|
71
72
|
return [];
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
+
const query = `SELECT cu."repositoryId"
|
|
75
|
+
FROM codeup cu
|
|
76
|
+
JOIN codeup_tokens ct ON cu.user_id = ct.user_id
|
|
77
|
+
WHERE ct.token = '${process.env.FEDIN_ACCESS_TOKEN}' AND cu."repositoryId" IS NOT NULL;`;
|
|
78
|
+
const projects = await fedinQueryRequest("api/supabase/query", query);
|
|
79
|
+
if (!projects.length) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const availableProjectIds = projects.map((project) => Number(project.repositoryId));
|
|
83
|
+
const availableRepositories = response.filter((repo) => availableProjectIds.includes(repo.id));
|
|
84
|
+
return availableRepositories.map((repo) => RepositorySchema.parse(repo));
|
|
74
85
|
}
|
|
@@ -266,12 +266,14 @@ export const CreateFileSchema = z.object({
|
|
|
266
266
|
});
|
|
267
267
|
export const PushFilesSchema = z.object({
|
|
268
268
|
organizationId: z.string().describe("Organization ID, can be found in the basic information page of the organization admin console"),
|
|
269
|
-
|
|
269
|
+
currntProjectPath: z.string().describe("The project path, and it must be an absolute path."),
|
|
270
|
+
projectId: z.string().describe("Project ID. Note that this is not the repository ID, but the project ID. The project ID is obtained from the user’s message. If the user’s message does not mention it, then it is retrieved from the projectName field in the package.json file."),
|
|
270
271
|
commitMessage: z.string().describe("Commit message, not empty, no more than 102400 characters")
|
|
271
272
|
});
|
|
272
273
|
export const PullFilesSchema = z.object({
|
|
273
274
|
organizationId: z.string().describe("Organization ID, can be found in the basic information page of the organization admin console"),
|
|
274
275
|
currntProjectPath: z.string().describe("The absolute local path of the current project, and it must be an absolute path."),
|
|
276
|
+
projectId: z.string().describe("Project ID, the ID of the project corresponding to the repository to be fetched.")
|
|
275
277
|
});
|
|
276
278
|
export const UpdateFileSchema = z.object({
|
|
277
279
|
organizationId: z.string().describe("Organization ID, can be found in the basic information page of the organization admin console"),
|
|
@@ -52,8 +52,8 @@ export const handleCodeManagementTools = async (request) => {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
case "push_files": {
|
|
55
|
-
const args = request.params.arguments;
|
|
56
|
-
const result = await files.pushFilesFunc(args.organizationId, args.
|
|
55
|
+
const args = types.PushFilesSchema.parse(request.params.arguments);
|
|
56
|
+
const result = await files.pushFilesFunc(args.organizationId, args.projectId, args.currntProjectPath, args.commitMessage);
|
|
57
57
|
const returnText = `项目部署地址:${result.onlineUrl}\n文件推送结果:${JSON.stringify(result.pushResult)}
|
|
58
58
|
`;
|
|
59
59
|
return {
|
|
@@ -61,8 +61,8 @@ export const handleCodeManagementTools = async (request) => {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
case "pull_files": {
|
|
64
|
-
const args = request.params.arguments;
|
|
65
|
-
const result = await files.pullFilesFunc(args.organizationId, args.
|
|
64
|
+
const args = types.PullFilesSchema.parse(request.params.arguments);
|
|
65
|
+
const result = await files.pullFilesFunc(args.organizationId, args.projectId, args.currntProjectPath);
|
|
66
66
|
return {
|
|
67
67
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
68
68
|
};
|
|
@@ -12,4 +12,20 @@ export const getCodeManagementTools = () => [
|
|
|
12
12
|
describe: "[Code Management] Synchronize the latest code from a Codeup repository, ensuring local project alignment with remote repository state. Use when: User needs to update local codebase with the most recent changes from the remote repository.",
|
|
13
13
|
inputSchema: zodToJsonSchema(types.PullFilesSchema),
|
|
14
14
|
},
|
|
15
|
+
{
|
|
16
|
+
name: "get_repository",
|
|
17
|
+
description: "[Code Management] Get information about a Codeup repository",
|
|
18
|
+
inputSchema: zodToJsonSchema(types.GetRepositorySchema),
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "list_repositories",
|
|
22
|
+
description: "[Code Management] Get the CodeUp Repository List.\n" +
|
|
23
|
+
"\n" +
|
|
24
|
+
"A Repository serves as a unit for managing source code and is distinct from a Project.\n" +
|
|
25
|
+
"\n" +
|
|
26
|
+
"Use Case:\n" +
|
|
27
|
+
"\n" +
|
|
28
|
+
"View my repositories",
|
|
29
|
+
inputSchema: zodToJsonSchema(types.ListRepositoriesSchema),
|
|
30
|
+
},
|
|
15
31
|
];
|