@perfai/mcp 1.0.24
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/LICENSE +21 -0
- package/README.md +352 -0
- package/dist/auth/authManager.d.ts +83 -0
- package/dist/auth/authManager.d.ts.map +1 -0
- package/dist/auth/authManager.js +555 -0
- package/dist/auth/sessionCache.d.ts +5 -0
- package/dist/auth/sessionCache.d.ts.map +1 -0
- package/dist/auth/sessionCache.js +29 -0
- package/dist/auth/sessionStorage.d.ts +53 -0
- package/dist/auth/sessionStorage.d.ts.map +1 -0
- package/dist/auth/sessionStorage.js +234 -0
- package/dist/auth/types.d.ts +28 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +1 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +74 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +144 -0
- package/dist/setup-config.d.ts +4 -0
- package/dist/setup-config.d.ts.map +1 -0
- package/dist/setup-config.js +69 -0
- package/dist/tools/index.d.ts +361 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +275 -0
- package/dist/tools/protected/aiFixDesignIssue.d.ts +17 -0
- package/dist/tools/protected/aiFixDesignIssue.d.ts.map +1 -0
- package/dist/tools/protected/aiFixDesignIssue.js +205 -0
- package/dist/tools/protected/aiFixQualityIssue.d.ts +17 -0
- package/dist/tools/protected/aiFixQualityIssue.d.ts.map +1 -0
- package/dist/tools/protected/aiFixQualityIssue.js +188 -0
- package/dist/tools/protected/aiFixSecurityIssue.d.ts +17 -0
- package/dist/tools/protected/aiFixSecurityIssue.d.ts.map +1 -0
- package/dist/tools/protected/aiFixSecurityIssue.js +205 -0
- package/dist/tools/protected/checkDesignFixes.d.ts +17 -0
- package/dist/tools/protected/checkDesignFixes.d.ts.map +1 -0
- package/dist/tools/protected/checkDesignFixes.js +199 -0
- package/dist/tools/protected/checkQualityFixes.d.ts +17 -0
- package/dist/tools/protected/checkQualityFixes.d.ts.map +1 -0
- package/dist/tools/protected/checkQualityFixes.js +199 -0
- package/dist/tools/protected/checkSecurityFixes.d.ts +17 -0
- package/dist/tools/protected/checkSecurityFixes.d.ts.map +1 -0
- package/dist/tools/protected/checkSecurityFixes.js +177 -0
- package/dist/tools/protected/listApis.d.ts +28 -0
- package/dist/tools/protected/listApis.d.ts.map +1 -0
- package/dist/tools/protected/listApis.js +102 -0
- package/dist/tools/protected/logout.d.ts +11 -0
- package/dist/tools/protected/logout.d.ts.map +1 -0
- package/dist/tools/protected/logout.js +22 -0
- package/dist/tools/protected/manageOrganizations.d.ts +26 -0
- package/dist/tools/protected/manageOrganizations.d.ts.map +1 -0
- package/dist/tools/protected/manageOrganizations.js +147 -0
- package/dist/tools/protected/runDesignTest.d.ts +21 -0
- package/dist/tools/protected/runDesignTest.d.ts.map +1 -0
- package/dist/tools/protected/runDesignTest.js +132 -0
- package/dist/tools/protected/runQualityTest.d.ts +21 -0
- package/dist/tools/protected/runQualityTest.d.ts.map +1 -0
- package/dist/tools/protected/runQualityTest.js +150 -0
- package/dist/tools/protected/runSecurityTest.d.ts +21 -0
- package/dist/tools/protected/runSecurityTest.d.ts.map +1 -0
- package/dist/tools/protected/runSecurityTest.js +107 -0
- package/dist/tools/protected/selectApi.d.ts +24 -0
- package/dist/tools/protected/selectApi.d.ts.map +1 -0
- package/dist/tools/protected/selectApi.js +172 -0
- package/dist/tools/protected/setup.d.ts +11 -0
- package/dist/tools/protected/setup.d.ts.map +1 -0
- package/dist/tools/protected/setup.js +151 -0
- package/dist/tools/protected/showDesignIssues.d.ts +38 -0
- package/dist/tools/protected/showDesignIssues.d.ts.map +1 -0
- package/dist/tools/protected/showDesignIssues.js +201 -0
- package/dist/tools/protected/showFixedIssues.d.ts +11 -0
- package/dist/tools/protected/showFixedIssues.d.ts.map +1 -0
- package/dist/tools/protected/showFixedIssues.js +36 -0
- package/dist/tools/protected/showQualityIssues.d.ts +33 -0
- package/dist/tools/protected/showQualityIssues.d.ts.map +1 -0
- package/dist/tools/protected/showQualityIssues.js +225 -0
- package/dist/tools/protected/showSecurityIssues.d.ts +47 -0
- package/dist/tools/protected/showSecurityIssues.d.ts.map +1 -0
- package/dist/tools/protected/showSecurityIssues.js +212 -0
- package/dist/tools/protected/summarizeIssues.d.ts +11 -0
- package/dist/tools/protected/summarizeIssues.d.ts.map +1 -0
- package/dist/tools/protected/summarizeIssues.js +161 -0
- package/dist/tools/protected/userInfo.d.ts +11 -0
- package/dist/tools/protected/userInfo.d.ts.map +1 -0
- package/dist/tools/protected/userInfo.js +21 -0
- package/dist/tools/protected/visionAiAppLearning.d.ts +37 -0
- package/dist/tools/protected/visionAiAppLearning.d.ts.map +1 -0
- package/dist/tools/protected/visionAiAppLearning.js +122 -0
- package/dist/tools/public/authStatus.d.ts +11 -0
- package/dist/tools/public/authStatus.d.ts.map +1 -0
- package/dist/tools/public/authStatus.js +78 -0
- package/dist/tools/public/login.d.ts +12 -0
- package/dist/tools/public/login.d.ts.map +1 -0
- package/dist/tools/public/login.js +230 -0
- package/dist/types/api.d.ts +12 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +1 -0
- package/dist/utils/dockerRunner.d.ts +44 -0
- package/dist/utils/dockerRunner.d.ts.map +1 -0
- package/dist/utils/dockerRunner.js +300 -0
- package/dist/utils/formatters.d.ts +14 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +510 -0
- package/dist/utils/promptBuilder.d.ts +4 -0
- package/dist/utils/promptBuilder.d.ts.map +1 -0
- package/dist/utils/promptBuilder.js +132 -0
- package/package.json +67 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { runDockerQualityTest } from "../../utils/dockerRunner.js";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { API_ENDPOINTS } from "../../config.js";
|
|
4
|
+
export const runQualityTestTool = {
|
|
5
|
+
name: "run_quality_test",
|
|
6
|
+
description: "š Run comprehensive quality/specification validation analysis test using Docker (requires authentication and APP selection)",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
spec_url: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "URL to OpenAPI spec"
|
|
13
|
+
},
|
|
14
|
+
local_base_path: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Base URL of your basepath"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
required: ["spec_url", "local_base_path"],
|
|
20
|
+
additionalProperties: true,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export async function handleRunQualityTest(authManager, args) {
|
|
24
|
+
try {
|
|
25
|
+
const specUrl = args.spec_url;
|
|
26
|
+
const localBasePath = args.local_base_path;
|
|
27
|
+
const accessToken = authManager.getAccessToken();
|
|
28
|
+
const tokenType = authManager.getTokenType();
|
|
29
|
+
const orgId = authManager.getSelectedOrgId();
|
|
30
|
+
const selectedAppId = authManager.getSelectedApiId();
|
|
31
|
+
if (!accessToken) {
|
|
32
|
+
throw new Error('No access token available');
|
|
33
|
+
}
|
|
34
|
+
if (!orgId) {
|
|
35
|
+
throw new Error('No organization selected. Please select an organization first using manage_organizations tool.');
|
|
36
|
+
}
|
|
37
|
+
if (!selectedAppId) {
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
text: `ā **No APP Selected**\n\nšÆ **Required**: You must select an APP first to run quality tests.\n\nš **Steps**:\n1. Run \`list_apps\` to see available APPs\n2. Run \`select_app\` with an APP ID\n3. Run \`show_quality_issues\` to get the quality app ID\n4. Then run this tool again`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// First, get the app details using the catalog ID to get the quality app ID
|
|
48
|
+
const catalogUrl = `${API_ENDPOINTS.API_CATALOG_SERVICE_IDS}/${selectedAppId}`;
|
|
49
|
+
let appDetails;
|
|
50
|
+
try {
|
|
51
|
+
const catalogResponse = await axios.get(catalogUrl, {
|
|
52
|
+
headers: {
|
|
53
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'X-Org-ID': orgId
|
|
56
|
+
},
|
|
57
|
+
timeout: 30000
|
|
58
|
+
});
|
|
59
|
+
if (catalogResponse.status !== 200 || !catalogResponse.data) {
|
|
60
|
+
throw new Error(`Failed to fetch app details with status ${catalogResponse.status}`);
|
|
61
|
+
}
|
|
62
|
+
appDetails = catalogResponse.data;
|
|
63
|
+
if (!appDetails.quality_test_appId) {
|
|
64
|
+
throw new Error('No quality_test_appId found for this app');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: `ā **Error fetching app details**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure the selected app has quality testing enabled.`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Use the quality_test_appId for running quality tests
|
|
79
|
+
const qualityAppId = appDetails.quality_test_appId;
|
|
80
|
+
// Store the quality app ID for use in other tools
|
|
81
|
+
authManager.setSelectedQualityAppId(qualityAppId);
|
|
82
|
+
if (!specUrl) {
|
|
83
|
+
throw new Error('Spec URL is required');
|
|
84
|
+
}
|
|
85
|
+
if (!localBasePath) {
|
|
86
|
+
throw new Error('Local base path is required');
|
|
87
|
+
}
|
|
88
|
+
// Validate URLs
|
|
89
|
+
try {
|
|
90
|
+
new URL(specUrl);
|
|
91
|
+
new URL(localBasePath);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw new Error('Invalid URL format. Please provide valid URLs for spec_url and local_base_path');
|
|
95
|
+
}
|
|
96
|
+
// Show test start information
|
|
97
|
+
const appDisplayName = appDetails.api_name || selectedAppId;
|
|
98
|
+
const startMessage = `š **Starting Quality Test**\n\nšÆ **APP**: ${appDisplayName}\nš **Quality App ID**: ${qualityAppId}\nš **Spec URL**: ${specUrl}\nš **Base Path**: ${localBasePath}\n\nā³ **Running comprehensive quality/specification validation analysis...**\n\nThis may take several minutes. Please wait...`;
|
|
99
|
+
// Return immediate response with test info
|
|
100
|
+
const immediateResponse = {
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: "text",
|
|
104
|
+
text: startMessage,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
// Run the Docker quality test in the background
|
|
109
|
+
const testPromise = runDockerQualityTest({
|
|
110
|
+
apiId: qualityAppId,
|
|
111
|
+
orgId: orgId,
|
|
112
|
+
accessToken: accessToken,
|
|
113
|
+
specUrl: specUrl,
|
|
114
|
+
localBasePath: localBasePath,
|
|
115
|
+
});
|
|
116
|
+
// Wait for the test to complete
|
|
117
|
+
const result = await testPromise;
|
|
118
|
+
if (result.success) {
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: `ā
**Quality Test Completed Successfully**\n\nšÆ **APP**: ${appDisplayName}\nš **Quality App ID**: ${qualityAppId}\nš **Spec URL**: ${specUrl}\nš **Base Path**: ${localBasePath}\n\nš **Test Results**:\n\`\`\`\n${result.output}\n\`\`\`\n\nš **Quality analysis completed!** New quality issues may have been detected.\n\nš” **Next Steps**:\n⢠Run \`show_quality_issues\` to see the latest quality issues\n⢠Run \`check_quality_fixes\` to verify if any previous fixes were resolved\n⢠Use \`ai_fix_quality_issue\` to get AI fixes for specific issues`,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
return {
|
|
130
|
+
content: [
|
|
131
|
+
{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: `ā **Quality Test Failed**\n\nšÆ **APP**: ${appDisplayName}\nš **Quality App ID**: ${qualityAppId}\nš **Spec URL**: ${specUrl}\nš **Base Path**: ${localBasePath}\n\nš“ **Error Details**:\n\`\`\`\n${result.errors}\n\`\`\`\n\nš **Output**:\n\`\`\`\n${result.output}\n\`\`\`\n\nš” **Troubleshooting**:\n⢠Check if Docker is running\n⢠Verify the spec URL is accessible\n⢠Ensure the base path is correct\n⢠Check if the app is running and accessible`,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
141
|
+
return {
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: "text",
|
|
145
|
+
text: `ā **Error running quality test**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated and have access to quality testing.\n\nš **Try**: Re-running the 'login' tool if the error persists.`,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const runSecurityTestTool: {
|
|
2
|
+
readonly name: "run_security_test";
|
|
3
|
+
readonly description: "š Run comprehensive security analysis test using Docker (requires authentication and APP selection)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly spec_url: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "URL to OpenAPI spec ";
|
|
10
|
+
};
|
|
11
|
+
readonly local_base_path: {
|
|
12
|
+
readonly type: "string";
|
|
13
|
+
readonly description: "Base URL of your basepath";
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
readonly required: readonly ["spec_url", "local_base_path"];
|
|
17
|
+
readonly additionalProperties: true;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare function handleRunSecurityTest(authManager: any, args: any): Promise<any>;
|
|
21
|
+
//# sourceMappingURL=runSecurityTest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runSecurityTest.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/runSecurityTest.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;CAkBtB,CAAC;AAEX,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAgGrF"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { runDockerSecurityTest } from "../../utils/dockerRunner.js";
|
|
2
|
+
export const runSecurityTestTool = {
|
|
3
|
+
name: "run_security_test",
|
|
4
|
+
description: "š Run comprehensive security analysis test using Docker (requires authentication and APP selection)",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
spec_url: {
|
|
9
|
+
type: "string",
|
|
10
|
+
description: "URL to OpenAPI spec "
|
|
11
|
+
},
|
|
12
|
+
local_base_path: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Base URL of your basepath"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
required: ["spec_url", "local_base_path"],
|
|
18
|
+
additionalProperties: true,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export async function handleRunSecurityTest(authManager, args) {
|
|
22
|
+
try {
|
|
23
|
+
const specUrl = args.spec_url;
|
|
24
|
+
const localBasePath = args.local_base_path;
|
|
25
|
+
const accessToken = authManager.getAccessToken();
|
|
26
|
+
const orgId = authManager.getSelectedOrgId();
|
|
27
|
+
const selectedAppId = authManager.getSelectedApiId();
|
|
28
|
+
const securityAppId = authManager.getSelectedSecurityAppId();
|
|
29
|
+
if (!accessToken) {
|
|
30
|
+
throw new Error('No access token available');
|
|
31
|
+
}
|
|
32
|
+
if (!orgId) {
|
|
33
|
+
throw new Error('No organization selected. Please select an organization first using manage_organizations tool.');
|
|
34
|
+
}
|
|
35
|
+
if (!selectedAppId) {
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: `ā **No APP Selected**\n\nšÆ **Required**: You must select an APP first to run security tests.\n\nš **Steps**:\n1. Run \`list_apps\` to see available APPs\n2. Run \`select_app\` with an APP ID\n3. Run \`show_security_issues\` to get the security app ID\n4. Then run this tool again`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (!securityAppId) {
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: `ā **No Security App ID Available**\n\nšÆ **Required**: You must run \`show_security_issues\` first to get the security app ID.\n\nš **Steps**:\n1. Make sure you have an APP selected\n2. Run \`show_security_issues\` to fetch and store the security app ID\n3. Then run this tool again`,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (!specUrl) {
|
|
56
|
+
throw new Error('Spec URL is required');
|
|
57
|
+
}
|
|
58
|
+
if (!localBasePath) {
|
|
59
|
+
throw new Error('Local base path is required');
|
|
60
|
+
}
|
|
61
|
+
// Validate URLs
|
|
62
|
+
try {
|
|
63
|
+
new URL(specUrl);
|
|
64
|
+
new URL(localBasePath);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new Error('Invalid URL format. Please provide valid URLs for spec_url and local_base_path');
|
|
68
|
+
}
|
|
69
|
+
// Run Docker security test using the security app ID
|
|
70
|
+
console.error('š Running Docker security test...');
|
|
71
|
+
const result = await runDockerSecurityTest({
|
|
72
|
+
apiId: securityAppId,
|
|
73
|
+
orgId,
|
|
74
|
+
accessToken,
|
|
75
|
+
specUrl,
|
|
76
|
+
localBasePath
|
|
77
|
+
});
|
|
78
|
+
// Wait 40 seconds for the system to process and update security issues
|
|
79
|
+
console.error('ā³ Waiting 40 seconds for system to process security issues...');
|
|
80
|
+
await new Promise(resolve => setTimeout(resolve, 40000));
|
|
81
|
+
console.error('ā
Wait completed, security issues should be processed');
|
|
82
|
+
// Create response based on Docker test result
|
|
83
|
+
const dockerStatus = result.success ? 'ā
**Docker Test**: Completed Successfully' : 'ā **Docker Test**: Failed';
|
|
84
|
+
const dockerOutput = result.success
|
|
85
|
+
? `š **Test Output**:\n\`\`\`\n${result.output || 'Test completed - check your PerfAI dashboard for results'}\n\`\`\``
|
|
86
|
+
: `š **Error Output**:\n\`\`\`\n${result.errors || 'Unknown error occurred'}\n\`\`\``;
|
|
87
|
+
return {
|
|
88
|
+
content: [
|
|
89
|
+
{
|
|
90
|
+
type: "text",
|
|
91
|
+
text: `š **Security Test Results**\n\n${dockerStatus}\n\nšÆ **APP**: ${selectedAppId}\nš **Security App ID**: ${securityAppId}\nš **Spec URL**: ${specUrl}\nš **Base Path**: ${localBasePath}\nš¢ **Organization**: ${orgId}\n\n${dockerOutput}\n\nā³ **Processing Wait**: System waited 40 seconds after Docker test completion to ensure security issues were fully processed and updated.\n\nā
**Status**: Security analysis completed. Results should be available in your PerfAI dashboard.\n\nš” **Next Steps**:\n1. Check your PerfAI dashboard for detailed results\n2. Use \`show_security_issues\` to see any new issues found\n3. Use \`ai_fix_security_issue\` to generate fixes for remaining issues\n4. Use \`check_security_fixes\` to verify which previously fixed issues were actually resolved`,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: `ā **Error running security test**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated, have an organization selected, and provided valid URLs.\n\nš **Try**: Check your inputs and try again.`,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const selectApiTool: {
|
|
2
|
+
readonly name: "select_app";
|
|
3
|
+
readonly description: "šÆ Select an APP by sequence number, name/label, or app ID (requires authentication). Provide exactly one of: sequence, name_or_label, or app_id.";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly sequence: {
|
|
8
|
+
readonly type: "number";
|
|
9
|
+
readonly description: "Sequence number from the list_apps table";
|
|
10
|
+
};
|
|
11
|
+
readonly name_or_label: {
|
|
12
|
+
readonly type: "string";
|
|
13
|
+
readonly description: "App name or label ā case-insensitive partial match";
|
|
14
|
+
};
|
|
15
|
+
readonly app_id: {
|
|
16
|
+
readonly type: "string";
|
|
17
|
+
readonly description: "Exact app catalog ID";
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
readonly additionalProperties: false;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export declare function handleSelectApi(authManager: any, args: any): Promise<any>;
|
|
24
|
+
//# sourceMappingURL=selectApi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectApi.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/selectApi.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;CAqBhB,CAAC;AAEX,wBAAsB,eAAe,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAuK/E"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { API_ENDPOINTS } from "../../config.js";
|
|
3
|
+
export const selectApiTool = {
|
|
4
|
+
name: "select_app",
|
|
5
|
+
description: "šÆ Select an APP by sequence number, name/label, or app ID (requires authentication). Provide exactly one of: sequence, name_or_label, or app_id.",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
sequence: {
|
|
10
|
+
type: "number",
|
|
11
|
+
description: "Sequence number from the list_apps table"
|
|
12
|
+
},
|
|
13
|
+
name_or_label: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "App name or label ā case-insensitive partial match"
|
|
16
|
+
},
|
|
17
|
+
app_id: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description: "Exact app catalog ID"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
export async function handleSelectApi(authManager, args) {
|
|
26
|
+
try {
|
|
27
|
+
const { sequence, name_or_label, app_id: directAppId } = args;
|
|
28
|
+
const accessToken = authManager.getAccessToken();
|
|
29
|
+
const tokenType = authManager.getTokenType();
|
|
30
|
+
const orgId = authManager.getSelectedOrgId();
|
|
31
|
+
if (!accessToken) {
|
|
32
|
+
throw new Error('No access token available');
|
|
33
|
+
}
|
|
34
|
+
if (!orgId) {
|
|
35
|
+
throw new Error('No organization selected. Please select an organization first using manage_organizations tool.');
|
|
36
|
+
}
|
|
37
|
+
if (!sequence && !name_or_label && !directAppId) {
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
text: `ā **No Selection Provided**\n\nš“ **Error**: Provide one of: sequence, name_or_label, or app_id\n\nš” **Examples**:\n⢠\`select_app {"sequence": 1}\`\n⢠\`select_app {"name_or_label": "bookstore"}\`\n⢠\`select_app {"app_id": "6a2928c1dff4218c8e8ac947"}\`\n\nš **Tip**: Use 'list_apps' first to see available APPs.`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Fetch full APP list to resolve name/label/id lookups and validate
|
|
48
|
+
const response = await axios.get(API_ENDPOINTS.API_CATALOG, {
|
|
49
|
+
headers: {
|
|
50
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
'X-Org-ID': orgId
|
|
53
|
+
},
|
|
54
|
+
timeout: 30000
|
|
55
|
+
});
|
|
56
|
+
const apis = Array.isArray(response.data) ? response.data : (response.data?.data ?? response.data);
|
|
57
|
+
let selectedAppId;
|
|
58
|
+
let matchedBy = '';
|
|
59
|
+
if (sequence) {
|
|
60
|
+
// Resolve via the in-memory sequence map built by list_apps
|
|
61
|
+
selectedAppId = authManager.getAppIdBySequence(sequence);
|
|
62
|
+
if (!selectedAppId) {
|
|
63
|
+
const sequenceMap = authManager.getAppSequenceMap();
|
|
64
|
+
const available = sequenceMap ? Array.from(sequenceMap.keys()).sort((a, b) => a - b) : [];
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: `ā **Invalid Sequence Number**\n\nš“ **Error**: Sequence '${sequence}' not found\n\nš **Available sequences**: ${available.length > 0 ? available.join(', ') : 'None ā run list_apps first'}\n\nš” **Tip**: Run 'list_apps' to refresh the sequence table.`,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
matchedBy = `sequence ${sequence}`;
|
|
75
|
+
}
|
|
76
|
+
else if (directAppId) {
|
|
77
|
+
// Direct ID ā just validate it exists in this org
|
|
78
|
+
const found = apis.find((app) => app._id === directAppId);
|
|
79
|
+
if (!found) {
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: `ā **APP Not Found**\n\nš“ **Error**: No app with ID '${directAppId}' in this organization\n\nš” **Tip**: Use 'list_apps' to see valid app IDs.`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
selectedAppId = directAppId;
|
|
90
|
+
matchedBy = `app_id`;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// name_or_label ā case-insensitive partial match against label and api_name
|
|
94
|
+
const query = name_or_label.toLowerCase();
|
|
95
|
+
const matches = apis.filter((app) => app.label?.toLowerCase().includes(query) ||
|
|
96
|
+
app.api_name?.toLowerCase().includes(query));
|
|
97
|
+
if (matches.length === 0) {
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: `ā **No Match Found**\n\nš“ **Error**: No app matching '${name_or_label}'\n\nš” **Tip**: Use 'list_apps' to see all available apps and their names/labels.`,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (matches.length > 1) {
|
|
108
|
+
const matchList = matches.slice(0, 10).map((app, i) => ` ${i + 1}. "${app.label || ''}" / "${app.api_name || ''}" ā ID: ${app._id}`).join('\n');
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: `ā ļø **Multiple Matches (${matches.length})**\n\nQuery '${name_or_label}' matched multiple apps:\n${matchList}\n\nš” **Tip**: Use a more specific name, or use \`select_app {"app_id": "<id>"}\` to select exactly.`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
selectedAppId = matches[0]._id;
|
|
119
|
+
matchedBy = `name/label "${name_or_label}"`;
|
|
120
|
+
}
|
|
121
|
+
const selectedApp = apis.find((app) => app._id === selectedAppId);
|
|
122
|
+
if (!selectedApp) {
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: `ā **APP Not Found**\n\nš“ **Error**: APP '${selectedAppId}' not found in organization\n\nš” **Tip**: Use list_apps tool to see all available APPs`,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// Pick the best unique identifier (label preferred if unique, else name)
|
|
133
|
+
function processAppSelection(app) {
|
|
134
|
+
const { label, api_name } = app;
|
|
135
|
+
if (label) {
|
|
136
|
+
const labelsCount = apis.filter((a) => a.label === label).length;
|
|
137
|
+
if (labelsCount === 1)
|
|
138
|
+
return { type: "label", value: label };
|
|
139
|
+
}
|
|
140
|
+
if (api_name) {
|
|
141
|
+
const namesCount = apis.filter((a) => a.api_name === api_name).length;
|
|
142
|
+
if (namesCount === 1)
|
|
143
|
+
return { type: "name", value: api_name };
|
|
144
|
+
}
|
|
145
|
+
return { type: label ? "label" : "name", value: label || api_name };
|
|
146
|
+
}
|
|
147
|
+
const finalIdentifier = processAppSelection(selectedApp);
|
|
148
|
+
authManager.setSelectedApi(selectedAppId, selectedApp, finalIdentifier);
|
|
149
|
+
const displayText = selectedApp.label && selectedApp.api_name
|
|
150
|
+
? `${selectedApp.label} - ${selectedApp.api_name}`
|
|
151
|
+
: selectedApp.label || selectedApp.api_name || selectedAppId;
|
|
152
|
+
return {
|
|
153
|
+
content: [
|
|
154
|
+
{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: `ā
**APP Selected Successfully**\n\nšÆ **Selected**: ${displayText}\nš **ID**: ${selectedAppId}\nš **Matched by**: ${matchedBy}\nš·ļø **Identifier**: Using ${finalIdentifier.type}: ${finalIdentifier.value}\n\nš¢ **Organization**: ${orgId}\nš **Environment**: ${selectedApp.environment || 'Unknown'}\n\nš ļø **Ready**: You can now use this APP with other tools!`,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
163
|
+
return {
|
|
164
|
+
content: [
|
|
165
|
+
{
|
|
166
|
+
type: "text",
|
|
167
|
+
text: `ā **Error selecting APP**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated and have an organization selected.\n\nš **Try**: Using list_apps tool first to see available APPs.`,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const setupTool: {
|
|
2
|
+
readonly name: "setup";
|
|
3
|
+
readonly description: "š Setup organization and APP with default values (requires authentication)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {};
|
|
7
|
+
readonly additionalProperties: false;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare function handleSetup(authManager: any, args: any): Promise<any>;
|
|
11
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/setup.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS;;;;;;;;CAQZ,CAAC;AAEX,wBAAsB,WAAW,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CA6J3E"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
export const setupTool = {
|
|
3
|
+
name: "setup",
|
|
4
|
+
description: "š Setup organization and APP with default values (requires authentication)",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {},
|
|
8
|
+
additionalProperties: false,
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
export async function handleSetup(authManager, args) {
|
|
12
|
+
try {
|
|
13
|
+
const userInfo = authManager.getUserInfo();
|
|
14
|
+
const accessToken = authManager.getAccessToken();
|
|
15
|
+
const tokenType = authManager.getTokenType();
|
|
16
|
+
if (!accessToken) {
|
|
17
|
+
throw new Error('No access token available');
|
|
18
|
+
}
|
|
19
|
+
// Step 1: Get organizations and set the first/default one
|
|
20
|
+
let selectedOrgId = authManager.getSelectedOrgId();
|
|
21
|
+
let selectedOrgName = 'Unknown';
|
|
22
|
+
if (!selectedOrgId) {
|
|
23
|
+
try {
|
|
24
|
+
console.error('š¢ Fetching organizations for setup...');
|
|
25
|
+
const orgResponse = await axios.get('https://api.perfai.ai/api/v1/org-users/get_users_orgs', {
|
|
26
|
+
headers: { Authorization: `${tokenType} ${accessToken}` }
|
|
27
|
+
});
|
|
28
|
+
const organizations = orgResponse.data.data?.userOrgs || [];
|
|
29
|
+
const currentOrgId = orgResponse.data.data?.currentOrg;
|
|
30
|
+
if (organizations.length === 0) {
|
|
31
|
+
throw new Error('No organizations found. Please check your account permissions.');
|
|
32
|
+
}
|
|
33
|
+
// Use current org from APP, or fallback to first available
|
|
34
|
+
selectedOrgId = currentOrgId || organizations[0]?.org_id;
|
|
35
|
+
selectedOrgName = organizations.find((org) => org.org_id === selectedOrgId)?.orgDetails?.name || 'Unknown';
|
|
36
|
+
// Update session with fresh org data
|
|
37
|
+
if (authManager.session) {
|
|
38
|
+
authManager.session.organizations = organizations;
|
|
39
|
+
authManager.session.selectedOrgId = selectedOrgId;
|
|
40
|
+
}
|
|
41
|
+
console.error(`ā
Selected organization: ${selectedOrgName} (${selectedOrgId})`);
|
|
42
|
+
}
|
|
43
|
+
catch (orgError) {
|
|
44
|
+
throw new Error(`Failed to fetch organizations: ${orgError instanceof Error ? orgError.message : String(orgError)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Get org name for display
|
|
49
|
+
const organizations = authManager.getOrganizations();
|
|
50
|
+
const selectedOrg = organizations.find((org) => org.org_id === selectedOrgId);
|
|
51
|
+
selectedOrgName = selectedOrg?.orgDetails?.name || selectedOrgId;
|
|
52
|
+
}
|
|
53
|
+
// Step 2: Get APPs and set the first/default one
|
|
54
|
+
let selectedAppId = authManager.getSelectedApiId();
|
|
55
|
+
let selectedAppName = 'Unknown';
|
|
56
|
+
let selectedAppData = null;
|
|
57
|
+
if (!selectedAppId) {
|
|
58
|
+
try {
|
|
59
|
+
console.error('š Fetching APPs for setup...');
|
|
60
|
+
const apiResponse = await axios.get('https://api.perfai.ai/api/v1/api-catalog/apps/catalog_id_name_label', {
|
|
61
|
+
headers: {
|
|
62
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
'X-Org-ID': selectedOrgId
|
|
65
|
+
},
|
|
66
|
+
timeout: 30000
|
|
67
|
+
});
|
|
68
|
+
const apis = apiResponse.data || [];
|
|
69
|
+
if (apis.length === 0) {
|
|
70
|
+
return {
|
|
71
|
+
content: [
|
|
72
|
+
{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: `š **Setup Complete (Partial)**\n\nā
**Organization**: ${selectedOrgName}\nš **Org ID**: ${selectedOrgId}\n\nā ļø **APP**: No APPs found in this organization\n\nš” **Next Steps**:\n1. Register an APP in your PerfAI dashboard\n2. Use 'list_apps' to see available APPs\n3. Use 'select_app' to choose a specific APP\n\nš ļø **Ready**: You can now use organization-specific tools!`,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Select the first APP as default
|
|
80
|
+
selectedAppId = apis[0]._id;
|
|
81
|
+
selectedAppData = apis[0];
|
|
82
|
+
selectedAppName = selectedAppData.label || selectedAppData.api_name || 'Unnamed APP';
|
|
83
|
+
// Process APP selection with unique identifier logic
|
|
84
|
+
const { label, api_name } = selectedAppData;
|
|
85
|
+
let finalIdentifier;
|
|
86
|
+
// Check if label exists and is unique
|
|
87
|
+
if (label) {
|
|
88
|
+
const labelsCount = apis.filter((a) => a.label === label).length;
|
|
89
|
+
if (labelsCount === 1) {
|
|
90
|
+
finalIdentifier = { type: "label", value: label };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Check if api_name is unique
|
|
94
|
+
if (!finalIdentifier && api_name) {
|
|
95
|
+
const namesCount = apis.filter((a) => a.api_name === api_name).length;
|
|
96
|
+
if (namesCount === 1) {
|
|
97
|
+
finalIdentifier = { type: "name", value: api_name };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// If neither is unique, use the first available
|
|
101
|
+
if (!finalIdentifier) {
|
|
102
|
+
finalIdentifier = {
|
|
103
|
+
type: label ? "label" : "name",
|
|
104
|
+
value: label || api_name,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Save APP selection to session
|
|
108
|
+
authManager.setSelectedApi(selectedAppId, selectedAppData, finalIdentifier);
|
|
109
|
+
console.error(`ā
Selected APP: ${selectedAppName} (${selectedAppId})`);
|
|
110
|
+
}
|
|
111
|
+
catch (apiError) {
|
|
112
|
+
return {
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: `š **Setup Complete (Partial)**\n\nā
**Organization**: ${selectedOrgName}\nš **Org ID**: ${selectedOrgId}\n\nā **APP Error**: ${apiError instanceof Error ? apiError.message : String(apiError)}\n\nš” **Next Steps**:\n1. Check your organization has APPs registered\n2. Use 'list_apps' to see available APPs\n3. Use 'select_app' to choose a specific APP\n\nš ļø **Ready**: You can now use organization-specific tools!`,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Get APP info for display
|
|
124
|
+
selectedAppData = authManager.getSelectedApiData();
|
|
125
|
+
selectedAppName = selectedAppData?.label || selectedAppData?.api_name || selectedAppId;
|
|
126
|
+
}
|
|
127
|
+
// Step 3: Return success message
|
|
128
|
+
const displayText = selectedAppData?.label && selectedAppData?.api_name
|
|
129
|
+
? `${selectedAppData.label} - ${selectedAppData.api_name}`
|
|
130
|
+
: selectedAppName;
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: `š **Setup Complete!**\n\nā
**Organization**: ${selectedOrgName}\nš **Org ID**: ${selectedOrgId}\n\nā
**APP**: ${displayText}\nš **APP ID**: ${selectedAppId}\n\nš ļø **Ready**: You can now use all PerfAI tools!\n\nš **Available Commands**:\n⢠show_security_issues - View all security issues\n⢠ai_fix_security_issue - Generate AI fixes\n⢠run_security_test - Run security tests\n⢠manage_organizations - Switch organizations\n⢠list_apps - View all APPs\n⢠select_app - Switch APPs\n\nš **Setup successful!** All tools are now configured and ready to use.`,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: `ā **Setup Failed**\n\nš“ **Error**: ${errorMessage}\n\nš” **Troubleshooting**:\n1. Make sure you're authenticated (run 'login' first)\n2. Check your account has organization access\n3. Verify your internet connection\n\nš **Try**: Run 'login' first, then try 'setup' again`,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|