@smartbear/mcp 0.12.0 → 0.13.1
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 +30 -6
- package/dist/bugsnag/client/api/CurrentUser.js +50 -26
- package/dist/bugsnag/client/api/Error.js +158 -57
- package/dist/bugsnag/client/api/Project.js +398 -243
- package/dist/bugsnag/client/api/api.js +4087 -3837
- package/dist/bugsnag/client/api/base.js +155 -173
- package/dist/bugsnag/client/api/configuration.js +28 -25
- package/dist/bugsnag/client/filters.js +11 -20
- package/dist/bugsnag/client.js +1398 -1277
- package/dist/bugsnag/input-schemas.js +39 -57
- package/dist/collaborator/client.js +335 -371
- package/dist/common/bugsnag.js +5 -3
- package/dist/common/cache.js +50 -57
- package/dist/common/client-registry.js +106 -119
- package/dist/common/info.js +7 -3
- package/dist/common/register-clients.js +0 -16
- package/dist/common/server.js +270 -228
- package/dist/common/tools.js +19 -0
- package/dist/common/transport-http.js +252 -343
- package/dist/common/transport-stdio.js +40 -37
- package/dist/common/zod-utils.js +20 -0
- package/dist/index.js +18 -23
- package/dist/package.json.js +11 -0
- package/dist/pactflow/client/ai.js +142 -169
- package/dist/pactflow/client/base.js +41 -51
- package/dist/pactflow/client/prompt-utils.js +93 -84
- package/dist/pactflow/client/prompts.js +95 -92
- package/dist/pactflow/client/tools.js +94 -83
- package/dist/pactflow/client/utils.js +60 -64
- package/dist/pactflow/client.js +399 -320
- package/dist/qmetry/client/api/client-api.js +43 -41
- package/dist/qmetry/client/api/error-handler.js +264 -310
- package/dist/qmetry/client/auto-resolve.js +78 -99
- package/dist/qmetry/client/automation.js +139 -162
- package/dist/qmetry/client/handlers.js +49 -46
- package/dist/qmetry/client/issues.js +133 -115
- package/dist/qmetry/client/project.js +153 -174
- package/dist/qmetry/client/requirement.js +82 -70
- package/dist/qmetry/client/testcase.js +240 -208
- package/dist/qmetry/client/testsuite.js +332 -293
- package/dist/qmetry/client/tools/automation-tools.js +291 -288
- package/dist/qmetry/client/tools/index.js +16 -13
- package/dist/qmetry/client/tools/issue-tools.js +534 -543
- package/dist/qmetry/client/tools/project-tools.js +635 -656
- package/dist/qmetry/client/tools/requirement-tools.js +525 -528
- package/dist/qmetry/client/tools/testcase-tools.js +773 -786
- package/dist/qmetry/client/tools/testsuite-tools.js +1069 -1083
- package/dist/qmetry/client/utils.js +8 -14
- package/dist/qmetry/client.js +111 -109
- package/dist/qmetry/config/constants.js +48 -44
- package/dist/qmetry/config/rest-endpoints.js +51 -48
- package/dist/qmetry/types/automation.js +7 -7
- package/dist/qmetry/types/common.js +763 -1049
- package/dist/qmetry/types/issues.js +26 -19
- package/dist/qmetry/types/project.js +32 -25
- package/dist/qmetry/types/requirements.js +26 -21
- package/dist/qmetry/types/testcase.js +55 -44
- package/dist/qmetry/types/testsuite.js +66 -52
- package/dist/reflect/client.js +284 -226
- package/dist/swagger/client/api.js +645 -662
- package/dist/swagger/client/configuration.js +31 -33
- package/dist/swagger/client/portal-types.js +204 -244
- package/dist/swagger/client/registry-types.js +62 -96
- package/dist/swagger/client/tools.js +148 -158
- package/dist/swagger/client/user-management-types.js +11 -22
- package/dist/swagger/client.js +143 -135
- package/dist/swagger/config-utils.js +10 -16
- package/dist/zephyr/client.js +43 -42
- package/dist/zephyr/common/api-client.js +35 -30
- package/dist/zephyr/common/auth-service.js +16 -13
- package/dist/zephyr/common/rest-api-schemas.js +3173 -5146
- package/dist/zephyr/tool/environment/get-environments.js +66 -66
- package/dist/zephyr/tool/priority/get-priorities.js +41 -41
- package/dist/zephyr/tool/project/get-project.js +37 -37
- package/dist/zephyr/tool/project/get-projects.js +46 -46
- package/dist/zephyr/tool/status/get-statuses.js +47 -47
- package/dist/zephyr/tool/test-case/get-test-case.js +37 -37
- package/dist/zephyr/tool/test-case/get-test-cases.js +62 -62
- package/dist/zephyr/tool/test-cycle/get-test-cycle.js +37 -37
- package/dist/zephyr/tool/test-cycle/get-test-cycles.js +70 -70
- package/dist/zephyr/tool/test-execution/get-test-execution.js +37 -37
- package/dist/zephyr/tool/test-execution/get-test-executions.js +43 -43
- package/package.json +5 -5
- package/dist/bugsnag/client/api/index.js +0 -6
- package/dist/common/types.js +0 -6
- package/dist/qmetry/client/tools/types.js +0 -1
- package/dist/swagger/client/index.js +0 -6
- package/dist/tests/unit/bugsnag/utils/factories.js +0 -86
- package/dist/zephyr/tool/zephyr-tool.js +0 -1
package/dist/pactflow/client.js
CHANGED
|
@@ -1,340 +1,419 @@
|
|
|
1
|
-
import
|
|
1
|
+
import zod__default from "zod";
|
|
2
2
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
|
|
3
|
-
import { ToolError
|
|
4
|
-
import { getOADMatcherRecommendations, getUserMatcherSelection
|
|
3
|
+
import { ToolError } from "../common/tools.js";
|
|
4
|
+
import { getOADMatcherRecommendations, getUserMatcherSelection } from "./client/prompt-utils.js";
|
|
5
5
|
import { PROMPTS } from "./client/prompts.js";
|
|
6
6
|
import { TOOLS } from "./client/tools.js";
|
|
7
|
-
const ConfigurationSchema =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
password: z.string().optional().describe("Password for Pact Broker"),
|
|
7
|
+
const ConfigurationSchema = zod__default.object({
|
|
8
|
+
base_url: zod__default.url().describe("Pact Broker or PactFlow base URL"),
|
|
9
|
+
token: zod__default.string().optional().describe(
|
|
10
|
+
"Bearer token for PactFlow authentication (use this OR username/password)"
|
|
11
|
+
),
|
|
12
|
+
username: zod__default.string().optional().describe("Username for Pact Broker"),
|
|
13
|
+
password: zod__default.string().optional().describe("Password for Pact Broker")
|
|
15
14
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
15
|
+
class PactflowClient {
|
|
16
|
+
name = "Contract Testing";
|
|
17
|
+
toolPrefix = "contract-testing";
|
|
18
|
+
configPrefix = "Pact-Broker";
|
|
19
|
+
config = ConfigurationSchema;
|
|
20
|
+
headers;
|
|
21
|
+
aiBaseUrl;
|
|
22
|
+
baseUrl;
|
|
23
|
+
_clientType;
|
|
24
|
+
_server;
|
|
25
|
+
get server() {
|
|
26
|
+
if (!this._server) throw new Error("Server not configured");
|
|
27
|
+
return this._server;
|
|
28
|
+
}
|
|
29
|
+
async configure(server, config) {
|
|
30
|
+
if (typeof config.token === "string") {
|
|
31
|
+
this.headers = {
|
|
32
|
+
Authorization: `Bearer ${config.token}`,
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
"User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
|
|
35
|
+
};
|
|
36
|
+
this._clientType = "pactflow";
|
|
37
|
+
} else if (typeof config.username === "string" && typeof config.password === "string") {
|
|
38
|
+
const authString = `${config.username}:${config.password}`;
|
|
39
|
+
this.headers = {
|
|
40
|
+
Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
"User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
|
|
43
|
+
};
|
|
44
|
+
this._clientType = "pact_broker";
|
|
45
|
+
} else {
|
|
46
|
+
return;
|
|
31
47
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
this.baseUrl = config.base_url;
|
|
49
|
+
this.aiBaseUrl = `${this.baseUrl}/api/ai`;
|
|
50
|
+
this._server = server.server;
|
|
51
|
+
}
|
|
52
|
+
isConfigured() {
|
|
53
|
+
return this.baseUrl !== void 0;
|
|
54
|
+
}
|
|
55
|
+
// PactFlow AI client methods
|
|
56
|
+
/**
|
|
57
|
+
* Generate new Pact tests based on the provided input.
|
|
58
|
+
*
|
|
59
|
+
* @param toolInput The input data for the generation process.
|
|
60
|
+
* @param getInput Function to get additional input from the user if needed.
|
|
61
|
+
* @returns The result of the generation process.
|
|
62
|
+
* @throws Error if the HTTP request fails or the operation times out.
|
|
63
|
+
*/
|
|
64
|
+
async generate(toolInput, getInput) {
|
|
65
|
+
if (toolInput.openapi?.document && (!toolInput.openapi?.matcher || Object.keys(toolInput.openapi.matcher).length === 0)) {
|
|
66
|
+
const matcherResponse = await getOADMatcherRecommendations(
|
|
67
|
+
toolInput.openapi.document,
|
|
68
|
+
this.server
|
|
69
|
+
);
|
|
70
|
+
const userSelection = await getUserMatcherSelection(
|
|
71
|
+
matcherResponse,
|
|
72
|
+
getInput
|
|
73
|
+
);
|
|
74
|
+
toolInput.openapi.matcher = userSelection;
|
|
36
75
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
const status_response = await this.submitHttpCallback(
|
|
77
|
+
"/generate",
|
|
78
|
+
toolInput
|
|
79
|
+
);
|
|
80
|
+
return await this.pollForCompletion(
|
|
81
|
+
status_response,
|
|
82
|
+
"Generation"
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Review the provided Pact tests and suggest improvements.
|
|
87
|
+
*
|
|
88
|
+
* @param toolInput The input data for the review process.
|
|
89
|
+
* @param getInput Function to get additional input from the user if needed.
|
|
90
|
+
* @returns The result of the review process.
|
|
91
|
+
* @throws Error if the HTTP request fails or the operation times out.
|
|
92
|
+
*/
|
|
93
|
+
async review(toolInput, getInput) {
|
|
94
|
+
if (toolInput.openapi?.document && (!toolInput.openapi?.matcher || Object.keys(toolInput.openapi.matcher).length === 0)) {
|
|
95
|
+
const matcherResponse = await getOADMatcherRecommendations(
|
|
96
|
+
toolInput.openapi.document,
|
|
97
|
+
this.server
|
|
98
|
+
);
|
|
99
|
+
const userSelection = await getUserMatcherSelection(
|
|
100
|
+
matcherResponse,
|
|
101
|
+
getInput
|
|
102
|
+
);
|
|
103
|
+
toolInput.openapi.matcher = userSelection;
|
|
64
104
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
const status_response = await this.submitHttpCallback("/review", toolInput);
|
|
106
|
+
return await this.pollForCompletion(
|
|
107
|
+
status_response,
|
|
108
|
+
"Review Pacts"
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Retrieve PactFlow AI entitlement information for the current user
|
|
113
|
+
* and organization when encountering 401 unauthorized errors.
|
|
114
|
+
* Use this to check AI entitlements and credits when AI operations fail.
|
|
115
|
+
*
|
|
116
|
+
* @returns Entitlement containing permissions, organization
|
|
117
|
+
* entitlements, and user entitlements.
|
|
118
|
+
* @throws Error if the request fails or returns a non-OK response.
|
|
119
|
+
*/
|
|
120
|
+
async checkAIEntitlements() {
|
|
121
|
+
return await this.fetchJson(`${this.aiBaseUrl}/entitlement`, {
|
|
122
|
+
method: "GET",
|
|
123
|
+
errorContext: "PactFlow AI Entitlements Request"
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async getStatus(statusUrl) {
|
|
127
|
+
const response = await fetch(statusUrl, {
|
|
128
|
+
method: "HEAD",
|
|
129
|
+
headers: this.headers
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
status: response.status,
|
|
133
|
+
isComplete: response.status === 200
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
get requestHeaders() {
|
|
137
|
+
return this.headers;
|
|
138
|
+
}
|
|
139
|
+
async getResult(resultUrl) {
|
|
140
|
+
const response = await fetch(resultUrl, {
|
|
141
|
+
method: "GET",
|
|
142
|
+
headers: this.headers
|
|
143
|
+
});
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
throw new ToolError(`HTTP error! status: ${response.status}`);
|
|
93
146
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const response = await fetch(`${this.aiBaseUrl}/review`, {
|
|
112
|
-
method: "POST",
|
|
113
|
-
headers: this.headers,
|
|
114
|
-
body: JSON.stringify(toolInput),
|
|
115
|
-
});
|
|
116
|
-
if (!response.ok) {
|
|
117
|
-
throw new ToolError(`HTTP error! status: ${response.status} - ${await response.text()}`);
|
|
118
|
-
}
|
|
119
|
-
const status_response = await response.json();
|
|
120
|
-
return await this.pollForCompletion(status_response, "Review Pacts");
|
|
147
|
+
return response.json();
|
|
148
|
+
}
|
|
149
|
+
async pollForCompletion(status_response, operationName) {
|
|
150
|
+
const startTime = Date.now();
|
|
151
|
+
const timeout = 12e4;
|
|
152
|
+
const pollInterval = 1e3;
|
|
153
|
+
while (Date.now() - startTime < timeout) {
|
|
154
|
+
const statusCheck = await this.getStatus(status_response.status_url);
|
|
155
|
+
if (statusCheck.isComplete) {
|
|
156
|
+
return await this.getResult(status_response.result_url);
|
|
157
|
+
}
|
|
158
|
+
if (statusCheck.status !== 202) {
|
|
159
|
+
throw new ToolError(
|
|
160
|
+
`${operationName} failed with status: ${statusCheck.status}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
121
164
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
165
|
+
throw new ToolError(
|
|
166
|
+
`${operationName} timed out after ${timeout / 1e3} seconds`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Helper method to fetch JSON data from an API endpoint.
|
|
171
|
+
*
|
|
172
|
+
* @param url The full URL to fetch from.
|
|
173
|
+
* @param options Options including method, body, and error context.
|
|
174
|
+
* @returns The parsed JSON response.
|
|
175
|
+
* @throws ToolError if the request fails.
|
|
176
|
+
*/
|
|
177
|
+
async fetchJson(url, options) {
|
|
178
|
+
const { method, body, errorContext = "Request" } = options;
|
|
179
|
+
try {
|
|
180
|
+
const response = await fetch(url, {
|
|
181
|
+
method,
|
|
182
|
+
headers: this.headers,
|
|
183
|
+
...body && { body: JSON.stringify(body) }
|
|
184
|
+
});
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
const errorText = await response.text().catch(() => "");
|
|
187
|
+
throw new ToolError(
|
|
188
|
+
`${errorContext} Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`,
|
|
189
|
+
void 0,
|
|
190
|
+
/* @__PURE__ */ new Map([["responseStatus", response.status]])
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return await response.json();
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (error instanceof ToolError) {
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
199
|
+
console.error(`[${errorContext}] Unexpected error: ${error}
|
|
200
|
+
`);
|
|
201
|
+
throw new ToolError(
|
|
202
|
+
`${errorContext} Failed - ${errorMessage}`,
|
|
203
|
+
void 0,
|
|
204
|
+
/* @__PURE__ */ new Map([["responseStatus", 500]])
|
|
205
|
+
);
|
|
148
206
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Submits an HTTP callback request to the PactFlow AI API.
|
|
210
|
+
* @param endpoint The AI API endpoint (relative to aiBaseUrl), e.g., '/generate' or '/review'.
|
|
211
|
+
* @param body The request body specific to the AI operation.
|
|
212
|
+
* @returns StatusResponse with status_url for polling and result_url for fetching results.
|
|
213
|
+
* @throws ToolError if the request fails.
|
|
214
|
+
*/
|
|
215
|
+
async submitHttpCallback(endpoint, body) {
|
|
216
|
+
return await this.fetchJson(
|
|
217
|
+
`${this.aiBaseUrl}${endpoint}`,
|
|
218
|
+
{
|
|
219
|
+
method: "POST",
|
|
220
|
+
body,
|
|
221
|
+
errorContext: `HTTP callback submission to ${endpoint}`
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
// PactFlow / Pact_Broker client methods
|
|
226
|
+
async getProviderStates({
|
|
227
|
+
provider
|
|
228
|
+
}) {
|
|
229
|
+
const uri_encoded_provider_name = encodeURIComponent(provider);
|
|
230
|
+
return await this.fetchJson(
|
|
231
|
+
`${this.baseUrl}/pacts/provider/${uri_encoded_provider_name}/provider-states`,
|
|
232
|
+
{
|
|
233
|
+
method: "GET",
|
|
234
|
+
errorContext: "Get Provider States"
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Checks if a given pacticipant version is safe to deploy
|
|
240
|
+
* to a specified environment.
|
|
241
|
+
*
|
|
242
|
+
* @param body - Input containing:
|
|
243
|
+
* - `pacticipant`: The name of the service (pacticipant).
|
|
244
|
+
* - `version`: The version of the pacticipant being evaluated for deployment.
|
|
245
|
+
* - `environment`: The target environment (e.g., staging, production).
|
|
246
|
+
* @returns CanIDeployResponse containing deployment decision and verification results.
|
|
247
|
+
* @throws Error if the request fails or returns a non-OK response.
|
|
248
|
+
*/
|
|
249
|
+
async canIDeploy(body) {
|
|
250
|
+
const { pacticipant, version, environment } = body;
|
|
251
|
+
const queryParams = new URLSearchParams({
|
|
252
|
+
pacticipant,
|
|
253
|
+
version,
|
|
254
|
+
environment
|
|
255
|
+
});
|
|
256
|
+
const url = `${this.baseUrl}/can-i-deploy?${queryParams.toString()}`;
|
|
257
|
+
return await this.fetchJson(url, {
|
|
258
|
+
method: "GET",
|
|
259
|
+
errorContext: "Can-I-Deploy Request"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Retrieves the matrix of pact verification results for the specified pacticipants.
|
|
264
|
+
* This allows you to see which consumer/provider combinations have been verified
|
|
265
|
+
* and make deployment decisions based on contract test results.
|
|
266
|
+
*
|
|
267
|
+
* @param body - Matrix query parameters including pacticipants, versions, environments, etc.
|
|
268
|
+
* @returns MatrixResponse containing the verification matrix, notices, and summary
|
|
269
|
+
* @throws Error if the request fails or returns a non-OK response
|
|
270
|
+
*/
|
|
271
|
+
async getMatrix(body) {
|
|
272
|
+
const { q, latestby, limit } = body;
|
|
273
|
+
const queryParts = [];
|
|
274
|
+
if (latestby) {
|
|
275
|
+
queryParts.push(`latestby=${encodeURIComponent(latestby)}`);
|
|
158
276
|
}
|
|
159
|
-
|
|
160
|
-
|
|
277
|
+
if (limit !== void 0) {
|
|
278
|
+
queryParts.push(`limit=${limit}`);
|
|
161
279
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
280
|
+
q.forEach((selector) => {
|
|
281
|
+
queryParts.push(
|
|
282
|
+
`q[]pacticipant=${encodeURIComponent(selector.pacticipant)}`
|
|
283
|
+
);
|
|
284
|
+
if (selector.version) {
|
|
285
|
+
queryParts.push(`q[]version=${encodeURIComponent(selector.version)}`);
|
|
286
|
+
}
|
|
287
|
+
if (selector.branch) {
|
|
288
|
+
queryParts.push(`q[]branch=${encodeURIComponent(selector.branch)}`);
|
|
289
|
+
}
|
|
290
|
+
if (selector.environment) {
|
|
291
|
+
queryParts.push(
|
|
292
|
+
`q[]environment=${encodeURIComponent(selector.environment)}`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (selector.latest !== void 0) {
|
|
296
|
+
queryParts.push(`q[]latest=${selector.latest}`);
|
|
297
|
+
}
|
|
298
|
+
if (selector.tag) {
|
|
299
|
+
queryParts.push(`q[]tag=${encodeURIComponent(selector.tag)}`);
|
|
300
|
+
}
|
|
301
|
+
if (selector.mainBranch !== void 0) {
|
|
302
|
+
queryParts.push(`q[]mainBranch=${selector.mainBranch}`);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
const url = `${this.baseUrl}/matrix?${queryParts.join("&")}`;
|
|
306
|
+
return await this.fetchJson(url, {
|
|
307
|
+
method: "GET",
|
|
308
|
+
errorContext: "Matrix Request"
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Retrieves metrics across the workspace.
|
|
313
|
+
*
|
|
314
|
+
* @returns MetricsResponse containing workspace-wide metrics
|
|
315
|
+
* @throws Error if the request fails or returns a non-OK response
|
|
316
|
+
*/
|
|
317
|
+
async getMetrics() {
|
|
318
|
+
const url = `${this.baseUrl}/metrics`;
|
|
319
|
+
try {
|
|
320
|
+
const response = await fetch(url, {
|
|
321
|
+
method: "GET",
|
|
322
|
+
headers: this.headers
|
|
323
|
+
});
|
|
324
|
+
if (!response.ok) {
|
|
325
|
+
const errorText = await response.text().catch(() => "");
|
|
326
|
+
throw new ToolError(
|
|
327
|
+
`Metrics Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
return await response.json();
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error("[GetMetrics] Unexpected error:", error);
|
|
333
|
+
throw error;
|
|
172
334
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
headers: this.headers,
|
|
198
|
-
});
|
|
199
|
-
if (!response.ok) {
|
|
200
|
-
throw new ToolError(`HTTP error! status: ${response.status} - ${await response.text()}`);
|
|
201
|
-
}
|
|
202
|
-
return response.json();
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Retrieves metrics for all teams.
|
|
338
|
+
*
|
|
339
|
+
* @returns TeamMetricsResponse containing metrics for all teams
|
|
340
|
+
* @throws Error if the request fails or returns a non-OK response
|
|
341
|
+
*/
|
|
342
|
+
async getTeamMetrics() {
|
|
343
|
+
const url = `${this.baseUrl}/metrics/teams`;
|
|
344
|
+
try {
|
|
345
|
+
const response = await fetch(url, {
|
|
346
|
+
method: "GET",
|
|
347
|
+
headers: this.headers
|
|
348
|
+
});
|
|
349
|
+
if (!response.ok) {
|
|
350
|
+
const errorText = await response.text().catch(() => "");
|
|
351
|
+
throw new ToolError(
|
|
352
|
+
`Team Metrics Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
return await response.json();
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error("[GetTeamMetrics] Unexpected error:", error);
|
|
358
|
+
throw error;
|
|
203
359
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const url = `${this.baseUrl}/can-i-deploy?${queryParams.toString()}`;
|
|
223
|
-
try {
|
|
224
|
-
const response = await fetch(url, {
|
|
225
|
-
method: "GET",
|
|
226
|
-
headers: this.headers,
|
|
227
|
-
});
|
|
228
|
-
if (!response.ok) {
|
|
229
|
-
const errorText = await response.text().catch(() => "");
|
|
230
|
-
throw new ToolError(`Can-I-Deploy Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
|
|
231
|
-
}
|
|
232
|
-
return (await response.json());
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
console.error(`[CanIDeploy] Unexpected error: ${error}\n`);
|
|
236
|
-
throw error;
|
|
237
|
-
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Registers tools with the provided register function.
|
|
363
|
+
*
|
|
364
|
+
* @param register - The function used to register tools.
|
|
365
|
+
* @param getInput - The function used to get input for tools.
|
|
366
|
+
*/
|
|
367
|
+
async registerTools(register, getInput) {
|
|
368
|
+
let disablePactflowAItools = false;
|
|
369
|
+
try {
|
|
370
|
+
const entitlement = await this.checkAIEntitlements();
|
|
371
|
+
if (!entitlement.aiEnabled) {
|
|
372
|
+
disablePactflowAItools = true;
|
|
373
|
+
}
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (error instanceof ToolError && error.metadata?.get("responseStatus") === 404) {
|
|
376
|
+
disablePactflowAItools = true;
|
|
377
|
+
}
|
|
238
378
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
// Build query parameters manually to avoid URL encoding of square brackets
|
|
251
|
-
const queryParts = [];
|
|
252
|
-
// Add optional parameters
|
|
253
|
-
if (latestby) {
|
|
254
|
-
queryParts.push(`latestby=${encodeURIComponent(latestby)}`);
|
|
255
|
-
}
|
|
256
|
-
if (limit !== undefined) {
|
|
257
|
-
queryParts.push(`limit=${limit}`);
|
|
379
|
+
for (const tool of TOOLS.filter(
|
|
380
|
+
(t) => !this._clientType || t.clients.includes(this._clientType)
|
|
381
|
+
)) {
|
|
382
|
+
if (tool.tags && disablePactflowAItools && tool.tags.includes("pactflow-ai")) {
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
const { handler, clients: _, formatResponse, ...toolparams } = tool;
|
|
386
|
+
register(toolparams, async (args, _extra) => {
|
|
387
|
+
const handler_fn = this[handler];
|
|
388
|
+
if (typeof handler_fn !== "function") {
|
|
389
|
+
throw new Error(`Handler '${handler}' not found on PactClient`);
|
|
258
390
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
if (selector.branch) {
|
|
266
|
-
queryParts.push(`q[]branch=${encodeURIComponent(selector.branch)}`);
|
|
267
|
-
}
|
|
268
|
-
if (selector.environment) {
|
|
269
|
-
queryParts.push(`q[]environment=${encodeURIComponent(selector.environment)}`);
|
|
270
|
-
}
|
|
271
|
-
if (selector.latest !== undefined) {
|
|
272
|
-
queryParts.push(`q[]latest=${selector.latest}`);
|
|
273
|
-
}
|
|
274
|
-
if (selector.tag) {
|
|
275
|
-
queryParts.push(`q[]tag=${encodeURIComponent(selector.tag)}`);
|
|
276
|
-
}
|
|
277
|
-
if (selector.mainBranch !== undefined) {
|
|
278
|
-
queryParts.push(`q[]mainBranch=${selector.mainBranch}`);
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
const url = `${this.baseUrl}/matrix?${queryParts.join("&")}`;
|
|
282
|
-
try {
|
|
283
|
-
const response = await fetch(url, {
|
|
284
|
-
method: "GET",
|
|
285
|
-
headers: this.headers,
|
|
286
|
-
});
|
|
287
|
-
if (!response.ok) {
|
|
288
|
-
const errorText = await response.text().catch(() => "");
|
|
289
|
-
throw new ToolError(`Matrix Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
|
|
290
|
-
}
|
|
291
|
-
return (await response.json());
|
|
391
|
+
let result;
|
|
392
|
+
if (tool.enableElicitation) {
|
|
393
|
+
result = await handler_fn.call(this, args, getInput);
|
|
394
|
+
} else {
|
|
395
|
+
result = await handler_fn.call(this, args);
|
|
292
396
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
throw error;
|
|
397
|
+
if (formatResponse) {
|
|
398
|
+
return formatResponse(result);
|
|
296
399
|
}
|
|
400
|
+
return {
|
|
401
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
402
|
+
};
|
|
403
|
+
});
|
|
297
404
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (typeof handler_fn !== "function") {
|
|
310
|
-
throw new Error(`Handler '${handler}' not found on PactClient`);
|
|
311
|
-
}
|
|
312
|
-
let result;
|
|
313
|
-
if (tool.enableElicitation) {
|
|
314
|
-
result = await handler_fn.call(this, args, getInput);
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
result = await handler_fn.call(this, args);
|
|
318
|
-
}
|
|
319
|
-
// Use custom response formatter if provided
|
|
320
|
-
if (formatResponse) {
|
|
321
|
-
return formatResponse(result);
|
|
322
|
-
}
|
|
323
|
-
// Default fallback
|
|
324
|
-
return {
|
|
325
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
326
|
-
};
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Registers prompts with the provided register function.
|
|
332
|
-
*
|
|
333
|
-
* @param register - The function used to register prompts.
|
|
334
|
-
*/
|
|
335
|
-
registerPrompts(register) {
|
|
336
|
-
PROMPTS.forEach((prompt) => {
|
|
337
|
-
register(prompt.name, prompt.params, prompt.callback);
|
|
338
|
-
});
|
|
339
|
-
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Registers prompts with the provided register function.
|
|
408
|
+
*
|
|
409
|
+
* @param register - The function used to register prompts.
|
|
410
|
+
*/
|
|
411
|
+
registerPrompts(register) {
|
|
412
|
+
PROMPTS.forEach((prompt) => {
|
|
413
|
+
register(prompt.name, prompt.params, prompt.callback);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
340
416
|
}
|
|
417
|
+
export {
|
|
418
|
+
PactflowClient
|
|
419
|
+
};
|