kickload-watcher-mcp 0.1.3 → 0.1.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/pipeline.js +82 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kickload-watcher-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Automated API performance testing for Claude Code teams via KickLoad",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/pipeline.js CHANGED
@@ -226,11 +226,85 @@ export async function runPhase1Pipeline(event) {
226
226
  const client = new KickloadClient(config.kickload.baseUrl, config.kickload.apiToken);
227
227
 
228
228
  logger.step("Generating test plan");
229
- const { jmx_filename: jmxFilename } = await withRetry(
230
- () => client.generateTestPlan({ prompt: testParams.testPrompt, jmxFilePath: jmxFilePath || null }),
231
- "generate-test-plan"
232
- );
233
229
 
230
+ let jmxFilename = null;
231
+ let kickloadFailed = false;
232
+ let kickloadErrorMsg = "";
233
+
234
+ for (let attempt = 1; attempt <= 3; attempt++) {
235
+ try {
236
+ const res = await client.generateTestPlan({
237
+ prompt: testParams.testPrompt,
238
+ jmxFilePath: jmxFilePath || null
239
+ });
240
+
241
+ jmxFilename = res.jmx_filename;
242
+ break;
243
+
244
+ } catch (err) {
245
+ kickloadErrorMsg = err.message || "unknown error";
246
+
247
+ const status =
248
+ err.status ||
249
+ err.response?.status ||
250
+ (err.message?.includes("500") ? 500 : 0);
251
+
252
+ // retry only for 503 / 5xx
253
+ if (status >= 500 && attempt < 3) {
254
+ logger.warn(`Kickload attempt ${attempt}/3 failed — retrying in 3s (${kickloadErrorMsg})`);
255
+ await sleep(3000);
256
+ } else {
257
+ kickloadFailed = true;
258
+ logger.error(`Kickload failed after retries: ${kickloadErrorMsg}`);
259
+ break;
260
+ }
261
+ }
262
+ }
263
+ if (kickloadFailed || !jmxFilename) {
264
+ logger.warn("⚠️ Kickload failed — continuing with fallback result");
265
+ logger.info(`Fallback reason: ${kickloadErrorMsg}`);
266
+
267
+ // fake result so pipeline completes fully
268
+ const totalMs = Date.now() - start;
269
+
270
+ const summary = {
271
+ passed: false,
272
+ averageLatency: null,
273
+ errorPercentage: 100,
274
+ throughput: null,
275
+ note: "Test plan generation failed — fallback result generated"
276
+ };
277
+
278
+ logPipelineSummary({
279
+ endpoint: endpoints.join(", "),
280
+ passed: summary.passed,
281
+ latency: summary.averageLatency,
282
+ errorRate: summary.errorPercentage,
283
+ throughput: summary.throughput,
284
+ durationSec: Math.round(totalMs / 1000),
285
+ emailSent: !!devEmail,
286
+ devEmail,
287
+ });
288
+
289
+ // send email
290
+ if (devEmail) {
291
+ await safeResultEmail({
292
+ toEmail: devEmail,
293
+ endpoint: endpoints.join(", "),
294
+ passed: false,
295
+ summary,
296
+ downloadUrl: null,
297
+ pdfFilename: null,
298
+ durationMs: totalMs,
299
+ testPrompt: testParams.testPrompt,
300
+ });
301
+ }
302
+
303
+ return {
304
+ success: true, // 👈 IMPORTANT: pipeline completed
305
+ fallback: true
306
+ };
307
+ }
234
308
  logger.step("Running test");
235
309
  const { task_id: taskId } = await withRetry(
236
310
  () => client.runTest(jmxFilename, { numThreads: testParams.numThreads, loopCount: testParams.loopCount, rampTime: testParams.rampTime }),
@@ -414,7 +488,10 @@ async function withRetry(fn, label) {
414
488
  return await fn();
415
489
  } catch (err) {
416
490
  lastErr = err;
417
- const status = err.status || 0;
491
+ const status =
492
+ err.status ||
493
+ err.response?.status ||
494
+ (err.message?.includes("500") ? 500 : 0);
418
495
  if (status >= 400 && status < 500 && status !== 429) throw err;
419
496
  if (attempt <= MAX_RETRIES) {
420
497
  logger.warn(`[${label}] attempt ${attempt} failed — retrying in ${RETRY_DELAY_MS / 1000}s (${err.message})`);