@muggleai/works 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,23 +1,17 @@
1
- import * as fs3 from 'fs';
2
- import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync, readdirSync, createWriteStream, statSync } from 'fs';
3
- import * as os3 from 'os';
4
- import { platform, arch, homedir } from 'os';
1
+ import * as crypto from 'crypto';
2
+ import { randomUUID } from 'crypto';
3
+ import * as fs5 from 'fs';
4
+ import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
5
5
  import * as path2 from 'path';
6
- import { join, dirname, resolve } from 'path';
7
- import { fileURLToPath } from 'url';
6
+ import { join, dirname } from 'path';
7
+ import * as os3 from 'os';
8
+ import { platform } from 'os';
8
9
  import winston from 'winston';
10
+ import { fileURLToPath } from 'url';
11
+ import { spawn, exec } from 'child_process';
9
12
  import axios, { AxiosError } from 'axios';
10
- import { spawn, exec, execFile } from 'child_process';
11
- import * as crypto from 'crypto';
12
- import { randomUUID } from 'crypto';
13
- import * as fs5 from 'fs/promises';
14
- import { z, ZodError } from 'zod';
15
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
16
- import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
17
- import { v4 } from 'uuid';
18
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
- import { Command } from 'commander';
20
- import { pipeline } from 'stream/promises';
13
+ import * as fs7 from 'fs/promises';
14
+ import { z } from 'zod';
21
15
 
22
16
  var __defProp = Object.defineProperty;
23
17
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -30,6 +24,60 @@ var __export = (target, all) => {
30
24
  for (var name in all)
31
25
  __defProp(target, name, { get: all[name], enumerable: true });
32
26
  };
27
+
28
+ // packages/mcps/src/index.ts
29
+ var src_exports = {};
30
+ __export(src_exports, {
31
+ buildElectronAppChecksumsUrl: () => buildElectronAppChecksumsUrl,
32
+ buildElectronAppReleaseAssetUrl: () => buildElectronAppReleaseAssetUrl,
33
+ buildElectronAppReleaseTag: () => buildElectronAppReleaseTag,
34
+ calculateFileChecksum: () => calculateFileChecksum,
35
+ createApiKeyWithToken: () => createApiKeyWithToken,
36
+ createChildLogger: () => createChildLogger,
37
+ deleteApiKeyData: () => deleteApiKeyData,
38
+ deleteCredentials: () => deleteCredentials,
39
+ e2e: () => e2e_exports2,
40
+ getApiKey: () => getApiKey,
41
+ getApiKeyFilePath: () => getApiKeyFilePath,
42
+ getAuthService: () => getAuthService,
43
+ getBundledElectronAppVersion: () => getBundledElectronAppVersion,
44
+ getCallerCredentials: () => getCallerCredentials,
45
+ getCallerCredentialsAsync: () => getCallerCredentialsAsync,
46
+ getChecksumForPlatform: () => getChecksumForPlatform,
47
+ getConfig: () => getConfig,
48
+ getCredentialsFilePath: () => getCredentialsFilePath,
49
+ getDataDir: () => getDataDir2,
50
+ getDownloadBaseUrl: () => getDownloadBaseUrl,
51
+ getElectronAppChecksums: () => getElectronAppChecksums,
52
+ getElectronAppDir: () => getElectronAppDir,
53
+ getElectronAppVersion: () => getElectronAppVersion,
54
+ getElectronAppVersionSource: () => getElectronAppVersionSource,
55
+ getLocalQaTools: () => getLocalQaTools,
56
+ getLogger: () => getLogger,
57
+ getPlatformKey: () => getPlatformKey,
58
+ getQaTools: () => getQaTools,
59
+ getValidApiKeyData: () => getValidApiKeyData,
60
+ getValidCredentials: () => getValidCredentials,
61
+ hasApiKey: () => hasApiKey,
62
+ isElectronAppInstalled: () => isElectronAppInstalled,
63
+ loadApiKeyData: () => loadApiKeyData,
64
+ loadCredentials: () => loadCredentials,
65
+ localQa: () => local_exports2,
66
+ mcp: () => mcp_exports,
67
+ openBrowserUrl: () => openBrowserUrl,
68
+ performLogin: () => performLogin,
69
+ performLogout: () => performLogout,
70
+ pollDeviceCode: () => pollDeviceCode,
71
+ qa: () => e2e_exports2,
72
+ resetConfig: () => resetConfig,
73
+ resetLogger: () => resetLogger,
74
+ saveApiKey: () => saveApiKey,
75
+ saveApiKeyData: () => saveApiKeyData,
76
+ saveCredentials: () => saveCredentials,
77
+ startDeviceCodeFlow: () => startDeviceCodeFlow,
78
+ toolRequiresAuth: () => toolRequiresAuth,
79
+ verifyFileChecksum: () => verifyFileChecksum
80
+ });
33
81
  var DATA_DIR_NAME = ".muggle-ai";
34
82
  function getDataDir() {
35
83
  return path2.join(os3.homedir(), DATA_DIR_NAME);
@@ -116,7 +164,7 @@ function getMuggleConfig() {
116
164
  const packageJsonPath = path2.join(packageRoot, "package.json");
117
165
  let packageJson;
118
166
  try {
119
- const content = fs3.readFileSync(packageJsonPath, "utf-8");
167
+ const content = fs5.readFileSync(packageJsonPath, "utf-8");
120
168
  packageJson = JSON.parse(content);
121
169
  } catch (error) {
122
170
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -191,7 +239,7 @@ function getDownloadedElectronAppPath() {
191
239
  default:
192
240
  return null;
193
241
  }
194
- if (fs3.existsSync(binaryPath)) {
242
+ if (fs5.existsSync(binaryPath)) {
195
243
  return binaryPath;
196
244
  }
197
245
  return null;
@@ -213,14 +261,14 @@ function getSystemElectronAppPath() {
213
261
  default:
214
262
  return null;
215
263
  }
216
- if (fs3.existsSync(binaryPath)) {
264
+ if (fs5.existsSync(binaryPath)) {
217
265
  return binaryPath;
218
266
  }
219
267
  return null;
220
268
  }
221
269
  function resolveElectronAppPathOrNull() {
222
270
  const customPath = process.env.ELECTRON_APP_PATH;
223
- if (customPath && fs3.existsSync(customPath)) {
271
+ if (customPath && fs5.existsSync(customPath)) {
224
272
  return customPath;
225
273
  }
226
274
  const downloadedPath = getDownloadedElectronAppPath();
@@ -235,12 +283,12 @@ function resolveElectronAppPathOrNull() {
235
283
  }
236
284
  function resolveWebServicePath() {
237
285
  const customPath = process.env.WEB_SERVICE_PATH;
238
- if (customPath && fs3.existsSync(customPath)) {
286
+ if (customPath && fs5.existsSync(customPath)) {
239
287
  return customPath;
240
288
  }
241
289
  const packageRoot = getPackageRoot();
242
290
  const siblingPath = path2.resolve(packageRoot, "..", "web-service", "dist", "src", "index.js");
243
- if (fs3.existsSync(siblingPath)) {
291
+ if (fs5.existsSync(siblingPath)) {
244
292
  return siblingPath;
245
293
  }
246
294
  return null;
@@ -369,9 +417,9 @@ function getElectronAppVersion() {
369
417
  return envVersion;
370
418
  }
371
419
  const overridePath = path2.join(getDataDir2(), VERSION_OVERRIDE_FILE);
372
- if (fs3.existsSync(overridePath)) {
420
+ if (fs5.existsSync(overridePath)) {
373
421
  try {
374
- const content = JSON.parse(fs3.readFileSync(overridePath, "utf-8"));
422
+ const content = JSON.parse(fs5.readFileSync(overridePath, "utf-8"));
375
423
  if (content.version && typeof content.version === "string") {
376
424
  return content.version;
377
425
  }
@@ -386,9 +434,9 @@ function getElectronAppVersionSource() {
386
434
  return "env";
387
435
  }
388
436
  const overridePath = path2.join(getDataDir2(), VERSION_OVERRIDE_FILE);
389
- if (fs3.existsSync(overridePath)) {
437
+ if (fs5.existsSync(overridePath)) {
390
438
  try {
391
- const content = JSON.parse(fs3.readFileSync(overridePath, "utf-8"));
439
+ const content = JSON.parse(fs5.readFileSync(overridePath, "utf-8"));
392
440
  if (content.version && typeof content.version === "string") {
393
441
  return "override";
394
442
  }
@@ -422,6 +470,8 @@ function getElectronAppDir(version) {
422
470
  const resolvedVersion = version ?? getElectronAppVersion();
423
471
  return path2.join(getDataDir2(), ELECTRON_APP_DIR, resolvedVersion);
424
472
  }
473
+
474
+ // packages/mcps/src/shared/logger.ts
425
475
  var loggerInstance = null;
426
476
  function createLogger() {
427
477
  const config = getConfig();
@@ -451,109 +501,101 @@ function getLogger() {
451
501
  return loggerInstance;
452
502
  }
453
503
  function createChildLogger(correlationId) {
454
- const logger14 = getLogger();
455
- return logger14.child({ correlationId });
504
+ const logger6 = getLogger();
505
+ return logger6.child({ correlationId });
456
506
  }
457
507
  function resetLogger() {
458
508
  loggerInstance = null;
459
509
  }
460
510
 
461
- // packages/mcps/src/mcp/e2e/index.ts
462
- var e2e_exports2 = {};
463
- __export(e2e_exports2, {
464
- ActionScriptGetInputSchema: () => ActionScriptGetInputSchema,
465
- ApiKeyCreateInputSchema: () => ApiKeyCreateInputSchema,
466
- ApiKeyGetInputSchema: () => ApiKeyGetInputSchema,
467
- ApiKeyListInputSchema: () => ApiKeyListInputSchema,
468
- ApiKeyRecordIdSchema: () => ApiKeyRecordIdSchema,
469
- ApiKeyRevokeInputSchema: () => ApiKeyRevokeInputSchema,
470
- AuthLoginInputSchema: () => AuthLoginInputSchema,
471
- AuthPollInputSchema: () => AuthPollInputSchema,
472
- BulkPreviewJobCancelInputSchema: () => BulkPreviewJobCancelInputSchema,
473
- BulkPreviewJobGetInputSchema: () => BulkPreviewJobGetInputSchema,
474
- BulkPreviewJobKindSchema: () => BulkPreviewJobKindSchema,
475
- BulkPreviewJobListInputSchema: () => BulkPreviewJobListInputSchema,
476
- BulkPreviewJobStatusSchema: () => BulkPreviewJobStatusSchema,
477
- BulkPreviewPromptSchema: () => BulkPreviewPromptSchema,
478
- BulkPreviewSubmitTestCaseInputSchema: () => BulkPreviewSubmitTestCaseInputSchema,
479
- BulkPreviewSubmitUseCaseInputSchema: () => BulkPreviewSubmitUseCaseInputSchema,
480
- EmptyInputSchema: () => EmptyInputSchema,
481
- GatewayError: () => GatewayError,
482
- IdSchema: () => IdSchema,
483
- LocalExecutionContextInputSchema: () => LocalExecutionContextInputSchema,
484
- LocalRunUploadInputSchema: () => LocalRunUploadInputSchema,
485
- McpErrorCode: () => McpErrorCode,
486
- MuggleEntityIdSchema: () => MuggleEntityIdSchema,
487
- PaginationInputSchema: () => PaginationInputSchema,
488
- PrdFileDeleteInputSchema: () => PrdFileDeleteInputSchema,
489
- PrdFileListInputSchema: () => PrdFileListInputSchema,
490
- PrdFileProcessLatestRunInputSchema: () => PrdFileProcessLatestRunInputSchema,
491
- PrdFileProcessStartInputSchema: () => PrdFileProcessStartInputSchema,
492
- PrdFileUploadInputSchema: () => PrdFileUploadInputSchema,
493
- ProjectCreateInputSchema: () => ProjectCreateInputSchema,
494
- ProjectDeleteInputSchema: () => ProjectDeleteInputSchema,
495
- ProjectGetInputSchema: () => ProjectGetInputSchema,
496
- ProjectListInputSchema: () => ProjectListInputSchema,
497
- ProjectTestResultsSummaryInputSchema: () => ProjectTestResultsSummaryInputSchema,
498
- ProjectTestRunsSummaryInputSchema: () => ProjectTestRunsSummaryInputSchema,
499
- ProjectTestScriptsSummaryInputSchema: () => ProjectTestScriptsSummaryInputSchema,
500
- ProjectUpdateInputSchema: () => ProjectUpdateInputSchema,
501
- PromptServiceClient: () => PromptServiceClient,
502
- RecommendCicdSetupInputSchema: () => RecommendCicdSetupInputSchema,
503
- RecommendScheduleInputSchema: () => RecommendScheduleInputSchema,
504
- ReportCostQueryInputSchema: () => ReportCostQueryInputSchema,
505
- ReportFinalGenerateInputSchema: () => ReportFinalGenerateInputSchema,
506
- ReportPreferencesUpsertInputSchema: () => ReportPreferencesUpsertInputSchema,
507
- ReportStatsSummaryInputSchema: () => ReportStatsSummaryInputSchema,
508
- RunBatchIdSchema: () => RunBatchIdSchema,
509
- SecretCreateInputSchema: () => SecretCreateInputSchema,
510
- SecretDeleteInputSchema: () => SecretDeleteInputSchema,
511
- SecretGetInputSchema: () => SecretGetInputSchema,
512
- SecretListInputSchema: () => SecretListInputSchema,
513
- SecretUpdateInputSchema: () => SecretUpdateInputSchema,
514
- StripePaymentMethodIdSchema: () => StripePaymentMethodIdSchema,
515
- TestCaseCreateInputSchema: () => TestCaseCreateInputSchema,
516
- TestCaseGenerateFromPromptInputSchema: () => TestCaseGenerateFromPromptInputSchema,
517
- TestCaseGetInputSchema: () => TestCaseGetInputSchema,
518
- TestCaseListByUseCaseInputSchema: () => TestCaseListByUseCaseInputSchema,
519
- TestCaseListInputSchema: () => TestCaseListInputSchema,
520
- TestScriptGetInputSchema: () => TestScriptGetInputSchema,
521
- TestScriptListInputSchema: () => TestScriptListInputSchema,
522
- TokenPackageIdSchema: () => TokenPackageIdSchema,
523
- TokenUsageFilterTypeSchema: () => TokenUsageFilterTypeSchema,
524
- UseCaseCandidatesApproveInputSchema: () => UseCaseCandidatesApproveInputSchema,
525
- UseCaseCreateFromPromptsInputSchema: () => UseCaseCreateFromPromptsInputSchema,
526
- UseCaseCreateInputSchema: () => UseCaseCreateInputSchema,
527
- UseCaseDiscoveryMemoryGetInputSchema: () => UseCaseDiscoveryMemoryGetInputSchema,
528
- UseCaseGetInputSchema: () => UseCaseGetInputSchema,
529
- UseCaseListInputSchema: () => UseCaseListInputSchema,
530
- UseCasePromptPreviewInputSchema: () => UseCasePromptPreviewInputSchema,
531
- UseCaseUpdateFromPromptInputSchema: () => UseCaseUpdateFromPromptInputSchema,
532
- WalletAutoTopUpSetPaymentMethodInputSchema: () => WalletAutoTopUpSetPaymentMethodInputSchema,
533
- WalletAutoTopUpUpdateInputSchema: () => WalletAutoTopUpUpdateInputSchema,
534
- WalletPaymentMethodCreateSetupSessionInputSchema: () => WalletPaymentMethodCreateSetupSessionInputSchema,
535
- WalletPaymentMethodListInputSchema: () => WalletPaymentMethodListInputSchema,
536
- WalletTopUpInputSchema: () => WalletTopUpInputSchema,
537
- WorkflowCancelRunInputSchema: () => WorkflowCancelRunInputSchema,
538
- WorkflowCancelRuntimeInputSchema: () => WorkflowCancelRuntimeInputSchema,
539
- WorkflowGetLatestRunInputSchema: () => WorkflowGetLatestRunInputSchema,
540
- WorkflowGetLatestScriptGenByTestCaseInputSchema: () => WorkflowGetLatestScriptGenByTestCaseInputSchema,
541
- WorkflowGetReplayBulkBatchSummaryInputSchema: () => WorkflowGetReplayBulkBatchSummaryInputSchema,
542
- WorkflowListRuntimesInputSchema: () => WorkflowListRuntimesInputSchema,
543
- WorkflowMemoryParamsSchema: () => WorkflowMemoryParamsSchema,
544
- WorkflowParamsSchema: () => WorkflowParamsSchema,
545
- WorkflowStartTestCaseDetectionInputSchema: () => WorkflowStartTestCaseDetectionInputSchema,
546
- WorkflowStartTestScriptGenerationInputSchema: () => WorkflowStartTestScriptGenerationInputSchema,
547
- WorkflowStartTestScriptReplayBulkInputSchema: () => WorkflowStartTestScriptReplayBulkInputSchema,
548
- WorkflowStartTestScriptReplayInputSchema: () => WorkflowStartTestScriptReplayInputSchema,
549
- WorkflowStartWebsiteScanInputSchema: () => WorkflowStartWebsiteScanInputSchema,
550
- allQaToolDefinitions: () => allQaToolDefinitions,
551
- executeQaTool: () => executeQaTool,
552
- getPromptServiceClient: () => getPromptServiceClient,
553
- getQaToolByName: () => getQaToolByName,
554
- getQaTools: () => getQaTools
555
- });
511
+ // packages/mcps/src/shared/checksum.ts
556
512
  var logger = getLogger();
513
+ function getPlatformKey() {
514
+ const os4 = platform();
515
+ const arch2 = process.arch;
516
+ switch (os4) {
517
+ case "darwin":
518
+ return arch2 === "arm64" ? "darwin-arm64" : "darwin-x64";
519
+ case "win32":
520
+ return "win32-x64";
521
+ case "linux":
522
+ return "linux-x64";
523
+ default:
524
+ throw new Error(`Unsupported platform: ${os4}`);
525
+ }
526
+ }
527
+ async function calculateFileChecksum(filePath) {
528
+ return new Promise((resolve3, reject) => {
529
+ const hash = crypto.createHash("sha256");
530
+ const stream = fs5.createReadStream(filePath);
531
+ stream.on("data", (data) => {
532
+ hash.update(data);
533
+ });
534
+ stream.on("end", () => {
535
+ resolve3(hash.digest("hex"));
536
+ });
537
+ stream.on("error", (error) => {
538
+ reject(error);
539
+ });
540
+ });
541
+ }
542
+ async function verifyFileChecksum(filePath, expectedChecksum) {
543
+ if (!expectedChecksum || expectedChecksum.trim() === "") {
544
+ logger.warn("Checksum verification skipped - no checksum provided", {
545
+ file: path2.basename(filePath)
546
+ });
547
+ return {
548
+ valid: true,
549
+ expected: "",
550
+ actual: "",
551
+ error: "Checksum verification skipped - no checksum configured"
552
+ };
553
+ }
554
+ try {
555
+ const actualChecksum = await calculateFileChecksum(filePath);
556
+ const normalizedExpected = expectedChecksum.toLowerCase().trim();
557
+ const normalizedActual = actualChecksum.toLowerCase();
558
+ const valid = normalizedExpected === normalizedActual;
559
+ if (!valid) {
560
+ logger.error("Checksum verification failed", {
561
+ file: path2.basename(filePath),
562
+ expected: normalizedExpected,
563
+ actual: normalizedActual
564
+ });
565
+ } else {
566
+ logger.info("Checksum verified successfully", {
567
+ file: path2.basename(filePath),
568
+ checksum: normalizedActual
569
+ });
570
+ }
571
+ return {
572
+ valid,
573
+ expected: normalizedExpected,
574
+ actual: normalizedActual,
575
+ error: valid ? void 0 : "Checksum mismatch - file may be corrupted or tampered with"
576
+ };
577
+ } catch (error) {
578
+ const errorMessage = error instanceof Error ? error.message : String(error);
579
+ logger.error("Checksum calculation failed", {
580
+ file: path2.basename(filePath),
581
+ error: errorMessage
582
+ });
583
+ return {
584
+ valid: false,
585
+ expected: expectedChecksum,
586
+ actual: "",
587
+ error: `Failed to calculate checksum: ${errorMessage}`
588
+ };
589
+ }
590
+ }
591
+ function getChecksumForPlatform(checksums) {
592
+ if (!checksums) {
593
+ return "";
594
+ }
595
+ const platformKey = getPlatformKey();
596
+ return checksums[platformKey] || "";
597
+ }
598
+ var logger2 = getLogger();
557
599
  function getOpenCommand(url) {
558
600
  const platformName = platform();
559
601
  switch (platformName) {
@@ -568,59 +610,160 @@ function getOpenCommand(url) {
568
610
  }
569
611
  }
570
612
  async function openBrowserUrl(options) {
571
- return new Promise((resolve4) => {
613
+ return new Promise((resolve3) => {
572
614
  try {
573
615
  const command = getOpenCommand(options.url);
574
- logger.debug("[Browser] Opening URL", { url: options.url, command });
616
+ logger2.debug("[Browser] Opening URL", { url: options.url, command });
575
617
  exec(command, (error) => {
576
618
  if (error) {
577
- logger.warn("[Browser] Failed to open URL", {
619
+ logger2.warn("[Browser] Failed to open URL", {
578
620
  url: options.url,
579
621
  error: error.message
580
622
  });
581
- resolve4({
623
+ resolve3({
582
624
  opened: false,
583
625
  error: error.message
584
626
  });
585
627
  } else {
586
- logger.info("[Browser] URL opened successfully", { url: options.url });
587
- resolve4({ opened: true });
628
+ logger2.info("[Browser] URL opened successfully", { url: options.url });
629
+ resolve3({ opened: true });
588
630
  }
589
631
  });
590
632
  } catch (error) {
591
633
  const errorMessage = error instanceof Error ? error.message : String(error);
592
- logger.warn("[Browser] Failed to open URL", {
634
+ logger2.warn("[Browser] Failed to open URL", {
593
635
  url: options.url,
594
636
  error: errorMessage
595
637
  });
596
- resolve4({
638
+ resolve3({
597
639
  opened: false,
598
640
  error: errorMessage
599
641
  });
600
642
  }
601
643
  });
602
644
  }
603
-
604
- // packages/mcps/src/mcp/local/types/enums.ts
605
- var DeviceCodePollStatus = /* @__PURE__ */ ((DeviceCodePollStatus2) => {
606
- DeviceCodePollStatus2["Pending"] = "pending";
607
- DeviceCodePollStatus2["Complete"] = "complete";
608
- DeviceCodePollStatus2["Expired"] = "expired";
609
- DeviceCodePollStatus2["Error"] = "error";
610
- return DeviceCodePollStatus2;
611
- })(DeviceCodePollStatus || {});
612
- var SessionStatus = /* @__PURE__ */ ((SessionStatus2) => {
613
- SessionStatus2["Running"] = "running";
614
- SessionStatus2["Completed"] = "completed";
615
- SessionStatus2["Failed"] = "failed";
616
- return SessionStatus2;
617
- })(SessionStatus || {});
618
- var LocalRunStatus = /* @__PURE__ */ ((LocalRunStatus2) => {
619
- LocalRunStatus2["PENDING"] = "pending";
620
- LocalRunStatus2["RUNNING"] = "running";
621
- LocalRunStatus2["PASSED"] = "passed";
622
- LocalRunStatus2["FAILED"] = "failed";
623
- LocalRunStatus2["CANCELLED"] = "cancelled";
645
+ var API_KEY_FILE2 = "api-key.json";
646
+ function getApiKeyFilePath() {
647
+ return path2.join(getDataDir(), API_KEY_FILE2);
648
+ }
649
+ function ensureDataDir() {
650
+ const dataDir = getDataDir();
651
+ if (!fs5.existsSync(dataDir)) {
652
+ fs5.mkdirSync(dataDir, { recursive: true });
653
+ }
654
+ }
655
+ function loadApiKeyData() {
656
+ const logger6 = getLogger();
657
+ const apiKeyPath = getApiKeyFilePath();
658
+ try {
659
+ if (!fs5.existsSync(apiKeyPath)) {
660
+ logger6.debug("No API key file found", { path: apiKeyPath });
661
+ return null;
662
+ }
663
+ const content = fs5.readFileSync(apiKeyPath, "utf-8");
664
+ const data = JSON.parse(content);
665
+ return data;
666
+ } catch (error) {
667
+ logger6.warn("Failed to load API key data", {
668
+ error: error instanceof Error ? error.message : String(error)
669
+ });
670
+ return null;
671
+ }
672
+ }
673
+ function saveApiKeyData(data) {
674
+ const logger6 = getLogger();
675
+ const apiKeyPath = getApiKeyFilePath();
676
+ try {
677
+ ensureDataDir();
678
+ const content = JSON.stringify(data, null, 2);
679
+ fs5.writeFileSync(apiKeyPath, content, { mode: 384 });
680
+ logger6.info("API key saved", { path: apiKeyPath });
681
+ } catch (error) {
682
+ logger6.error("Failed to save API key", {
683
+ error: error instanceof Error ? error.message : String(error)
684
+ });
685
+ throw error;
686
+ }
687
+ }
688
+ function deleteApiKeyData() {
689
+ const logger6 = getLogger();
690
+ const apiKeyPath = getApiKeyFilePath();
691
+ try {
692
+ if (fs5.existsSync(apiKeyPath)) {
693
+ fs5.unlinkSync(apiKeyPath);
694
+ logger6.info("API key deleted", { path: apiKeyPath });
695
+ }
696
+ } catch (error) {
697
+ logger6.warn("Failed to delete API key", {
698
+ error: error instanceof Error ? error.message : String(error)
699
+ });
700
+ }
701
+ }
702
+ function getValidApiKeyData() {
703
+ const data = loadApiKeyData();
704
+ if (!data) {
705
+ return null;
706
+ }
707
+ if (data.apiKey) {
708
+ return data;
709
+ }
710
+ return null;
711
+ }
712
+ function hasApiKey() {
713
+ const data = loadApiKeyData();
714
+ return !!data?.apiKey;
715
+ }
716
+ function getApiKey() {
717
+ const data = loadApiKeyData();
718
+ return data?.apiKey ?? null;
719
+ }
720
+ function saveApiKey(params) {
721
+ const logger6 = getLogger();
722
+ const apiKeyPath = getApiKeyFilePath();
723
+ try {
724
+ ensureDataDir();
725
+ const data = {
726
+ accessToken: "",
727
+ expiresAt: "",
728
+ apiKey: params.apiKey,
729
+ apiKeyId: params.apiKeyId
730
+ };
731
+ const content = JSON.stringify(data, null, 2);
732
+ fs5.writeFileSync(apiKeyPath, content, { mode: 384 });
733
+ logger6.info("API key saved", { path: apiKeyPath });
734
+ } catch (error) {
735
+ logger6.error("Failed to save API key", {
736
+ error: error instanceof Error ? error.message : String(error)
737
+ });
738
+ throw error;
739
+ }
740
+ }
741
+ var loadCredentials = loadApiKeyData;
742
+ var saveCredentials = saveApiKeyData;
743
+ var deleteCredentials = deleteApiKeyData;
744
+ var getValidCredentials = getValidApiKeyData;
745
+ var getCredentialsFilePath = getApiKeyFilePath;
746
+
747
+ // packages/mcps/src/mcp/local/types/enums.ts
748
+ var DeviceCodePollStatus = /* @__PURE__ */ ((DeviceCodePollStatus2) => {
749
+ DeviceCodePollStatus2["Pending"] = "pending";
750
+ DeviceCodePollStatus2["Complete"] = "complete";
751
+ DeviceCodePollStatus2["Expired"] = "expired";
752
+ DeviceCodePollStatus2["Error"] = "error";
753
+ return DeviceCodePollStatus2;
754
+ })(DeviceCodePollStatus || {});
755
+ var SessionStatus = /* @__PURE__ */ ((SessionStatus2) => {
756
+ SessionStatus2["Running"] = "running";
757
+ SessionStatus2["Completed"] = "completed";
758
+ SessionStatus2["Failed"] = "failed";
759
+ return SessionStatus2;
760
+ })(SessionStatus || {});
761
+ var LocalRunStatus = /* @__PURE__ */ ((LocalRunStatus2) => {
762
+ LocalRunStatus2["PENDING"] = "pending";
763
+ LocalRunStatus2["RUNNING"] = "running";
764
+ LocalRunStatus2["PASSED"] = "passed";
765
+ LocalRunStatus2["FAILED"] = "failed";
766
+ LocalRunStatus2["CANCELLED"] = "cancelled";
624
767
  return LocalRunStatus2;
625
768
  })(LocalRunStatus || {});
626
769
  var LocalRunType = /* @__PURE__ */ ((LocalRunType2) => {
@@ -699,16 +842,16 @@ var AuthService = class {
699
842
  * Get current authentication status.
700
843
  */
701
844
  getAuthStatus() {
702
- const logger14 = getLogger();
845
+ const logger6 = getLogger();
703
846
  const storedAuth = this.loadStoredAuth();
704
847
  if (!storedAuth) {
705
- logger14.debug("No stored auth found");
848
+ logger6.debug("No stored auth found");
706
849
  return { authenticated: false };
707
850
  }
708
851
  const now = /* @__PURE__ */ new Date();
709
852
  const expiresAt = new Date(storedAuth.expiresAt);
710
853
  const isExpired = now >= expiresAt;
711
- logger14.debug("Auth status checked", {
854
+ logger6.debug("Auth status checked", {
712
855
  email: storedAuth.email,
713
856
  isExpired,
714
857
  expiresAt: storedAuth.expiresAt
@@ -726,10 +869,10 @@ var AuthService = class {
726
869
  * @param options.forceNewSession - Clear existing Auth0 browser session before login to allow account switching.
727
870
  */
728
871
  async startDeviceCodeFlow(options) {
729
- const logger14 = getLogger();
872
+ const logger6 = getLogger();
730
873
  const config = getConfig();
731
874
  const { domain, clientId, audience, scopes } = config.localQa.auth0;
732
- logger14.info("Starting device code flow");
875
+ logger6.info("Starting device code flow");
733
876
  const url = `https://${domain}/oauth/device/code`;
734
877
  const body = new URLSearchParams({
735
878
  client_id: clientId,
@@ -745,14 +888,14 @@ var AuthService = class {
745
888
  });
746
889
  if (!response.ok) {
747
890
  const errorText = await response.text();
748
- logger14.error("Device code request failed", {
891
+ logger6.error("Device code request failed", {
749
892
  status: response.status,
750
893
  error: errorText
751
894
  });
752
895
  throw new Error(`Failed to start device code flow: ${response.status} ${errorText}`);
753
896
  }
754
897
  const data = await response.json();
755
- logger14.info("Device code flow started", {
898
+ logger6.info("Device code flow started", {
756
899
  userCode: data.user_code,
757
900
  verificationUri: data.verification_uri,
758
901
  expiresIn: data.expires_in
@@ -768,7 +911,7 @@ var AuthService = class {
768
911
  logoutUrl.searchParams.set("client_id", clientId);
769
912
  logoutUrl.searchParams.set("returnTo", data.verification_uri_complete);
770
913
  browserUrl = logoutUrl.toString();
771
- logger14.info("Force new session: opening logout-redirect URL", {
914
+ logger6.info("Force new session: opening logout-redirect URL", {
772
915
  logoutUrl: browserUrl
773
916
  });
774
917
  }
@@ -776,9 +919,9 @@ var AuthService = class {
776
919
  url: browserUrl
777
920
  });
778
921
  if (browserOpenResult.opened) {
779
- logger14.info("Browser opened for device code login");
922
+ logger6.info("Browser opened for device code login");
780
923
  } else {
781
- logger14.warn("Failed to open browser for device code login", {
924
+ logger6.warn("Failed to open browser for device code login", {
782
925
  error: browserOpenResult.error,
783
926
  url: browserUrl
784
927
  });
@@ -798,39 +941,39 @@ var AuthService = class {
798
941
  * Store a pending device code for later retrieval.
799
942
  */
800
943
  storePendingDeviceCode(params) {
801
- const logger14 = getLogger();
944
+ const logger6 = getLogger();
802
945
  const dir = path2.dirname(this.pendingDeviceCodePath);
803
- if (!fs3.existsSync(dir)) {
804
- fs3.mkdirSync(dir, { recursive: true });
946
+ if (!fs5.existsSync(dir)) {
947
+ fs5.mkdirSync(dir, { recursive: true });
805
948
  }
806
- fs3.writeFileSync(this.pendingDeviceCodePath, JSON.stringify(params, null, 2), {
949
+ fs5.writeFileSync(this.pendingDeviceCodePath, JSON.stringify(params, null, 2), {
807
950
  encoding: "utf-8",
808
951
  mode: 384
809
952
  });
810
- logger14.debug("Pending device code stored", { userCode: params.userCode });
953
+ logger6.debug("Pending device code stored", { userCode: params.userCode });
811
954
  }
812
955
  /**
813
956
  * Get the pending device code if one exists and is not expired.
814
957
  */
815
958
  getPendingDeviceCode() {
816
- const logger14 = getLogger();
817
- if (!fs3.existsSync(this.pendingDeviceCodePath)) {
818
- logger14.debug("No pending device code found");
959
+ const logger6 = getLogger();
960
+ if (!fs5.existsSync(this.pendingDeviceCodePath)) {
961
+ logger6.debug("No pending device code found");
819
962
  return null;
820
963
  }
821
964
  try {
822
- const content = fs3.readFileSync(this.pendingDeviceCodePath, "utf-8");
965
+ const content = fs5.readFileSync(this.pendingDeviceCodePath, "utf-8");
823
966
  const data = JSON.parse(content);
824
967
  const now = /* @__PURE__ */ new Date();
825
968
  const expiresAt = new Date(data.expiresAt);
826
969
  if (now >= expiresAt) {
827
- logger14.debug("Pending device code expired");
970
+ logger6.debug("Pending device code expired");
828
971
  this.clearPendingDeviceCode();
829
972
  return null;
830
973
  }
831
974
  return data.deviceCode;
832
975
  } catch (error) {
833
- logger14.warn("Failed to read pending device code", {
976
+ logger6.warn("Failed to read pending device code", {
834
977
  error: error instanceof Error ? error.message : String(error)
835
978
  });
836
979
  return null;
@@ -840,13 +983,13 @@ var AuthService = class {
840
983
  * Clear the pending device code file.
841
984
  */
842
985
  clearPendingDeviceCode() {
843
- const logger14 = getLogger();
844
- if (fs3.existsSync(this.pendingDeviceCodePath)) {
986
+ const logger6 = getLogger();
987
+ if (fs5.existsSync(this.pendingDeviceCodePath)) {
845
988
  try {
846
- fs3.unlinkSync(this.pendingDeviceCodePath);
847
- logger14.debug("Pending device code cleared");
989
+ fs5.unlinkSync(this.pendingDeviceCodePath);
990
+ logger6.debug("Pending device code cleared");
848
991
  } catch (error) {
849
- logger14.warn("Failed to clear pending device code", {
992
+ logger6.warn("Failed to clear pending device code", {
850
993
  error: error instanceof Error ? error.message : String(error)
851
994
  });
852
995
  }
@@ -856,10 +999,10 @@ var AuthService = class {
856
999
  * Poll for device code authorization completion.
857
1000
  */
858
1001
  async pollDeviceCode(deviceCode) {
859
- const logger14 = getLogger();
1002
+ const logger6 = getLogger();
860
1003
  const config = getConfig();
861
1004
  const { domain, clientId } = config.localQa.auth0;
862
- logger14.debug("Polling for device code authorization");
1005
+ logger6.debug("Polling for device code authorization");
863
1006
  const url = `https://${domain}/oauth/token`;
864
1007
  const body = new URLSearchParams({
865
1008
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
@@ -889,7 +1032,7 @@ var AuthService = class {
889
1032
  userId: userInfo.sub
890
1033
  });
891
1034
  this.clearPendingDeviceCode();
892
- logger14.info("Device code authorization complete", { email: userInfo.email });
1035
+ logger6.info("Device code authorization complete", { email: userInfo.email });
893
1036
  return {
894
1037
  status: "complete" /* Complete */,
895
1038
  message: "Authentication successful!",
@@ -898,35 +1041,35 @@ var AuthService = class {
898
1041
  }
899
1042
  const errorData = await response.json();
900
1043
  if (errorData.error === "authorization_pending") {
901
- logger14.debug("Authorization pending");
1044
+ logger6.debug("Authorization pending");
902
1045
  return {
903
1046
  status: "pending" /* Pending */,
904
1047
  message: "Waiting for user to complete authorization..."
905
1048
  };
906
1049
  }
907
1050
  if (errorData.error === "slow_down") {
908
- logger14.debug("Polling too fast");
1051
+ logger6.debug("Polling too fast");
909
1052
  return {
910
1053
  status: "pending" /* Pending */,
911
1054
  message: "Polling too fast, slowing down..."
912
1055
  };
913
1056
  }
914
1057
  if (errorData.error === "expired_token") {
915
- logger14.warn("Device code expired");
1058
+ logger6.warn("Device code expired");
916
1059
  return {
917
1060
  status: "expired" /* Expired */,
918
1061
  message: "The authorization code has expired. Please start again."
919
1062
  };
920
1063
  }
921
1064
  if (errorData.error === "access_denied") {
922
- logger14.warn("Access denied");
1065
+ logger6.warn("Access denied");
923
1066
  return {
924
1067
  status: "error" /* Error */,
925
1068
  message: "Access was denied by the user.",
926
1069
  error: errorData.error_description ?? errorData.error
927
1070
  };
928
1071
  }
929
- logger14.error("Unexpected error during polling", { error: errorData });
1072
+ logger6.error("Unexpected error during polling", { error: errorData });
930
1073
  return {
931
1074
  status: "error" /* Error */,
932
1075
  message: errorData.error_description ?? errorData.error,
@@ -934,7 +1077,7 @@ var AuthService = class {
934
1077
  };
935
1078
  } catch (error) {
936
1079
  const errorMessage = error instanceof Error ? error.message : String(error);
937
- logger14.error("Poll request failed", { error: errorMessage });
1080
+ logger6.error("Poll request failed", { error: errorMessage });
938
1081
  return {
939
1082
  status: "error" /* Error */,
940
1083
  message: `Poll request failed: ${errorMessage}`,
@@ -946,11 +1089,11 @@ var AuthService = class {
946
1089
  * Poll for device code authorization until completion or timeout.
947
1090
  */
948
1091
  async waitForDeviceCodeAuthorization(params) {
949
- const logger14 = getLogger();
1092
+ const logger6 = getLogger();
950
1093
  const timeoutMs = params.timeoutMs ?? DEFAULT_LOGIN_WAIT_TIMEOUT_MS;
951
1094
  const pollIntervalMs = Math.max(params.intervalSeconds, 1) * 1e3;
952
1095
  const startedAt = Date.now();
953
- logger14.info("Waiting for device code authorization", {
1096
+ logger6.info("Waiting for device code authorization", {
954
1097
  timeoutMs,
955
1098
  pollIntervalMs
956
1099
  });
@@ -974,7 +1117,7 @@ var AuthService = class {
974
1117
  * Get user info from Auth0.
975
1118
  */
976
1119
  async getUserInfo(accessToken) {
977
- const logger14 = getLogger();
1120
+ const logger6 = getLogger();
978
1121
  const config = getConfig();
979
1122
  const { domain } = config.localQa.auth0;
980
1123
  const url = `https://${domain}/userinfo`;
@@ -986,13 +1129,13 @@ var AuthService = class {
986
1129
  }
987
1130
  });
988
1131
  if (!response.ok) {
989
- logger14.warn("Failed to get user info", { status: response.status });
1132
+ logger6.warn("Failed to get user info", { status: response.status });
990
1133
  return {};
991
1134
  }
992
1135
  const data = await response.json();
993
1136
  return data;
994
1137
  } catch (error) {
995
- logger14.warn("User info request failed", {
1138
+ logger6.warn("User info request failed", {
996
1139
  error: error instanceof Error ? error.message : String(error)
997
1140
  });
998
1141
  return {};
@@ -1003,7 +1146,7 @@ var AuthService = class {
1003
1146
  */
1004
1147
  async storeAuth(params) {
1005
1148
  const { tokenResponse, email, userId } = params;
1006
- const logger14 = getLogger();
1149
+ const logger6 = getLogger();
1007
1150
  const expiresAt = new Date(Date.now() + tokenResponse.expiresIn * 1e3).toISOString();
1008
1151
  const storedAuth = {
1009
1152
  accessToken: tokenResponse.accessToken,
@@ -1013,28 +1156,28 @@ var AuthService = class {
1013
1156
  userId
1014
1157
  };
1015
1158
  const dir = path2.dirname(this.oauthSessionFilePath);
1016
- if (!fs3.existsSync(dir)) {
1017
- fs3.mkdirSync(dir, { recursive: true });
1159
+ if (!fs5.existsSync(dir)) {
1160
+ fs5.mkdirSync(dir, { recursive: true });
1018
1161
  }
1019
- fs3.writeFileSync(this.oauthSessionFilePath, JSON.stringify(storedAuth, null, 2), {
1162
+ fs5.writeFileSync(this.oauthSessionFilePath, JSON.stringify(storedAuth, null, 2), {
1020
1163
  encoding: "utf-8",
1021
1164
  mode: 384
1022
1165
  });
1023
- logger14.info("Auth stored successfully", { email, expiresAt });
1166
+ logger6.info("Auth stored successfully", { email, expiresAt });
1024
1167
  }
1025
1168
  /**
1026
1169
  * Load stored authentication.
1027
1170
  */
1028
1171
  loadStoredAuth() {
1029
- const logger14 = getLogger();
1030
- if (!fs3.existsSync(this.oauthSessionFilePath)) {
1172
+ const logger6 = getLogger();
1173
+ if (!fs5.existsSync(this.oauthSessionFilePath)) {
1031
1174
  return null;
1032
1175
  }
1033
1176
  try {
1034
- const content = fs3.readFileSync(this.oauthSessionFilePath, "utf-8");
1177
+ const content = fs5.readFileSync(this.oauthSessionFilePath, "utf-8");
1035
1178
  return JSON.parse(content);
1036
1179
  } catch (error) {
1037
- logger14.error("Failed to load stored auth", {
1180
+ logger6.error("Failed to load stored auth", {
1038
1181
  error: error instanceof Error ? error.message : String(error)
1039
1182
  });
1040
1183
  return null;
@@ -1075,15 +1218,15 @@ var AuthService = class {
1075
1218
  * @returns New access token or null if refresh failed.
1076
1219
  */
1077
1220
  async refreshAccessToken() {
1078
- const logger14 = getLogger();
1221
+ const logger6 = getLogger();
1079
1222
  const storedAuth = this.loadStoredAuth();
1080
1223
  if (!storedAuth?.refreshToken) {
1081
- logger14.debug("No refresh token available");
1224
+ logger6.debug("No refresh token available");
1082
1225
  return null;
1083
1226
  }
1084
1227
  const config = getConfig();
1085
1228
  const { domain, clientId } = config.localQa.auth0;
1086
- logger14.info("Refreshing access token");
1229
+ logger6.info("Refreshing access token");
1087
1230
  const url = `https://${domain}/oauth/token`;
1088
1231
  const body = new URLSearchParams({
1089
1232
  grant_type: "refresh_token",
@@ -1100,7 +1243,7 @@ var AuthService = class {
1100
1243
  });
1101
1244
  if (!response.ok) {
1102
1245
  const errorText = await response.text();
1103
- logger14.error("Token refresh failed", {
1246
+ logger6.error("Token refresh failed", {
1104
1247
  status: response.status,
1105
1248
  error: errorText
1106
1249
  });
@@ -1116,17 +1259,17 @@ var AuthService = class {
1116
1259
  userId: storedAuth.userId
1117
1260
  };
1118
1261
  const dir = path2.dirname(this.oauthSessionFilePath);
1119
- if (!fs3.existsSync(dir)) {
1120
- fs3.mkdirSync(dir, { recursive: true });
1262
+ if (!fs5.existsSync(dir)) {
1263
+ fs5.mkdirSync(dir, { recursive: true });
1121
1264
  }
1122
- fs3.writeFileSync(this.oauthSessionFilePath, JSON.stringify(updatedAuth, null, 2), {
1265
+ fs5.writeFileSync(this.oauthSessionFilePath, JSON.stringify(updatedAuth, null, 2), {
1123
1266
  encoding: "utf-8",
1124
1267
  mode: 384
1125
1268
  });
1126
- logger14.info("Access token refreshed", { expiresAt: newExpiresAt });
1269
+ logger6.info("Access token refreshed", { expiresAt: newExpiresAt });
1127
1270
  return tokenData.access_token;
1128
1271
  } catch (error) {
1129
- logger14.error("Token refresh request failed", {
1272
+ logger6.error("Token refresh request failed", {
1130
1273
  error: error instanceof Error ? error.message : String(error)
1131
1274
  });
1132
1275
  return null;
@@ -1138,10 +1281,10 @@ var AuthService = class {
1138
1281
  * @returns Valid access token or null if not authenticated or refresh failed.
1139
1282
  */
1140
1283
  async getValidAccessToken() {
1141
- const logger14 = getLogger();
1284
+ const logger6 = getLogger();
1142
1285
  const storedAuth = this.loadStoredAuth();
1143
1286
  if (!storedAuth) {
1144
- logger14.debug("No stored auth, cannot get valid token");
1287
+ logger6.debug("No stored auth, cannot get valid token");
1145
1288
  return null;
1146
1289
  }
1147
1290
  const now = /* @__PURE__ */ new Date();
@@ -1151,36 +1294,36 @@ var AuthService = class {
1151
1294
  return storedAuth.accessToken;
1152
1295
  }
1153
1296
  if (!isStrictlyExpired) {
1154
- logger14.debug("Access token in buffer zone, attempting proactive refresh");
1297
+ logger6.debug("Access token in buffer zone, attempting proactive refresh");
1155
1298
  } else {
1156
- logger14.info("Access token expired, attempting refresh");
1299
+ logger6.info("Access token expired, attempting refresh");
1157
1300
  }
1158
1301
  const refreshedToken = await this.refreshAccessToken();
1159
1302
  if (refreshedToken) {
1160
1303
  return refreshedToken;
1161
1304
  }
1162
1305
  if (!isStrictlyExpired) {
1163
- logger14.warn("Token refresh failed, but token not yet expired - using existing token");
1306
+ logger6.warn("Token refresh failed, but token not yet expired - using existing token");
1164
1307
  return storedAuth.accessToken;
1165
1308
  }
1166
- logger14.warn("Token refresh failed and token is expired, user needs to re-authenticate");
1309
+ logger6.warn("Token refresh failed and token is expired, user needs to re-authenticate");
1167
1310
  return null;
1168
1311
  }
1169
1312
  /**
1170
1313
  * Clear stored authentication (logout).
1171
1314
  */
1172
1315
  logout() {
1173
- const logger14 = getLogger();
1174
- if (!fs3.existsSync(this.oauthSessionFilePath)) {
1175
- logger14.debug("No auth to clear");
1316
+ const logger6 = getLogger();
1317
+ if (!fs5.existsSync(this.oauthSessionFilePath)) {
1318
+ logger6.debug("No auth to clear");
1176
1319
  return false;
1177
1320
  }
1178
1321
  try {
1179
- fs3.unlinkSync(this.oauthSessionFilePath);
1180
- logger14.info("Auth cleared successfully");
1322
+ fs5.unlinkSync(this.oauthSessionFilePath);
1323
+ logger6.info("Auth cleared successfully");
1181
1324
  return true;
1182
1325
  } catch (error) {
1183
- logger14.error("Failed to clear auth", {
1326
+ logger6.error("Failed to clear auth", {
1184
1327
  error: error instanceof Error ? error.message : String(error)
1185
1328
  });
1186
1329
  return false;
@@ -1196,9 +1339,9 @@ function resetAuthService() {
1196
1339
  serviceInstance = null;
1197
1340
  }
1198
1341
  function sleep(params) {
1199
- return new Promise((resolve4) => {
1342
+ return new Promise((resolve3) => {
1200
1343
  setTimeout(() => {
1201
- resolve4();
1344
+ resolve3();
1202
1345
  }, params.durationMs);
1203
1346
  });
1204
1347
  }
@@ -1220,14 +1363,14 @@ var StorageService = class {
1220
1363
  * Ensure the base directories exist.
1221
1364
  */
1222
1365
  ensureDirectories() {
1223
- const logger14 = getLogger();
1224
- if (!fs3.existsSync(this.dataDir)) {
1225
- fs3.mkdirSync(this.dataDir, { recursive: true });
1226
- logger14.info("Created data directory", { path: this.dataDir });
1366
+ const logger6 = getLogger();
1367
+ if (!fs5.existsSync(this.dataDir)) {
1368
+ fs5.mkdirSync(this.dataDir, { recursive: true });
1369
+ logger6.info("Created data directory", { path: this.dataDir });
1227
1370
  }
1228
- if (!fs3.existsSync(this.sessionsDir)) {
1229
- fs3.mkdirSync(this.sessionsDir, { recursive: true });
1230
- logger14.info("Created sessions directory", { path: this.sessionsDir });
1371
+ if (!fs5.existsSync(this.sessionsDir)) {
1372
+ fs5.mkdirSync(this.sessionsDir, { recursive: true });
1373
+ logger6.info("Created sessions directory", { path: this.sessionsDir });
1231
1374
  }
1232
1375
  }
1233
1376
  /**
@@ -1236,14 +1379,14 @@ var StorageService = class {
1236
1379
  * @returns Path to the session directory.
1237
1380
  */
1238
1381
  createSessionDirectory(sessionId) {
1239
- const logger14 = getLogger();
1382
+ const logger6 = getLogger();
1240
1383
  this.ensureDirectories();
1241
1384
  const sessionDir = path2.join(this.sessionsDir, sessionId);
1242
- if (!fs3.existsSync(sessionDir)) {
1243
- fs3.mkdirSync(sessionDir, { recursive: true });
1244
- fs3.mkdirSync(path2.join(sessionDir, "screenshots"), { recursive: true });
1245
- fs3.mkdirSync(path2.join(sessionDir, "logs"), { recursive: true });
1246
- logger14.info("Created session directory", { sessionId, path: sessionDir });
1385
+ if (!fs5.existsSync(sessionDir)) {
1386
+ fs5.mkdirSync(sessionDir, { recursive: true });
1387
+ fs5.mkdirSync(path2.join(sessionDir, "screenshots"), { recursive: true });
1388
+ fs5.mkdirSync(path2.join(sessionDir, "logs"), { recursive: true });
1389
+ logger6.info("Created session directory", { sessionId, path: sessionDir });
1247
1390
  }
1248
1391
  return sessionDir;
1249
1392
  }
@@ -1252,11 +1395,11 @@ var StorageService = class {
1252
1395
  * @param metadata - Session metadata to save.
1253
1396
  */
1254
1397
  saveSessionMetadata(metadata) {
1255
- const logger14 = getLogger();
1398
+ const logger6 = getLogger();
1256
1399
  const sessionDir = this.createSessionDirectory(metadata.sessionId);
1257
1400
  const metadataPath = path2.join(sessionDir, "metadata.json");
1258
- fs3.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
1259
- logger14.debug("Saved session metadata", { sessionId: metadata.sessionId });
1401
+ fs5.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
1402
+ logger6.debug("Saved session metadata", { sessionId: metadata.sessionId });
1260
1403
  }
1261
1404
  /**
1262
1405
  * Load session metadata.
@@ -1264,16 +1407,16 @@ var StorageService = class {
1264
1407
  * @returns Session metadata, or null if not found.
1265
1408
  */
1266
1409
  loadSessionMetadata(sessionId) {
1267
- const logger14 = getLogger();
1410
+ const logger6 = getLogger();
1268
1411
  const metadataPath = path2.join(this.sessionsDir, sessionId, "metadata.json");
1269
- if (!fs3.existsSync(metadataPath)) {
1412
+ if (!fs5.existsSync(metadataPath)) {
1270
1413
  return null;
1271
1414
  }
1272
1415
  try {
1273
- const content = fs3.readFileSync(metadataPath, "utf-8");
1416
+ const content = fs5.readFileSync(metadataPath, "utf-8");
1274
1417
  return JSON.parse(content);
1275
1418
  } catch (error) {
1276
- logger14.error("Failed to load session metadata", {
1419
+ logger6.error("Failed to load session metadata", {
1277
1420
  sessionId,
1278
1421
  error: error instanceof Error ? error.message : String(error)
1279
1422
  });
@@ -1285,12 +1428,12 @@ var StorageService = class {
1285
1428
  * @returns Array of session IDs.
1286
1429
  */
1287
1430
  listSessions() {
1288
- if (!fs3.existsSync(this.sessionsDir)) {
1431
+ if (!fs5.existsSync(this.sessionsDir)) {
1289
1432
  return [];
1290
1433
  }
1291
- return fs3.readdirSync(this.sessionsDir).filter((entry) => {
1434
+ return fs5.readdirSync(this.sessionsDir).filter((entry) => {
1292
1435
  const entryPath = path2.join(this.sessionsDir, entry);
1293
- return fs3.statSync(entryPath).isDirectory();
1436
+ return fs5.statSync(entryPath).isDirectory();
1294
1437
  });
1295
1438
  }
1296
1439
  /**
@@ -1299,11 +1442,11 @@ var StorageService = class {
1299
1442
  */
1300
1443
  getCurrentSessionId() {
1301
1444
  const currentPath = path2.join(this.sessionsDir, "current");
1302
- if (!fs3.existsSync(currentPath)) {
1445
+ if (!fs5.existsSync(currentPath)) {
1303
1446
  return null;
1304
1447
  }
1305
1448
  try {
1306
- const target = fs3.readlinkSync(currentPath);
1449
+ const target = fs5.readlinkSync(currentPath);
1307
1450
  return path2.basename(target);
1308
1451
  } catch {
1309
1452
  return null;
@@ -1314,14 +1457,14 @@ var StorageService = class {
1314
1457
  * @param sessionId - Session ID to set as current.
1315
1458
  */
1316
1459
  setCurrentSession(sessionId) {
1317
- const logger14 = getLogger();
1460
+ const logger6 = getLogger();
1318
1461
  const currentPath = path2.join(this.sessionsDir, "current");
1319
1462
  const targetPath = path2.join(this.sessionsDir, sessionId);
1320
- if (fs3.existsSync(currentPath)) {
1321
- fs3.unlinkSync(currentPath);
1463
+ if (fs5.existsSync(currentPath)) {
1464
+ fs5.unlinkSync(currentPath);
1322
1465
  }
1323
- fs3.symlinkSync(targetPath, currentPath);
1324
- logger14.info("Set current session", { sessionId });
1466
+ fs5.symlinkSync(targetPath, currentPath);
1467
+ logger6.info("Set current session", { sessionId });
1325
1468
  }
1326
1469
  /**
1327
1470
  * Save a screenshot to the session directory.
@@ -1329,11 +1472,11 @@ var StorageService = class {
1329
1472
  */
1330
1473
  saveScreenshot(params) {
1331
1474
  const { sessionId, filename, data } = params;
1332
- const logger14 = getLogger();
1475
+ const logger6 = getLogger();
1333
1476
  const sessionDir = this.createSessionDirectory(sessionId);
1334
1477
  const screenshotPath = path2.join(sessionDir, "screenshots", filename);
1335
- fs3.writeFileSync(screenshotPath, data);
1336
- logger14.debug("Saved screenshot", { sessionId, filename });
1478
+ fs5.writeFileSync(screenshotPath, data);
1479
+ logger6.debug("Saved screenshot", { sessionId, filename });
1337
1480
  return screenshotPath;
1338
1481
  }
1339
1482
  /**
@@ -1342,11 +1485,11 @@ var StorageService = class {
1342
1485
  */
1343
1486
  appendToResults(params) {
1344
1487
  const { sessionId, content } = params;
1345
- const logger14 = getLogger();
1488
+ const logger6 = getLogger();
1346
1489
  const sessionDir = this.createSessionDirectory(sessionId);
1347
1490
  const resultsPath = path2.join(sessionDir, "results.md");
1348
- fs3.appendFileSync(resultsPath, content + "\n", "utf-8");
1349
- logger14.debug("Appended to results", { sessionId });
1491
+ fs5.appendFileSync(resultsPath, content + "\n", "utf-8");
1492
+ logger6.debug("Appended to results", { sessionId });
1350
1493
  }
1351
1494
  /**
1352
1495
  * Get the results markdown content.
@@ -1355,10 +1498,10 @@ var StorageService = class {
1355
1498
  */
1356
1499
  getResults(sessionId) {
1357
1500
  const resultsPath = path2.join(this.sessionsDir, sessionId, "results.md");
1358
- if (!fs3.existsSync(resultsPath)) {
1501
+ if (!fs5.existsSync(resultsPath)) {
1359
1502
  return null;
1360
1503
  }
1361
- return fs3.readFileSync(resultsPath, "utf-8");
1504
+ return fs5.readFileSync(resultsPath, "utf-8");
1362
1505
  }
1363
1506
  /**
1364
1507
  * Get the data directory path.
@@ -1381,7 +1524,7 @@ var StorageService = class {
1381
1524
  */
1382
1525
  createSession(params) {
1383
1526
  const { sessionId, targetUrl, testInstructions } = params;
1384
- const logger14 = getLogger();
1527
+ const logger6 = getLogger();
1385
1528
  const sessionDir = this.createSessionDirectory(sessionId);
1386
1529
  const metadata = {
1387
1530
  sessionId,
@@ -1393,7 +1536,7 @@ var StorageService = class {
1393
1536
  };
1394
1537
  this.saveSessionMetadata(metadata);
1395
1538
  this.setCurrentSession(sessionId);
1396
- logger14.info("Created session", { sessionId, targetUrl });
1539
+ logger6.info("Created session", { sessionId, targetUrl });
1397
1540
  return sessionDir;
1398
1541
  }
1399
1542
  /**
@@ -1402,10 +1545,10 @@ var StorageService = class {
1402
1545
  */
1403
1546
  updateSessionStatus(params) {
1404
1547
  const { sessionId, status } = params;
1405
- const logger14 = getLogger();
1548
+ const logger6 = getLogger();
1406
1549
  const metadata = this.loadSessionMetadata(sessionId);
1407
1550
  if (!metadata) {
1408
- logger14.warn("Session not found for status update", { sessionId });
1551
+ logger6.warn("Session not found for status update", { sessionId });
1409
1552
  return;
1410
1553
  }
1411
1554
  metadata.status = status;
@@ -1413,7 +1556,7 @@ var StorageService = class {
1413
1556
  metadata.endTime = (/* @__PURE__ */ new Date()).toISOString();
1414
1557
  }
1415
1558
  this.saveSessionMetadata(metadata);
1416
- logger14.debug("Updated session status", { sessionId, status });
1559
+ logger6.debug("Updated session status", { sessionId, status });
1417
1560
  }
1418
1561
  /**
1419
1562
  * Initialize the results.md file with a header.
@@ -1421,10 +1564,10 @@ var StorageService = class {
1421
1564
  */
1422
1565
  initializeResults(params) {
1423
1566
  const { sessionId, targetUrl, testInstructions } = params;
1424
- const logger14 = getLogger();
1567
+ const logger6 = getLogger();
1425
1568
  const sessionDir = this.createSessionDirectory(sessionId);
1426
1569
  const resultsPath = path2.join(sessionDir, "results.md");
1427
- const header2 = [
1570
+ const header = [
1428
1571
  `# Test Results: ${sessionId}`,
1429
1572
  "",
1430
1573
  `**Target URL:** ${targetUrl}`,
@@ -1436,8 +1579,8 @@ var StorageService = class {
1436
1579
  "## Test Steps",
1437
1580
  ""
1438
1581
  ].join("\n");
1439
- fs3.writeFileSync(resultsPath, header2, "utf-8");
1440
- logger14.debug("Initialized results.md", { sessionId });
1582
+ fs5.writeFileSync(resultsPath, header, "utf-8");
1583
+ logger6.debug("Initialized results.md", { sessionId });
1441
1584
  }
1442
1585
  /**
1443
1586
  * Append a test step to the results.md file.
@@ -1445,7 +1588,7 @@ var StorageService = class {
1445
1588
  */
1446
1589
  appendStepToResults(params) {
1447
1590
  const { sessionId, step } = params;
1448
- const logger14 = getLogger();
1591
+ const logger6 = getLogger();
1449
1592
  const sessionDir = this.createSessionDirectory(sessionId);
1450
1593
  const resultsPath = path2.join(sessionDir, "results.md");
1451
1594
  const statusIcon = step.success ? "\u2713" : "\u2717";
@@ -1457,8 +1600,8 @@ var StorageService = class {
1457
1600
  step.screenshotPath ? `- **Screenshot:** [step-${String(step.stepNumber).padStart(3, "0")}.png](screenshots/step-${String(step.stepNumber).padStart(3, "0")}.png)` : "",
1458
1601
  ""
1459
1602
  ].filter(Boolean).join("\n");
1460
- fs3.appendFileSync(resultsPath, stepContent + "\n", "utf-8");
1461
- logger14.debug("Appended step to results", { sessionId, stepNumber: step.stepNumber });
1603
+ fs5.appendFileSync(resultsPath, stepContent + "\n", "utf-8");
1604
+ logger6.debug("Appended step to results", { sessionId, stepNumber: step.stepNumber });
1462
1605
  const metadata = this.loadSessionMetadata(sessionId);
1463
1606
  if (metadata) {
1464
1607
  metadata.stepsCount = (metadata.stepsCount ?? 0) + 1;
@@ -1471,11 +1614,11 @@ var StorageService = class {
1471
1614
  */
1472
1615
  finalizeResults(params) {
1473
1616
  const { sessionId, status, summary } = params;
1474
- const logger14 = getLogger();
1617
+ const logger6 = getLogger();
1475
1618
  const sessionDir = path2.join(this.sessionsDir, sessionId);
1476
1619
  const resultsPath = path2.join(sessionDir, "results.md");
1477
- if (!fs3.existsSync(resultsPath)) {
1478
- logger14.warn("Results file not found for finalization", { sessionId });
1620
+ if (!fs5.existsSync(resultsPath)) {
1621
+ logger6.warn("Results file not found for finalization", { sessionId });
1479
1622
  return;
1480
1623
  }
1481
1624
  const metadata = this.loadSessionMetadata(sessionId);
@@ -1497,8 +1640,8 @@ var StorageService = class {
1497
1640
  "",
1498
1641
  summary ? summary : ""
1499
1642
  ].join("\n");
1500
- fs3.appendFileSync(resultsPath, footer, "utf-8");
1501
- logger14.debug("Finalized results.md", { sessionId, status });
1643
+ fs5.appendFileSync(resultsPath, footer, "utf-8");
1644
+ logger6.debug("Finalized results.md", { sessionId, status });
1502
1645
  if (metadata) {
1503
1646
  metadata.durationMs = durationMs;
1504
1647
  metadata.endTime = endTime.toISOString();
@@ -1540,7 +1683,7 @@ var StorageService = class {
1540
1683
  */
1541
1684
  cleanupOldSessions(params) {
1542
1685
  const maxAgeDays = params?.maxAgeDays ?? DEFAULT_SESSION_MAX_AGE_DAYS;
1543
- const logger14 = getLogger();
1686
+ const logger6 = getLogger();
1544
1687
  const cutoffDate = /* @__PURE__ */ new Date();
1545
1688
  cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
1546
1689
  const sessionIds = this.listSessions();
@@ -1557,14 +1700,14 @@ var StorageService = class {
1557
1700
  if (sessionDate < cutoffDate) {
1558
1701
  const sessionDir = path2.join(this.sessionsDir, sessionId);
1559
1702
  try {
1560
- fs3.rmSync(sessionDir, { recursive: true, force: true });
1703
+ fs5.rmSync(sessionDir, { recursive: true, force: true });
1561
1704
  deletedCount++;
1562
- logger14.info("Deleted old session", {
1705
+ logger6.info("Deleted old session", {
1563
1706
  sessionId,
1564
1707
  age: Math.floor((Date.now() - sessionDate.getTime()) / (1e3 * 60 * 60 * 24))
1565
1708
  });
1566
1709
  } catch (error) {
1567
- logger14.error("Failed to delete session", {
1710
+ logger6.error("Failed to delete session", {
1568
1711
  sessionId,
1569
1712
  error: error instanceof Error ? error.message : String(error)
1570
1713
  });
@@ -1572,7 +1715,7 @@ var StorageService = class {
1572
1715
  }
1573
1716
  }
1574
1717
  if (deletedCount > 0) {
1575
- logger14.info("Session cleanup completed", {
1718
+ logger6.info("Session cleanup completed", {
1576
1719
  deletedCount,
1577
1720
  maxAgeDays
1578
1721
  });
@@ -1593,25 +1736,25 @@ var StorageService = class {
1593
1736
  * @returns Whether deletion succeeded.
1594
1737
  */
1595
1738
  deleteSession(sessionId) {
1596
- const logger14 = getLogger();
1739
+ const logger6 = getLogger();
1597
1740
  const sessionDir = path2.join(this.sessionsDir, sessionId);
1598
- if (!fs3.existsSync(sessionDir)) {
1599
- logger14.warn("Session not found for deletion", { sessionId });
1741
+ if (!fs5.existsSync(sessionDir)) {
1742
+ logger6.warn("Session not found for deletion", { sessionId });
1600
1743
  return false;
1601
1744
  }
1602
1745
  try {
1603
1746
  const currentId = this.getCurrentSessionId();
1604
1747
  if (currentId === sessionId) {
1605
1748
  const currentPath = path2.join(this.sessionsDir, "current");
1606
- if (fs3.existsSync(currentPath)) {
1607
- fs3.unlinkSync(currentPath);
1749
+ if (fs5.existsSync(currentPath)) {
1750
+ fs5.unlinkSync(currentPath);
1608
1751
  }
1609
1752
  }
1610
- fs3.rmSync(sessionDir, { recursive: true, force: true });
1611
- logger14.info("Deleted session", { sessionId });
1753
+ fs5.rmSync(sessionDir, { recursive: true, force: true });
1754
+ logger6.info("Deleted session", { sessionId });
1612
1755
  return true;
1613
1756
  } catch (error) {
1614
- logger14.error("Failed to delete session", {
1757
+ logger6.error("Failed to delete session", {
1615
1758
  sessionId,
1616
1759
  error: error instanceof Error ? error.message : String(error)
1617
1760
  });
@@ -1627,7 +1770,7 @@ function getStorageService() {
1627
1770
  function resetStorageService() {
1628
1771
  serviceInstance2 = null;
1629
1772
  }
1630
- var logger2 = getLogger();
1773
+ var logger3 = getLogger();
1631
1774
  var RunResultStorageService = class {
1632
1775
  /** Base directory for run results. */
1633
1776
  runResultsDir;
@@ -1644,11 +1787,11 @@ var RunResultStorageService = class {
1644
1787
  * Ensure storage directories exist.
1645
1788
  */
1646
1789
  ensureDirectories() {
1647
- if (!fs3.existsSync(this.runResultsDir)) {
1648
- fs3.mkdirSync(this.runResultsDir, { recursive: true });
1790
+ if (!fs5.existsSync(this.runResultsDir)) {
1791
+ fs5.mkdirSync(this.runResultsDir, { recursive: true });
1649
1792
  }
1650
- if (!fs3.existsSync(this.testScriptsDir)) {
1651
- fs3.mkdirSync(this.testScriptsDir, { recursive: true });
1793
+ if (!fs5.existsSync(this.testScriptsDir)) {
1794
+ fs5.mkdirSync(this.testScriptsDir, { recursive: true });
1652
1795
  }
1653
1796
  }
1654
1797
  // ========================================
@@ -1659,14 +1802,14 @@ var RunResultStorageService = class {
1659
1802
  */
1660
1803
  listRunResults() {
1661
1804
  try {
1662
- const files = fs3.readdirSync(this.runResultsDir).filter((f) => f.endsWith(".json"));
1805
+ const files = fs5.readdirSync(this.runResultsDir).filter((f) => f.endsWith(".json"));
1663
1806
  const results = [];
1664
1807
  for (const file of files) {
1665
1808
  try {
1666
- const content = fs3.readFileSync(path2.join(this.runResultsDir, file), "utf-8");
1809
+ const content = fs5.readFileSync(path2.join(this.runResultsDir, file), "utf-8");
1667
1810
  results.push(JSON.parse(content));
1668
1811
  } catch {
1669
- logger2.warn("Failed to read run result file", { file });
1812
+ logger3.warn("Failed to read run result file", { file });
1670
1813
  }
1671
1814
  }
1672
1815
  return results.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
@@ -1679,11 +1822,11 @@ var RunResultStorageService = class {
1679
1822
  */
1680
1823
  getRunResult(runId) {
1681
1824
  const filePath = path2.join(this.runResultsDir, `${runId}.json`);
1682
- if (!fs3.existsSync(filePath)) {
1825
+ if (!fs5.existsSync(filePath)) {
1683
1826
  return void 0;
1684
1827
  }
1685
1828
  try {
1686
- const content = fs3.readFileSync(filePath, "utf-8");
1829
+ const content = fs5.readFileSync(filePath, "utf-8");
1687
1830
  return JSON.parse(content);
1688
1831
  } catch {
1689
1832
  return void 0;
@@ -1694,7 +1837,7 @@ var RunResultStorageService = class {
1694
1837
  */
1695
1838
  saveRunResult(result) {
1696
1839
  const filePath = path2.join(this.runResultsDir, `${result.id}.json`);
1697
- fs3.writeFileSync(filePath, JSON.stringify(result, null, 2));
1840
+ fs5.writeFileSync(filePath, JSON.stringify(result, null, 2));
1698
1841
  }
1699
1842
  /**
1700
1843
  * Create a new run result.
@@ -1742,14 +1885,14 @@ var RunResultStorageService = class {
1742
1885
  */
1743
1886
  listTestScripts() {
1744
1887
  try {
1745
- const files = fs3.readdirSync(this.testScriptsDir).filter((f) => f.endsWith(".json"));
1888
+ const files = fs5.readdirSync(this.testScriptsDir).filter((f) => f.endsWith(".json"));
1746
1889
  const scripts = [];
1747
1890
  for (const file of files) {
1748
1891
  try {
1749
- const content = fs3.readFileSync(path2.join(this.testScriptsDir, file), "utf-8");
1892
+ const content = fs5.readFileSync(path2.join(this.testScriptsDir, file), "utf-8");
1750
1893
  scripts.push(JSON.parse(content));
1751
1894
  } catch {
1752
- logger2.warn("Failed to read test script file", { file });
1895
+ logger3.warn("Failed to read test script file", { file });
1753
1896
  }
1754
1897
  }
1755
1898
  return scripts.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
@@ -1762,11 +1905,11 @@ var RunResultStorageService = class {
1762
1905
  */
1763
1906
  getTestScript(testScriptId) {
1764
1907
  const filePath = path2.join(this.testScriptsDir, `${testScriptId}.json`);
1765
- if (!fs3.existsSync(filePath)) {
1908
+ if (!fs5.existsSync(filePath)) {
1766
1909
  return void 0;
1767
1910
  }
1768
1911
  try {
1769
- const content = fs3.readFileSync(filePath, "utf-8");
1912
+ const content = fs5.readFileSync(filePath, "utf-8");
1770
1913
  return JSON.parse(content);
1771
1914
  } catch {
1772
1915
  return void 0;
@@ -1777,7 +1920,7 @@ var RunResultStorageService = class {
1777
1920
  */
1778
1921
  saveTestScript(script) {
1779
1922
  const filePath = path2.join(this.testScriptsDir, `${script.id}.json`);
1780
- fs3.writeFileSync(filePath, JSON.stringify(script, null, 2));
1923
+ fs5.writeFileSync(filePath, JSON.stringify(script, null, 2));
1781
1924
  }
1782
1925
  /**
1783
1926
  * Create a new test script.
@@ -1825,7 +1968,7 @@ function getRunResultStorageService() {
1825
1968
  function resetRunResultStorageService() {
1826
1969
  instance = null;
1827
1970
  }
1828
- var logger3 = getLogger();
1971
+ var logger4 = getLogger();
1829
1972
  var activeProcesses = /* @__PURE__ */ new Map();
1830
1973
  function getAuthenticatedUserId() {
1831
1974
  const authService = getAuthService();
@@ -1848,7 +1991,7 @@ async function findPackageJsonAsync() {
1848
1991
  ];
1849
1992
  for (const candidatePath of candidatePaths) {
1850
1993
  try {
1851
- const packageJsonRaw = await fs5.readFile(candidatePath, "utf-8");
1994
+ const packageJsonRaw = await fs7.readFile(candidatePath, "utf-8");
1852
1995
  const packageJson = JSON.parse(packageJsonRaw);
1853
1996
  if (packageJson.name === "@muggleai/works" && packageJson.version && packageJson.muggleConfig?.electronAppVersion) {
1854
1997
  return {
@@ -1895,19 +2038,19 @@ function buildStudioAuthContent() {
1895
2038
  async function ensureTempDir() {
1896
2039
  const config = getConfig();
1897
2040
  const tempDir = path2.join(config.localQa.dataDir, "temp");
1898
- await fs5.mkdir(tempDir, { recursive: true });
2041
+ await fs7.mkdir(tempDir, { recursive: true });
1899
2042
  return tempDir;
1900
2043
  }
1901
2044
  async function writeTempFile(params) {
1902
2045
  const tempDir = await ensureTempDir();
1903
2046
  const filePath = path2.join(tempDir, params.filename);
1904
- await fs5.writeFile(filePath, JSON.stringify(params.data, null, 2));
2047
+ await fs7.writeFile(filePath, JSON.stringify(params.data, null, 2));
1905
2048
  return filePath;
1906
2049
  }
1907
2050
  async function cleanupTempFiles(params) {
1908
2051
  for (const filePath of params.filePaths) {
1909
2052
  try {
1910
- await fs5.unlink(filePath);
2053
+ await fs7.unlink(filePath);
1911
2054
  } catch {
1912
2055
  }
1913
2056
  }
@@ -1917,10 +2060,10 @@ async function moveResultsToArtifacts(params) {
1917
2060
  const sessionsDir = storageService.getSessionsDir();
1918
2061
  const artifactsDir = path2.join(sessionsDir, params.runId);
1919
2062
  const actionScriptPath = path2.join(artifactsDir, "action-script.json");
1920
- await fs5.mkdir(artifactsDir, { recursive: true });
1921
- await fs5.copyFile(params.generatedScriptPath, actionScriptPath);
2063
+ await fs7.mkdir(artifactsDir, { recursive: true });
2064
+ await fs7.copyFile(params.generatedScriptPath, actionScriptPath);
1922
2065
  try {
1923
- await fs5.unlink(params.generatedScriptPath);
2066
+ await fs7.unlink(params.generatedScriptPath);
1924
2067
  } catch {
1925
2068
  }
1926
2069
  return artifactsDir;
@@ -1929,14 +2072,14 @@ async function writeExecutionLogs(params) {
1929
2072
  const storageService = getStorageService();
1930
2073
  const sessionsDir = storageService.getSessionsDir();
1931
2074
  const artifactsDir = path2.join(sessionsDir, params.runId);
1932
- await fs5.mkdir(artifactsDir, { recursive: true });
2075
+ await fs7.mkdir(artifactsDir, { recursive: true });
1933
2076
  const stdoutPath = path2.join(artifactsDir, "stdout.log");
1934
2077
  const stderrPath = path2.join(artifactsDir, "stderr.log");
1935
2078
  await Promise.all([
1936
- fs5.writeFile(stdoutPath, params.stdout, "utf-8"),
1937
- fs5.writeFile(stderrPath, params.stderr, "utf-8")
2079
+ fs7.writeFile(stdoutPath, params.stdout, "utf-8"),
2080
+ fs7.writeFile(stderrPath, params.stderr, "utf-8")
1938
2081
  ]);
1939
- logger3.info("Wrote execution logs to artifacts directory", {
2082
+ logger4.info("Wrote execution logs to artifacts directory", {
1940
2083
  runId: params.runId,
1941
2084
  artifactsDir
1942
2085
  });
@@ -2068,7 +2211,7 @@ async function executeElectronAppAsync(params) {
2068
2211
  if (params.showUi) {
2069
2212
  spawnArgs.push("--show-ui");
2070
2213
  }
2071
- logger3.info("Spawning electron-app for local execution", {
2214
+ logger4.info("Spawning electron-app for local execution", {
2072
2215
  runId: params.runId,
2073
2216
  mode,
2074
2217
  electronAppPath,
@@ -2090,7 +2233,7 @@ async function executeElectronAppAsync(params) {
2090
2233
  capturedStderr: ""
2091
2234
  };
2092
2235
  activeProcesses.set(params.runId, processInfo);
2093
- return await new Promise((resolve4, reject) => {
2236
+ return await new Promise((resolve3, reject) => {
2094
2237
  let settled = false;
2095
2238
  const finalize = (result) => {
2096
2239
  if (settled) {
@@ -2102,7 +2245,7 @@ async function executeElectronAppAsync(params) {
2102
2245
  }
2103
2246
  activeProcesses.delete(params.runId);
2104
2247
  if (result.ok) {
2105
- resolve4(result.payload);
2248
+ resolve3(result.payload);
2106
2249
  } else {
2107
2250
  reject(result.payload);
2108
2251
  }
@@ -2256,7 +2399,7 @@ ${executionResult.stderr}`;
2256
2399
  path2.dirname(inputFilePath),
2257
2400
  `gen_${path2.basename(inputFilePath)}`
2258
2401
  );
2259
- const generatedScriptRaw = await fs5.readFile(generatedScriptPath, "utf-8");
2402
+ const generatedScriptRaw = await fs7.readFile(generatedScriptPath, "utf-8");
2260
2403
  const generatedScript = JSON.parse(generatedScriptRaw);
2261
2404
  const generatedSteps = generatedScript.steps;
2262
2405
  if (!Array.isArray(generatedSteps)) {
@@ -2463,114 +2606,13 @@ function listActiveExecutions() {
2463
2606
  status: process2.status
2464
2607
  }));
2465
2608
  }
2466
- var API_KEY_FILE2 = "api-key.json";
2467
- function getApiKeyFilePath() {
2468
- return path2.join(getDataDir(), API_KEY_FILE2);
2469
- }
2470
- function ensureDataDir() {
2471
- const dataDir = getDataDir();
2472
- if (!fs3.existsSync(dataDir)) {
2473
- fs3.mkdirSync(dataDir, { recursive: true });
2474
- }
2475
- }
2476
- function loadApiKeyData() {
2477
- const logger14 = getLogger();
2478
- const apiKeyPath = getApiKeyFilePath();
2479
- try {
2480
- if (!fs3.existsSync(apiKeyPath)) {
2481
- logger14.debug("No API key file found", { path: apiKeyPath });
2482
- return null;
2483
- }
2484
- const content = fs3.readFileSync(apiKeyPath, "utf-8");
2485
- const data = JSON.parse(content);
2486
- return data;
2487
- } catch (error) {
2488
- logger14.warn("Failed to load API key data", {
2489
- error: error instanceof Error ? error.message : String(error)
2490
- });
2491
- return null;
2492
- }
2493
- }
2494
- function saveApiKeyData(data) {
2495
- const logger14 = getLogger();
2496
- const apiKeyPath = getApiKeyFilePath();
2497
- try {
2498
- ensureDataDir();
2499
- const content = JSON.stringify(data, null, 2);
2500
- fs3.writeFileSync(apiKeyPath, content, { mode: 384 });
2501
- logger14.info("API key saved", { path: apiKeyPath });
2502
- } catch (error) {
2503
- logger14.error("Failed to save API key", {
2504
- error: error instanceof Error ? error.message : String(error)
2505
- });
2506
- throw error;
2507
- }
2508
- }
2509
- function deleteApiKeyData() {
2510
- const logger14 = getLogger();
2511
- const apiKeyPath = getApiKeyFilePath();
2512
- try {
2513
- if (fs3.existsSync(apiKeyPath)) {
2514
- fs3.unlinkSync(apiKeyPath);
2515
- logger14.info("API key deleted", { path: apiKeyPath });
2516
- }
2517
- } catch (error) {
2518
- logger14.warn("Failed to delete API key", {
2519
- error: error instanceof Error ? error.message : String(error)
2520
- });
2521
- }
2522
- }
2523
- function getValidApiKeyData() {
2524
- const data = loadApiKeyData();
2525
- if (!data) {
2526
- return null;
2527
- }
2528
- if (data.apiKey) {
2529
- return data;
2530
- }
2531
- return null;
2532
- }
2533
- function hasApiKey() {
2534
- const data = loadApiKeyData();
2535
- return !!data?.apiKey;
2536
- }
2537
- function getApiKey() {
2538
- const data = loadApiKeyData();
2539
- return data?.apiKey ?? null;
2540
- }
2541
- function saveApiKey(params) {
2542
- const logger14 = getLogger();
2543
- const apiKeyPath = getApiKeyFilePath();
2544
- try {
2545
- ensureDataDir();
2546
- const data = {
2547
- accessToken: "",
2548
- expiresAt: "",
2549
- apiKey: params.apiKey,
2550
- apiKeyId: params.apiKeyId
2551
- };
2552
- const content = JSON.stringify(data, null, 2);
2553
- fs3.writeFileSync(apiKeyPath, content, { mode: 384 });
2554
- logger14.info("API key saved", { path: apiKeyPath });
2555
- } catch (error) {
2556
- logger14.error("Failed to save API key", {
2557
- error: error instanceof Error ? error.message : String(error)
2558
- });
2559
- throw error;
2560
- }
2561
- }
2562
- var loadCredentials = loadApiKeyData;
2563
- var saveCredentials = saveApiKeyData;
2564
- var deleteCredentials = deleteApiKeyData;
2565
- var getValidCredentials = getValidApiKeyData;
2566
- var getCredentialsFilePath = getApiKeyFilePath;
2567
2609
 
2568
2610
  // packages/mcps/src/shared/auth.ts
2569
- var logger4 = getLogger();
2611
+ var logger5 = getLogger();
2570
2612
  async function startDeviceCodeFlow(config, options) {
2571
2613
  const deviceCodeUrl = `https://${config.domain}/oauth/device/code`;
2572
2614
  try {
2573
- logger4.info("[Auth] Starting device code flow", {
2615
+ logger5.info("[Auth] Starting device code flow", {
2574
2616
  domain: config.domain,
2575
2617
  clientId: config.clientId
2576
2618
  });
@@ -2588,7 +2630,7 @@ async function startDeviceCodeFlow(config, options) {
2588
2630
  }
2589
2631
  );
2590
2632
  const data = response.data;
2591
- logger4.info("[Auth] Device code flow started successfully", {
2633
+ logger5.info("[Auth] Device code flow started successfully", {
2592
2634
  userCode: data.user_code,
2593
2635
  expiresIn: data.expires_in
2594
2636
  });
@@ -2598,15 +2640,15 @@ async function startDeviceCodeFlow(config, options) {
2598
2640
  logoutUrl.searchParams.set("client_id", config.clientId);
2599
2641
  logoutUrl.searchParams.set("returnTo", data.verification_uri_complete);
2600
2642
  browserUrl = logoutUrl.toString();
2601
- logger4.info("[Auth] Force new session: opening logout-redirect URL");
2643
+ logger5.info("[Auth] Force new session: opening logout-redirect URL");
2602
2644
  }
2603
2645
  const browserOpenResult = await openBrowserUrl({
2604
2646
  url: browserUrl
2605
2647
  });
2606
2648
  if (browserOpenResult.opened) {
2607
- logger4.info("[Auth] Browser opened for device code login");
2649
+ logger5.info("[Auth] Browser opened for device code login");
2608
2650
  } else {
2609
- logger4.warn("[Auth] Failed to open browser", {
2651
+ logger5.warn("[Auth] Failed to open browser", {
2610
2652
  error: browserOpenResult.error,
2611
2653
  verificationUriComplete: data.verification_uri_complete
2612
2654
  });
@@ -2623,7 +2665,7 @@ async function startDeviceCodeFlow(config, options) {
2623
2665
  };
2624
2666
  } catch (error) {
2625
2667
  if (error instanceof AxiosError) {
2626
- logger4.error("[Auth] Failed to start device code flow", {
2668
+ logger5.error("[Auth] Failed to start device code flow", {
2627
2669
  status: error.response?.status,
2628
2670
  data: error.response?.data
2629
2671
  });
@@ -2651,7 +2693,7 @@ async function pollDeviceCode(config, deviceCode) {
2651
2693
  }
2652
2694
  }
2653
2695
  );
2654
- logger4.info("[Auth] Authorization successful");
2696
+ logger5.info("[Auth] Authorization successful");
2655
2697
  return {
2656
2698
  status: "authorized",
2657
2699
  accessToken: response.data.access_token,
@@ -2690,7 +2732,7 @@ async function pollDeviceCode(config, deviceCode) {
2690
2732
  errorDescription: data.error_description || "User denied access"
2691
2733
  };
2692
2734
  }
2693
- logger4.error("[Auth] Unexpected error during poll", {
2735
+ logger5.error("[Auth] Unexpected error during poll", {
2694
2736
  status: error.response.status,
2695
2737
  error: errorCode,
2696
2738
  description: data.error_description
@@ -2707,7 +2749,7 @@ async function createApiKeyWithToken(accessToken, keyName, expiry = "90d") {
2707
2749
  const config = getConfig();
2708
2750
  const apiKeyUrl = `${config.e2e.promptServiceBaseUrl}/v1/protected/api-keys`;
2709
2751
  try {
2710
- logger4.info("[Auth] Creating API key", {
2752
+ logger5.info("[Auth] Creating API key", {
2711
2753
  keyName,
2712
2754
  expiry
2713
2755
  });
@@ -2724,13 +2766,13 @@ async function createApiKeyWithToken(accessToken, keyName, expiry = "90d") {
2724
2766
  }
2725
2767
  }
2726
2768
  );
2727
- logger4.info("[Auth] API key created successfully", {
2769
+ logger5.info("[Auth] API key created successfully", {
2728
2770
  keyId: response.data.id
2729
2771
  });
2730
2772
  return response.data;
2731
2773
  } catch (error) {
2732
2774
  if (error instanceof AxiosError) {
2733
- logger4.error("[Auth] Failed to create API key", {
2775
+ logger5.error("[Auth] Failed to create API key", {
2734
2776
  status: error.response?.status,
2735
2777
  data: error.response?.data
2736
2778
  });
@@ -2770,7 +2812,7 @@ async function performLogin(keyName, keyExpiry = "90d", timeoutMs = 12e4) {
2770
2812
  userId: storedAuth?.userId
2771
2813
  };
2772
2814
  if (keyName && storedAuth?.accessToken) {
2773
- logger4.info("[Auth] Creating API key as explicitly requested", {
2815
+ logger5.info("[Auth] Creating API key as explicitly requested", {
2774
2816
  keyName
2775
2817
  });
2776
2818
  const apiKeyResult = await createApiKeyWithToken(
@@ -2818,7 +2860,7 @@ function performLogout() {
2818
2860
  const authService = getAuthService();
2819
2861
  authService.logout();
2820
2862
  deleteApiKeyData();
2821
- logger4.info("[Auth] Logged out successfully");
2863
+ logger5.info("[Auth] Logged out successfully");
2822
2864
  }
2823
2865
  function getCallerCredentials() {
2824
2866
  const apiKeyData = getValidApiKeyData();
@@ -2898,6 +2940,34 @@ function toolRequiresAuth(toolName) {
2898
2940
  ];
2899
2941
  return !noAuthTools.includes(toolName);
2900
2942
  }
2943
+
2944
+ // packages/mcps/src/mcp/index.ts
2945
+ var mcp_exports = {};
2946
+ __export(mcp_exports, {
2947
+ agents: () => agents_exports,
2948
+ e2e: () => e2e_exports2,
2949
+ localQa: () => local_exports2,
2950
+ plugins: () => plugins_exports,
2951
+ qa: () => e2e_exports2,
2952
+ skills: () => skills_exports,
2953
+ tools: () => tools_exports
2954
+ });
2955
+
2956
+ // packages/mcps/src/mcp/tools/index.ts
2957
+ var tools_exports = {};
2958
+ __export(tools_exports, {
2959
+ e2e: () => e2e_exports,
2960
+ localQa: () => local_exports,
2961
+ qa: () => e2e_exports
2962
+ });
2963
+
2964
+ // packages/mcps/src/mcp/tools/e2e/index.ts
2965
+ var e2e_exports = {};
2966
+ __export(e2e_exports, {
2967
+ allQaToolDefinitions: () => allQaToolDefinitions,
2968
+ executeQaTool: () => executeQaTool,
2969
+ getQaToolByName: () => getQaToolByName
2970
+ });
2901
2971
  var MuggleEntityIdSchema = z.string().uuid();
2902
2972
  var LocalExecutionContextInputSchema = z.object({
2903
2973
  originalUrl: z.string().url().describe("Original local URL used during local execution (typically localhost)"),
@@ -3383,17 +3453,17 @@ var PromptServiceClient = class {
3383
3453
  * @param path - Path to validate.
3384
3454
  * @throws GatewayError if path is not allowed.
3385
3455
  */
3386
- validatePath(path15) {
3387
- const isAllowed = ALLOWED_UPSTREAM_PREFIXES.some((prefix) => path15.startsWith(prefix));
3456
+ validatePath(path10) {
3457
+ const isAllowed = ALLOWED_UPSTREAM_PREFIXES.some((prefix) => path10.startsWith(prefix));
3388
3458
  if (!isAllowed) {
3389
- const logger14 = getLogger();
3390
- logger14.error("Path not in allowlist", {
3391
- path: path15,
3459
+ const logger6 = getLogger();
3460
+ logger6.error("Path not in allowlist", {
3461
+ path: path10,
3392
3462
  allowedPrefixes: ALLOWED_UPSTREAM_PREFIXES
3393
3463
  });
3394
3464
  throw new GatewayError({
3395
3465
  code: "FORBIDDEN" /* FORBIDDEN */,
3396
- message: `Path '${path15}' is not allowed`
3466
+ message: `Path '${path10}' is not allowed`
3397
3467
  });
3398
3468
  }
3399
3469
  }
@@ -3502,7 +3572,7 @@ var PromptServiceClient = class {
3502
3572
  * @throws GatewayError on validation or upstream errors.
3503
3573
  */
3504
3574
  async execute(call, credentials, correlationId) {
3505
- const logger14 = getLogger();
3575
+ const logger6 = getLogger();
3506
3576
  if (!credentials.bearerToken && !credentials.apiKey) {
3507
3577
  throw new GatewayError({
3508
3578
  code: "UNAUTHORIZED" /* UNAUTHORIZED */,
@@ -3514,7 +3584,7 @@ var PromptServiceClient = class {
3514
3584
  const headers = this.buildHeaders(credentials, correlationId);
3515
3585
  const timeout = call.timeoutMs || this.requestTimeoutMs;
3516
3586
  const startTime = Date.now();
3517
- logger14.info("Upstream request", {
3587
+ logger6.info("Upstream request", {
3518
3588
  correlationId,
3519
3589
  method: call.method,
3520
3590
  path: call.path,
@@ -3541,7 +3611,7 @@ var PromptServiceClient = class {
3541
3611
  }
3542
3612
  const response = await this.httpClient.request(requestConfig);
3543
3613
  const latency = Date.now() - startTime;
3544
- logger14.info("Upstream response", {
3614
+ logger6.info("Upstream response", {
3545
3615
  correlationId,
3546
3616
  statusCode: response.status,
3547
3617
  latencyMs: latency
@@ -3572,7 +3642,7 @@ var PromptServiceClient = class {
3572
3642
  throw error;
3573
3643
  }
3574
3644
  if (error instanceof AxiosError) {
3575
- logger14.error("Upstream request failed", {
3645
+ logger6.error("Upstream request failed", {
3576
3646
  correlationId,
3577
3647
  error: error.message,
3578
3648
  code: error.code,
@@ -3591,7 +3661,7 @@ var PromptServiceClient = class {
3591
3661
  details: { upstreamPath: call.path }
3592
3662
  });
3593
3663
  }
3594
- logger14.error("Unknown upstream error", {
3664
+ logger6.error("Unknown upstream error", {
3595
3665
  correlationId,
3596
3666
  error: String(error),
3597
3667
  latencyMs: latency
@@ -5007,7 +5077,7 @@ function defaultResponseMapper(response) {
5007
5077
  return response.data;
5008
5078
  }
5009
5079
  async function executeQaTool(toolName, input, correlationId) {
5010
- const logger14 = createChildLogger(correlationId);
5080
+ const logger6 = createChildLogger(correlationId);
5011
5081
  const tool = getQaToolByName(toolName);
5012
5082
  if (!tool) {
5013
5083
  return {
@@ -5048,7 +5118,7 @@ async function executeQaTool(toolName, input, correlationId) {
5048
5118
  }
5049
5119
  } catch (error) {
5050
5120
  if (error instanceof GatewayError) {
5051
- logger14.warn("Tool call failed with gateway error", {
5121
+ logger6.warn("Tool call failed with gateway error", {
5052
5122
  tool: toolName,
5053
5123
  code: error.code,
5054
5124
  message: error.message
@@ -5059,7 +5129,7 @@ async function executeQaTool(toolName, input, correlationId) {
5059
5129
  };
5060
5130
  }
5061
5131
  const errorMessage = error instanceof Error ? error.message : String(error);
5062
- logger14.error("Tool call failed", { tool: toolName, error: errorMessage });
5132
+ logger6.error("Tool call failed", { tool: toolName, error: errorMessage });
5063
5133
  return {
5064
5134
  content: JSON.stringify({ error: "INTERNAL_ERROR", message: errorMessage }),
5065
5135
  isError: true
@@ -5067,82 +5137,12 @@ async function executeQaTool(toolName, input, correlationId) {
5067
5137
  }
5068
5138
  }
5069
5139
 
5070
- // packages/mcps/src/mcp/tools/e2e/index.ts
5071
- var e2e_exports = {};
5072
- __export(e2e_exports, {
5073
- allQaToolDefinitions: () => allQaToolDefinitions,
5074
- executeQaTool: () => executeQaTool,
5075
- getQaToolByName: () => getQaToolByName
5076
- });
5077
-
5078
- // packages/mcps/src/mcp/e2e/index.ts
5079
- function getQaTools() {
5080
- return allQaToolDefinitions.map((tool) => ({
5081
- name: tool.name,
5082
- description: tool.description,
5083
- inputSchema: tool.inputSchema,
5084
- requiresAuth: tool.requiresAuth !== false,
5085
- execute: async (params) => {
5086
- return executeQaTool(tool.name, params.input, params.correlationId);
5087
- }
5088
- }));
5089
- }
5090
-
5091
- // packages/mcps/src/mcp/local/index.ts
5092
- var local_exports2 = {};
5093
- __export(local_exports2, {
5094
- AuthLoginInputSchema: () => AuthLoginInputSchema2,
5095
- AuthPollInputSchema: () => AuthPollInputSchema2,
5096
- AuthService: () => AuthService,
5097
- CancelExecutionInputSchema: () => CancelExecutionInputSchema,
5098
- CleanupSessionsInputSchema: () => CleanupSessionsInputSchema,
5099
- CloudMappingEntityType: () => CloudMappingEntityType,
5100
- DeviceCodePollStatus: () => DeviceCodePollStatus,
5101
- EmptyInputSchema: () => EmptyInputSchema2,
5102
- ExecuteReplayInputSchema: () => ExecuteReplayInputSchema,
5103
- ExecuteTestGenerationInputSchema: () => ExecuteTestGenerationInputSchema,
5104
- ExecutionStatus: () => ExecutionStatus,
5105
- HealthStatus: () => HealthStatus,
5106
- ListSessionsInputSchema: () => ListSessionsInputSchema,
5107
- LocalRunStatus: () => LocalRunStatus,
5108
- LocalRunType: () => LocalRunType,
5109
- LocalTestScriptStatus: () => LocalTestScriptStatus,
5110
- LocalWorkflowFileEntityType: () => LocalWorkflowFileEntityType,
5111
- LocalWorkflowRunStatus: () => LocalWorkflowRunStatus,
5112
- MuggleEntityIdSchema: () => MuggleEntityIdSchema,
5113
- PublishTestScriptInputSchema: () => PublishTestScriptInputSchema,
5114
- RunResultGetInputSchema: () => RunResultGetInputSchema,
5115
- RunResultListInputSchema: () => RunResultListInputSchema,
5116
- RunResultStorageService: () => RunResultStorageService,
5117
- SessionStatus: () => SessionStatus,
5118
- StorageService: () => StorageService,
5119
- TestCaseDetailsSchema: () => TestCaseDetailsSchema,
5120
- TestResultStatus: () => TestResultStatus,
5121
- TestScriptDetailsSchema: () => TestScriptDetailsSchema,
5122
- TestScriptGetInputSchema: () => TestScriptGetInputSchema2,
5123
- TestScriptListInputSchema: () => TestScriptListInputSchema2,
5124
- allLocalQaTools: () => allLocalQaTools,
5125
- cancelExecution: () => cancelExecution,
5126
- executeReplay: () => executeReplay,
5127
- executeTestGeneration: () => executeTestGeneration,
5128
- executeTool: () => executeTool,
5129
- getAuthService: () => getAuthService,
5130
- getLocalQaTools: () => getLocalQaTools,
5131
- getRunResultStorageService: () => getRunResultStorageService,
5132
- getStorageService: () => getStorageService,
5133
- getTool: () => getTool,
5134
- listActiveExecutions: () => listActiveExecutions,
5135
- resetAuthService: () => resetAuthService,
5136
- resetRunResultStorageService: () => resetRunResultStorageService,
5137
- resetStorageService: () => resetStorageService
5138
- });
5139
-
5140
- // packages/mcps/src/mcp/tools/local/index.ts
5141
- var local_exports = {};
5142
- __export(local_exports, {
5143
- allLocalQaTools: () => allLocalQaTools,
5144
- executeTool: () => executeTool,
5145
- getTool: () => getTool
5140
+ // packages/mcps/src/mcp/tools/local/index.ts
5141
+ var local_exports = {};
5142
+ __export(local_exports, {
5143
+ allLocalQaTools: () => allLocalQaTools,
5144
+ executeTool: () => executeTool,
5145
+ getTool: () => getTool
5146
5146
  });
5147
5147
  var AuthLoginInputSchema2 = z.object({
5148
5148
  waitForCompletion: z.boolean().optional().describe("Whether to wait for browser login completion before returning. Default: true"),
@@ -5241,12 +5241,12 @@ var CleanupSessionsInputSchema = z.object({
5241
5241
 
5242
5242
  // packages/mcps/src/mcp/tools/local/tool-registry.ts
5243
5243
  function createChildLogger2(correlationId) {
5244
- const logger14 = getLogger();
5244
+ const logger6 = getLogger();
5245
5245
  return {
5246
- info: (msg, meta) => logger14.info(msg, { ...meta, correlationId }),
5247
- error: (msg, meta) => logger14.error(msg, { ...meta, correlationId }),
5248
- warn: (msg, meta) => logger14.warn(msg, { ...meta, correlationId }),
5249
- debug: (msg, meta) => logger14.debug(msg, { ...meta, correlationId })
5246
+ info: (msg, meta) => logger6.info(msg, { ...meta, correlationId }),
5247
+ error: (msg, meta) => logger6.error(msg, { ...meta, correlationId }),
5248
+ warn: (msg, meta) => logger6.warn(msg, { ...meta, correlationId }),
5249
+ debug: (msg, meta) => logger6.debug(msg, { ...meta, correlationId })
5250
5250
  };
5251
5251
  }
5252
5252
  var checkStatusTool = {
@@ -5254,8 +5254,8 @@ var checkStatusTool = {
5254
5254
  description: "Check the status of Muggle Test Local. This verifies the connection to web-service and shows current session information.",
5255
5255
  inputSchema: EmptyInputSchema2,
5256
5256
  execute: async (ctx) => {
5257
- const logger14 = createChildLogger2(ctx.correlationId);
5258
- logger14.info("Executing muggle-local-check-status");
5257
+ const logger6 = createChildLogger2(ctx.correlationId);
5258
+ logger6.info("Executing muggle-local-check-status");
5259
5259
  const authService = getAuthService();
5260
5260
  const storageService = getStorageService();
5261
5261
  const authStatus = authService.getAuthStatus();
@@ -5278,8 +5278,8 @@ var listSessionsTool = {
5278
5278
  description: "List all stored testing sessions. Shows session IDs, status, and metadata for each session.",
5279
5279
  inputSchema: ListSessionsInputSchema,
5280
5280
  execute: async (ctx) => {
5281
- const logger14 = createChildLogger2(ctx.correlationId);
5282
- logger14.info("Executing muggle-local-list-sessions");
5281
+ const logger6 = createChildLogger2(ctx.correlationId);
5282
+ logger6.info("Executing muggle-local-list-sessions");
5283
5283
  const input = ListSessionsInputSchema.parse(ctx.input);
5284
5284
  const storageService = getStorageService();
5285
5285
  const sessions = storageService.listSessionsWithMetadata();
@@ -5306,8 +5306,8 @@ var runResultListTool = {
5306
5306
  description: "List run results (test generation and replay history), optionally filtered by cloud test case ID.",
5307
5307
  inputSchema: RunResultListInputSchema,
5308
5308
  execute: async (ctx) => {
5309
- const logger14 = createChildLogger2(ctx.correlationId);
5310
- logger14.info("Executing muggle-local-run-result-list");
5309
+ const logger6 = createChildLogger2(ctx.correlationId);
5310
+ logger6.info("Executing muggle-local-run-result-list");
5311
5311
  const input = RunResultListInputSchema.parse(ctx.input);
5312
5312
  const storage = getRunResultStorageService();
5313
5313
  let results = storage.listRunResults();
@@ -5331,8 +5331,8 @@ var runResultGetTool = {
5331
5331
  description: "Get detailed information about a run result including screenshots and action script output.",
5332
5332
  inputSchema: RunResultGetInputSchema,
5333
5333
  execute: async (ctx) => {
5334
- const logger14 = createChildLogger2(ctx.correlationId);
5335
- logger14.info("Executing muggle-local-run-result-get");
5334
+ const logger6 = createChildLogger2(ctx.correlationId);
5335
+ logger6.info("Executing muggle-local-run-result-get");
5336
5336
  const input = RunResultGetInputSchema.parse(ctx.input);
5337
5337
  const storage = getRunResultStorageService();
5338
5338
  const result = storage.getRunResult(input.runId);
@@ -5354,7 +5354,7 @@ var runResultGetTool = {
5354
5354
  const testScript = storage.getTestScript(result.testScriptId);
5355
5355
  testScriptSteps = testScript?.actionScript?.length;
5356
5356
  }
5357
- if (result.artifactsDir && fs3.existsSync(result.artifactsDir)) {
5357
+ if (result.artifactsDir && fs5.existsSync(result.artifactsDir)) {
5358
5358
  contentParts.push(
5359
5359
  "",
5360
5360
  "### Artifacts (view action script + screenshots)",
@@ -5368,20 +5368,20 @@ var runResultGetTool = {
5368
5368
  const stdoutLogPath = path2.join(result.artifactsDir, "stdout.log");
5369
5369
  const stderrLogPath = path2.join(result.artifactsDir, "stderr.log");
5370
5370
  const artifactItems = [];
5371
- if (fs3.existsSync(actionScriptPath)) {
5371
+ if (fs5.existsSync(actionScriptPath)) {
5372
5372
  artifactItems.push("- `action-script.json` \u2014 generated test steps");
5373
5373
  }
5374
- if (fs3.existsSync(resultsMdPath)) {
5374
+ if (fs5.existsSync(resultsMdPath)) {
5375
5375
  artifactItems.push("- `results.md` \u2014 step-by-step report with screenshot links");
5376
5376
  }
5377
- if (fs3.existsSync(screenshotsDir)) {
5378
- const screenshots = fs3.readdirSync(screenshotsDir).filter((f) => /\.(png|jpg|jpeg|webp)$/i.test(f));
5377
+ if (fs5.existsSync(screenshotsDir)) {
5378
+ const screenshots = fs5.readdirSync(screenshotsDir).filter((f) => /\.(png|jpg|jpeg|webp)$/i.test(f));
5379
5379
  artifactItems.push(`- \`screenshots/\` \u2014 ${screenshots.length} image(s)`);
5380
5380
  }
5381
- if (fs3.existsSync(stdoutLogPath)) {
5381
+ if (fs5.existsSync(stdoutLogPath)) {
5382
5382
  artifactItems.push("- `stdout.log` \u2014 electron-app stdout output");
5383
5383
  }
5384
- if (fs3.existsSync(stderrLogPath)) {
5384
+ if (fs5.existsSync(stderrLogPath)) {
5385
5385
  artifactItems.push("- `stderr.log` \u2014 electron-app stderr output");
5386
5386
  }
5387
5387
  if (artifactItems.length > 0) {
@@ -5406,8 +5406,8 @@ var testScriptListTool = {
5406
5406
  description: "List locally generated test scripts, optionally filtered by cloud test case ID.",
5407
5407
  inputSchema: TestScriptListInputSchema2,
5408
5408
  execute: async (ctx) => {
5409
- const logger14 = createChildLogger2(ctx.correlationId);
5410
- logger14.info("Executing muggle-local-test-script-list");
5409
+ const logger6 = createChildLogger2(ctx.correlationId);
5410
+ logger6.info("Executing muggle-local-test-script-list");
5411
5411
  const input = TestScriptListInputSchema2.parse(ctx.input);
5412
5412
  const storage = getRunResultStorageService();
5413
5413
  let scripts = storage.listTestScripts();
@@ -5427,8 +5427,8 @@ var testScriptGetTool = {
5427
5427
  description: "Get details of a locally generated test script including action script steps.",
5428
5428
  inputSchema: TestScriptGetInputSchema2,
5429
5429
  execute: async (ctx) => {
5430
- const logger14 = createChildLogger2(ctx.correlationId);
5431
- logger14.info("Executing muggle-local-test-script-get");
5430
+ const logger6 = createChildLogger2(ctx.correlationId);
5431
+ logger6.info("Executing muggle-local-test-script-get");
5432
5432
  const input = TestScriptGetInputSchema2.parse(ctx.input);
5433
5433
  const storage = getRunResultStorageService();
5434
5434
  const testScript = storage.getTestScript(input.testScriptId);
@@ -5453,8 +5453,8 @@ var executeTestGenerationTool = {
5453
5453
  description: "Generate an end-to-end (E2E) acceptance test script by launching a real browser against your web app. The browser navigates your app, executes the test case steps (like signing up, filling forms, clicking through flows), and produces a replayable test script with screenshots. Use this to create new browser tests for any user flow. Requires a test case (from muggle-remote-test-case-get) and a localhost URL. Launches an Electron browser \u2014 defaults to a visible window; pass showUi: false to run headless.",
5454
5454
  inputSchema: ExecuteTestGenerationInputSchema,
5455
5455
  execute: async (ctx) => {
5456
- const logger14 = createChildLogger2(ctx.correlationId);
5457
- logger14.info("Executing muggle-local-execute-test-generation");
5456
+ const logger6 = createChildLogger2(ctx.correlationId);
5457
+ logger6.info("Executing muggle-local-execute-test-generation");
5458
5458
  const input = ExecuteTestGenerationInputSchema.parse(ctx.input);
5459
5459
  const showUi = input.showUi !== false;
5460
5460
  try {
@@ -5481,7 +5481,7 @@ var executeTestGenerationTool = {
5481
5481
  };
5482
5482
  } catch (error) {
5483
5483
  const errorMessage = error instanceof Error ? error.message : String(error);
5484
- logger14.error("Test generation failed", { error: errorMessage });
5484
+ logger6.error("Test generation failed", { error: errorMessage });
5485
5485
  return { content: `Test generation failed: ${errorMessage}`, isError: true };
5486
5486
  }
5487
5487
  }
@@ -5491,8 +5491,8 @@ var executeReplayTool = {
5491
5491
  description: "Replay an existing E2E acceptance test script in a real browser to verify your app still works correctly \u2014 use this for regression testing after code changes. The browser executes each saved step and captures screenshots so you can see what happened. Requires: (1) test script metadata from muggle-remote-test-script-get, (2) actionScript content from muggle-remote-action-script-get using the testScript.actionScriptId, and (3) a localhost URL. Launches an Electron browser \u2014 defaults to a visible window; pass showUi: false to run headless.",
5492
5492
  inputSchema: ExecuteReplayInputSchema,
5493
5493
  execute: async (ctx) => {
5494
- const logger14 = createChildLogger2(ctx.correlationId);
5495
- logger14.info("Executing muggle-local-execute-replay");
5494
+ const logger6 = createChildLogger2(ctx.correlationId);
5495
+ logger6.info("Executing muggle-local-execute-replay");
5496
5496
  const input = ExecuteReplayInputSchema.parse(ctx.input);
5497
5497
  const showUi = input.showUi !== false;
5498
5498
  try {
@@ -5520,7 +5520,7 @@ var executeReplayTool = {
5520
5520
  };
5521
5521
  } catch (error) {
5522
5522
  const errorMessage = error instanceof Error ? error.message : String(error);
5523
- logger14.error("Test replay failed", { error: errorMessage });
5523
+ logger6.error("Test replay failed", { error: errorMessage });
5524
5524
  return { content: `Test replay failed: ${errorMessage}`, isError: true };
5525
5525
  }
5526
5526
  }
@@ -5530,8 +5530,8 @@ var cancelExecutionTool = {
5530
5530
  description: "Cancel an active test generation or replay execution.",
5531
5531
  inputSchema: CancelExecutionInputSchema,
5532
5532
  execute: async (ctx) => {
5533
- const logger14 = createChildLogger2(ctx.correlationId);
5534
- logger14.info("Executing muggle-local-cancel-execution");
5533
+ const logger6 = createChildLogger2(ctx.correlationId);
5534
+ logger6.info("Executing muggle-local-cancel-execution");
5535
5535
  const input = CancelExecutionInputSchema.parse(ctx.input);
5536
5536
  const cancelled = cancelExecution({ runId: input.runId });
5537
5537
  if (cancelled) {
@@ -5545,8 +5545,8 @@ var publishTestScriptTool = {
5545
5545
  description: "Publish a locally generated test script to the cloud. Uses the run ID from muggle_execute_test_generation to find the script and uploads it to the specified cloud test case. Returns a viewUrl that can be opened in the user's browser to view the published test script on the dashboard.",
5546
5546
  inputSchema: PublishTestScriptInputSchema,
5547
5547
  execute: async (ctx) => {
5548
- const logger14 = createChildLogger2(ctx.correlationId);
5549
- logger14.info("Executing muggle-local-publish-test-script");
5548
+ const logger6 = createChildLogger2(ctx.correlationId);
5549
+ logger6.info("Executing muggle-local-publish-test-script");
5550
5550
  const input = PublishTestScriptInputSchema.parse(ctx.input);
5551
5551
  const storage = getRunResultStorageService();
5552
5552
  const runResult = storage.getRunResult(input.runId);
@@ -5665,7 +5665,7 @@ var publishTestScriptTool = {
5665
5665
  };
5666
5666
  } catch (error) {
5667
5667
  const errorMessage = error instanceof Error ? error.message : String(error);
5668
- logger14.error("Failed to publish local test script to cloud", {
5668
+ logger6.error("Failed to publish local test script to cloud", {
5669
5669
  runId: input.runId,
5670
5670
  cloudTestCaseId: input.cloudTestCaseId,
5671
5671
  error: errorMessage
@@ -5711,7 +5711,161 @@ async function executeTool(name, input, correlationId) {
5711
5711
  return tool.execute({ input, correlationId });
5712
5712
  }
5713
5713
 
5714
+ // packages/mcps/src/mcp/e2e/index.ts
5715
+ var e2e_exports2 = {};
5716
+ __export(e2e_exports2, {
5717
+ ActionScriptGetInputSchema: () => ActionScriptGetInputSchema,
5718
+ ApiKeyCreateInputSchema: () => ApiKeyCreateInputSchema,
5719
+ ApiKeyGetInputSchema: () => ApiKeyGetInputSchema,
5720
+ ApiKeyListInputSchema: () => ApiKeyListInputSchema,
5721
+ ApiKeyRecordIdSchema: () => ApiKeyRecordIdSchema,
5722
+ ApiKeyRevokeInputSchema: () => ApiKeyRevokeInputSchema,
5723
+ AuthLoginInputSchema: () => AuthLoginInputSchema,
5724
+ AuthPollInputSchema: () => AuthPollInputSchema,
5725
+ BulkPreviewJobCancelInputSchema: () => BulkPreviewJobCancelInputSchema,
5726
+ BulkPreviewJobGetInputSchema: () => BulkPreviewJobGetInputSchema,
5727
+ BulkPreviewJobKindSchema: () => BulkPreviewJobKindSchema,
5728
+ BulkPreviewJobListInputSchema: () => BulkPreviewJobListInputSchema,
5729
+ BulkPreviewJobStatusSchema: () => BulkPreviewJobStatusSchema,
5730
+ BulkPreviewPromptSchema: () => BulkPreviewPromptSchema,
5731
+ BulkPreviewSubmitTestCaseInputSchema: () => BulkPreviewSubmitTestCaseInputSchema,
5732
+ BulkPreviewSubmitUseCaseInputSchema: () => BulkPreviewSubmitUseCaseInputSchema,
5733
+ EmptyInputSchema: () => EmptyInputSchema,
5734
+ GatewayError: () => GatewayError,
5735
+ IdSchema: () => IdSchema,
5736
+ LocalExecutionContextInputSchema: () => LocalExecutionContextInputSchema,
5737
+ LocalRunUploadInputSchema: () => LocalRunUploadInputSchema,
5738
+ McpErrorCode: () => McpErrorCode,
5739
+ MuggleEntityIdSchema: () => MuggleEntityIdSchema,
5740
+ PaginationInputSchema: () => PaginationInputSchema,
5741
+ PrdFileDeleteInputSchema: () => PrdFileDeleteInputSchema,
5742
+ PrdFileListInputSchema: () => PrdFileListInputSchema,
5743
+ PrdFileProcessLatestRunInputSchema: () => PrdFileProcessLatestRunInputSchema,
5744
+ PrdFileProcessStartInputSchema: () => PrdFileProcessStartInputSchema,
5745
+ PrdFileUploadInputSchema: () => PrdFileUploadInputSchema,
5746
+ ProjectCreateInputSchema: () => ProjectCreateInputSchema,
5747
+ ProjectDeleteInputSchema: () => ProjectDeleteInputSchema,
5748
+ ProjectGetInputSchema: () => ProjectGetInputSchema,
5749
+ ProjectListInputSchema: () => ProjectListInputSchema,
5750
+ ProjectTestResultsSummaryInputSchema: () => ProjectTestResultsSummaryInputSchema,
5751
+ ProjectTestRunsSummaryInputSchema: () => ProjectTestRunsSummaryInputSchema,
5752
+ ProjectTestScriptsSummaryInputSchema: () => ProjectTestScriptsSummaryInputSchema,
5753
+ ProjectUpdateInputSchema: () => ProjectUpdateInputSchema,
5754
+ PromptServiceClient: () => PromptServiceClient,
5755
+ RecommendCicdSetupInputSchema: () => RecommendCicdSetupInputSchema,
5756
+ RecommendScheduleInputSchema: () => RecommendScheduleInputSchema,
5757
+ ReportCostQueryInputSchema: () => ReportCostQueryInputSchema,
5758
+ ReportFinalGenerateInputSchema: () => ReportFinalGenerateInputSchema,
5759
+ ReportPreferencesUpsertInputSchema: () => ReportPreferencesUpsertInputSchema,
5760
+ ReportStatsSummaryInputSchema: () => ReportStatsSummaryInputSchema,
5761
+ RunBatchIdSchema: () => RunBatchIdSchema,
5762
+ SecretCreateInputSchema: () => SecretCreateInputSchema,
5763
+ SecretDeleteInputSchema: () => SecretDeleteInputSchema,
5764
+ SecretGetInputSchema: () => SecretGetInputSchema,
5765
+ SecretListInputSchema: () => SecretListInputSchema,
5766
+ SecretUpdateInputSchema: () => SecretUpdateInputSchema,
5767
+ StripePaymentMethodIdSchema: () => StripePaymentMethodIdSchema,
5768
+ TestCaseCreateInputSchema: () => TestCaseCreateInputSchema,
5769
+ TestCaseGenerateFromPromptInputSchema: () => TestCaseGenerateFromPromptInputSchema,
5770
+ TestCaseGetInputSchema: () => TestCaseGetInputSchema,
5771
+ TestCaseListByUseCaseInputSchema: () => TestCaseListByUseCaseInputSchema,
5772
+ TestCaseListInputSchema: () => TestCaseListInputSchema,
5773
+ TestScriptGetInputSchema: () => TestScriptGetInputSchema,
5774
+ TestScriptListInputSchema: () => TestScriptListInputSchema,
5775
+ TokenPackageIdSchema: () => TokenPackageIdSchema,
5776
+ TokenUsageFilterTypeSchema: () => TokenUsageFilterTypeSchema,
5777
+ UseCaseCandidatesApproveInputSchema: () => UseCaseCandidatesApproveInputSchema,
5778
+ UseCaseCreateFromPromptsInputSchema: () => UseCaseCreateFromPromptsInputSchema,
5779
+ UseCaseCreateInputSchema: () => UseCaseCreateInputSchema,
5780
+ UseCaseDiscoveryMemoryGetInputSchema: () => UseCaseDiscoveryMemoryGetInputSchema,
5781
+ UseCaseGetInputSchema: () => UseCaseGetInputSchema,
5782
+ UseCaseListInputSchema: () => UseCaseListInputSchema,
5783
+ UseCasePromptPreviewInputSchema: () => UseCasePromptPreviewInputSchema,
5784
+ UseCaseUpdateFromPromptInputSchema: () => UseCaseUpdateFromPromptInputSchema,
5785
+ WalletAutoTopUpSetPaymentMethodInputSchema: () => WalletAutoTopUpSetPaymentMethodInputSchema,
5786
+ WalletAutoTopUpUpdateInputSchema: () => WalletAutoTopUpUpdateInputSchema,
5787
+ WalletPaymentMethodCreateSetupSessionInputSchema: () => WalletPaymentMethodCreateSetupSessionInputSchema,
5788
+ WalletPaymentMethodListInputSchema: () => WalletPaymentMethodListInputSchema,
5789
+ WalletTopUpInputSchema: () => WalletTopUpInputSchema,
5790
+ WorkflowCancelRunInputSchema: () => WorkflowCancelRunInputSchema,
5791
+ WorkflowCancelRuntimeInputSchema: () => WorkflowCancelRuntimeInputSchema,
5792
+ WorkflowGetLatestRunInputSchema: () => WorkflowGetLatestRunInputSchema,
5793
+ WorkflowGetLatestScriptGenByTestCaseInputSchema: () => WorkflowGetLatestScriptGenByTestCaseInputSchema,
5794
+ WorkflowGetReplayBulkBatchSummaryInputSchema: () => WorkflowGetReplayBulkBatchSummaryInputSchema,
5795
+ WorkflowListRuntimesInputSchema: () => WorkflowListRuntimesInputSchema,
5796
+ WorkflowMemoryParamsSchema: () => WorkflowMemoryParamsSchema,
5797
+ WorkflowParamsSchema: () => WorkflowParamsSchema,
5798
+ WorkflowStartTestCaseDetectionInputSchema: () => WorkflowStartTestCaseDetectionInputSchema,
5799
+ WorkflowStartTestScriptGenerationInputSchema: () => WorkflowStartTestScriptGenerationInputSchema,
5800
+ WorkflowStartTestScriptReplayBulkInputSchema: () => WorkflowStartTestScriptReplayBulkInputSchema,
5801
+ WorkflowStartTestScriptReplayInputSchema: () => WorkflowStartTestScriptReplayInputSchema,
5802
+ WorkflowStartWebsiteScanInputSchema: () => WorkflowStartWebsiteScanInputSchema,
5803
+ allQaToolDefinitions: () => allQaToolDefinitions,
5804
+ executeQaTool: () => executeQaTool,
5805
+ getPromptServiceClient: () => getPromptServiceClient,
5806
+ getQaToolByName: () => getQaToolByName,
5807
+ getQaTools: () => getQaTools
5808
+ });
5809
+ function getQaTools() {
5810
+ return allQaToolDefinitions.map((tool) => ({
5811
+ name: tool.name,
5812
+ description: tool.description,
5813
+ inputSchema: tool.inputSchema,
5814
+ requiresAuth: tool.requiresAuth !== false,
5815
+ execute: async (params) => {
5816
+ return executeQaTool(tool.name, params.input, params.correlationId);
5817
+ }
5818
+ }));
5819
+ }
5820
+
5714
5821
  // packages/mcps/src/mcp/local/index.ts
5822
+ var local_exports2 = {};
5823
+ __export(local_exports2, {
5824
+ AuthLoginInputSchema: () => AuthLoginInputSchema2,
5825
+ AuthPollInputSchema: () => AuthPollInputSchema2,
5826
+ AuthService: () => AuthService,
5827
+ CancelExecutionInputSchema: () => CancelExecutionInputSchema,
5828
+ CleanupSessionsInputSchema: () => CleanupSessionsInputSchema,
5829
+ CloudMappingEntityType: () => CloudMappingEntityType,
5830
+ DeviceCodePollStatus: () => DeviceCodePollStatus,
5831
+ EmptyInputSchema: () => EmptyInputSchema2,
5832
+ ExecuteReplayInputSchema: () => ExecuteReplayInputSchema,
5833
+ ExecuteTestGenerationInputSchema: () => ExecuteTestGenerationInputSchema,
5834
+ ExecutionStatus: () => ExecutionStatus,
5835
+ HealthStatus: () => HealthStatus,
5836
+ ListSessionsInputSchema: () => ListSessionsInputSchema,
5837
+ LocalRunStatus: () => LocalRunStatus,
5838
+ LocalRunType: () => LocalRunType,
5839
+ LocalTestScriptStatus: () => LocalTestScriptStatus,
5840
+ LocalWorkflowFileEntityType: () => LocalWorkflowFileEntityType,
5841
+ LocalWorkflowRunStatus: () => LocalWorkflowRunStatus,
5842
+ MuggleEntityIdSchema: () => MuggleEntityIdSchema,
5843
+ PublishTestScriptInputSchema: () => PublishTestScriptInputSchema,
5844
+ RunResultGetInputSchema: () => RunResultGetInputSchema,
5845
+ RunResultListInputSchema: () => RunResultListInputSchema,
5846
+ RunResultStorageService: () => RunResultStorageService,
5847
+ SessionStatus: () => SessionStatus,
5848
+ StorageService: () => StorageService,
5849
+ TestCaseDetailsSchema: () => TestCaseDetailsSchema,
5850
+ TestResultStatus: () => TestResultStatus,
5851
+ TestScriptDetailsSchema: () => TestScriptDetailsSchema,
5852
+ TestScriptGetInputSchema: () => TestScriptGetInputSchema2,
5853
+ TestScriptListInputSchema: () => TestScriptListInputSchema2,
5854
+ allLocalQaTools: () => allLocalQaTools,
5855
+ cancelExecution: () => cancelExecution,
5856
+ executeReplay: () => executeReplay,
5857
+ executeTestGeneration: () => executeTestGeneration,
5858
+ executeTool: () => executeTool,
5859
+ getAuthService: () => getAuthService,
5860
+ getLocalQaTools: () => getLocalQaTools,
5861
+ getRunResultStorageService: () => getRunResultStorageService,
5862
+ getStorageService: () => getStorageService,
5863
+ getTool: () => getTool,
5864
+ listActiveExecutions: () => listActiveExecutions,
5865
+ resetAuthService: () => resetAuthService,
5866
+ resetRunResultStorageService: () => resetRunResultStorageService,
5867
+ resetStorageService: () => resetStorageService
5868
+ });
5715
5869
  function getLocalQaTools() {
5716
5870
  return allLocalQaTools.map((tool) => ({
5717
5871
  name: tool.name,
@@ -5745,26 +5899,6 @@ function isLocalOnlyTool(toolName) {
5745
5899
  return localOnlyTools.includes(toolName);
5746
5900
  }
5747
5901
 
5748
- // packages/mcps/src/mcp/index.ts
5749
- var mcp_exports = {};
5750
- __export(mcp_exports, {
5751
- agents: () => agents_exports,
5752
- e2e: () => e2e_exports2,
5753
- localQa: () => local_exports2,
5754
- plugins: () => plugins_exports,
5755
- qa: () => e2e_exports2,
5756
- skills: () => skills_exports,
5757
- tools: () => tools_exports
5758
- });
5759
-
5760
- // packages/mcps/src/mcp/tools/index.ts
5761
- var tools_exports = {};
5762
- __export(tools_exports, {
5763
- e2e: () => e2e_exports,
5764
- localQa: () => local_exports,
5765
- qa: () => e2e_exports
5766
- });
5767
-
5768
5902
  // packages/mcps/src/mcp/skills/index.ts
5769
5903
  var skills_exports = {};
5770
5904
 
@@ -5774,2164 +5908,4 @@ var plugins_exports = {};
5774
5908
  // packages/mcps/src/mcp/agents/index.ts
5775
5909
  var agents_exports = {};
5776
5910
 
5777
- // packages/mcps/src/index.ts
5778
- var src_exports = {};
5779
- __export(src_exports, {
5780
- buildElectronAppChecksumsUrl: () => buildElectronAppChecksumsUrl,
5781
- buildElectronAppReleaseAssetUrl: () => buildElectronAppReleaseAssetUrl,
5782
- buildElectronAppReleaseTag: () => buildElectronAppReleaseTag,
5783
- calculateFileChecksum: () => calculateFileChecksum,
5784
- createApiKeyWithToken: () => createApiKeyWithToken,
5785
- createChildLogger: () => createChildLogger,
5786
- deleteApiKeyData: () => deleteApiKeyData,
5787
- deleteCredentials: () => deleteCredentials,
5788
- e2e: () => e2e_exports2,
5789
- getApiKey: () => getApiKey,
5790
- getApiKeyFilePath: () => getApiKeyFilePath,
5791
- getAuthService: () => getAuthService,
5792
- getBundledElectronAppVersion: () => getBundledElectronAppVersion,
5793
- getCallerCredentials: () => getCallerCredentials,
5794
- getCallerCredentialsAsync: () => getCallerCredentialsAsync,
5795
- getChecksumForPlatform: () => getChecksumForPlatform,
5796
- getConfig: () => getConfig,
5797
- getCredentialsFilePath: () => getCredentialsFilePath,
5798
- getDataDir: () => getDataDir2,
5799
- getDownloadBaseUrl: () => getDownloadBaseUrl,
5800
- getElectronAppChecksums: () => getElectronAppChecksums,
5801
- getElectronAppDir: () => getElectronAppDir,
5802
- getElectronAppVersion: () => getElectronAppVersion,
5803
- getElectronAppVersionSource: () => getElectronAppVersionSource,
5804
- getLocalQaTools: () => getLocalQaTools,
5805
- getLogger: () => getLogger,
5806
- getPlatformKey: () => getPlatformKey,
5807
- getQaTools: () => getQaTools,
5808
- getValidApiKeyData: () => getValidApiKeyData,
5809
- getValidCredentials: () => getValidCredentials,
5810
- hasApiKey: () => hasApiKey,
5811
- isElectronAppInstalled: () => isElectronAppInstalled,
5812
- loadApiKeyData: () => loadApiKeyData,
5813
- loadCredentials: () => loadCredentials,
5814
- localQa: () => local_exports2,
5815
- mcp: () => mcp_exports,
5816
- openBrowserUrl: () => openBrowserUrl,
5817
- performLogin: () => performLogin,
5818
- performLogout: () => performLogout,
5819
- pollDeviceCode: () => pollDeviceCode,
5820
- qa: () => e2e_exports2,
5821
- resetConfig: () => resetConfig,
5822
- resetLogger: () => resetLogger,
5823
- saveApiKey: () => saveApiKey,
5824
- saveApiKeyData: () => saveApiKeyData,
5825
- saveCredentials: () => saveCredentials,
5826
- startDeviceCodeFlow: () => startDeviceCodeFlow,
5827
- toolRequiresAuth: () => toolRequiresAuth,
5828
- verifyFileChecksum: () => verifyFileChecksum
5829
- });
5830
- var logger5 = getLogger();
5831
- function getPlatformKey() {
5832
- const os4 = platform();
5833
- const arch3 = process.arch;
5834
- switch (os4) {
5835
- case "darwin":
5836
- return arch3 === "arm64" ? "darwin-arm64" : "darwin-x64";
5837
- case "win32":
5838
- return "win32-x64";
5839
- case "linux":
5840
- return "linux-x64";
5841
- default:
5842
- throw new Error(`Unsupported platform: ${os4}`);
5843
- }
5844
- }
5845
- async function calculateFileChecksum(filePath) {
5846
- return new Promise((resolve4, reject) => {
5847
- const hash = crypto.createHash("sha256");
5848
- const stream = fs3.createReadStream(filePath);
5849
- stream.on("data", (data) => {
5850
- hash.update(data);
5851
- });
5852
- stream.on("end", () => {
5853
- resolve4(hash.digest("hex"));
5854
- });
5855
- stream.on("error", (error) => {
5856
- reject(error);
5857
- });
5858
- });
5859
- }
5860
- async function verifyFileChecksum(filePath, expectedChecksum) {
5861
- if (!expectedChecksum || expectedChecksum.trim() === "") {
5862
- logger5.warn("Checksum verification skipped - no checksum provided", {
5863
- file: path2.basename(filePath)
5864
- });
5865
- return {
5866
- valid: true,
5867
- expected: "",
5868
- actual: "",
5869
- error: "Checksum verification skipped - no checksum configured"
5870
- };
5871
- }
5872
- try {
5873
- const actualChecksum = await calculateFileChecksum(filePath);
5874
- const normalizedExpected = expectedChecksum.toLowerCase().trim();
5875
- const normalizedActual = actualChecksum.toLowerCase();
5876
- const valid = normalizedExpected === normalizedActual;
5877
- if (!valid) {
5878
- logger5.error("Checksum verification failed", {
5879
- file: path2.basename(filePath),
5880
- expected: normalizedExpected,
5881
- actual: normalizedActual
5882
- });
5883
- } else {
5884
- logger5.info("Checksum verified successfully", {
5885
- file: path2.basename(filePath),
5886
- checksum: normalizedActual
5887
- });
5888
- }
5889
- return {
5890
- valid,
5891
- expected: normalizedExpected,
5892
- actual: normalizedActual,
5893
- error: valid ? void 0 : "Checksum mismatch - file may be corrupted or tampered with"
5894
- };
5895
- } catch (error) {
5896
- const errorMessage = error instanceof Error ? error.message : String(error);
5897
- logger5.error("Checksum calculation failed", {
5898
- file: path2.basename(filePath),
5899
- error: errorMessage
5900
- });
5901
- return {
5902
- valid: false,
5903
- expected: expectedChecksum,
5904
- actual: "",
5905
- error: `Failed to calculate checksum: ${errorMessage}`
5906
- };
5907
- }
5908
- }
5909
- function getChecksumForPlatform(checksums) {
5910
- if (!checksums) {
5911
- return "";
5912
- }
5913
- const platformKey = getPlatformKey();
5914
- return checksums[platformKey] || "";
5915
- }
5916
- var registeredTools = [];
5917
- function registerTools(tools) {
5918
- registeredTools = [...registeredTools, ...tools];
5919
- }
5920
- function getAllTools() {
5921
- return registeredTools;
5922
- }
5923
- function clearTools() {
5924
- registeredTools = [];
5925
- }
5926
- function zodToJsonSchema(schema) {
5927
- try {
5928
- if (schema && typeof schema === "object" && "safeParse" in schema) {
5929
- return z.toJSONSchema(schema);
5930
- }
5931
- } catch {
5932
- }
5933
- try {
5934
- const zodSchema = schema;
5935
- if (zodSchema._def) {
5936
- return convertZodDef(zodSchema);
5937
- }
5938
- } catch {
5939
- }
5940
- return {
5941
- type: "object",
5942
- properties: {},
5943
- additionalProperties: true
5944
- };
5945
- }
5946
- function convertZodDef(schema) {
5947
- const zodSchema = schema;
5948
- if (!zodSchema._def) {
5949
- return { type: "object" };
5950
- }
5951
- const def = zodSchema._def;
5952
- const typeName = def.typeName ?? def.type;
5953
- switch (typeName) {
5954
- case "ZodObject":
5955
- case "object": {
5956
- const shapeFromDef = typeof def.shape === "function" ? def.shape() : def.shape;
5957
- const shape = shapeFromDef || zodSchema.shape || {};
5958
- const properties = {};
5959
- const required = [];
5960
- for (const [key, value] of Object.entries(shape)) {
5961
- properties[key] = convertZodDef(value);
5962
- const valueDef = value._def;
5963
- if (valueDef?.typeName !== "ZodOptional") {
5964
- required.push(key);
5965
- }
5966
- }
5967
- const result = {
5968
- type: "object",
5969
- properties
5970
- };
5971
- if (required.length > 0) {
5972
- result.required = required;
5973
- }
5974
- return result;
5975
- }
5976
- case "ZodString":
5977
- case "string": {
5978
- const result = { type: "string" };
5979
- if (def.description) result.description = def.description;
5980
- if (def.checks) {
5981
- for (const check of def.checks) {
5982
- if (check.kind === "min") result.minLength = check.value;
5983
- if (check.kind === "max") result.maxLength = check.value;
5984
- if (check.kind === "url") result.format = "uri";
5985
- if (check.kind === "email") result.format = "email";
5986
- }
5987
- }
5988
- return result;
5989
- }
5990
- case "ZodNumber":
5991
- case "number": {
5992
- const result = { type: "number" };
5993
- if (def.description) result.description = def.description;
5994
- if (def.checks) {
5995
- for (const check of def.checks) {
5996
- if (check.kind === "int") result.type = "integer";
5997
- if (check.kind === "min") result.minimum = check.value;
5998
- if (check.kind === "max") result.maximum = check.value;
5999
- }
6000
- }
6001
- return result;
6002
- }
6003
- case "ZodBoolean":
6004
- case "boolean": {
6005
- const result = { type: "boolean" };
6006
- if (def.description) result.description = def.description;
6007
- return result;
6008
- }
6009
- case "ZodArray":
6010
- case "array": {
6011
- const result = {
6012
- type: "array",
6013
- items: def.innerType ? convertZodDef(def.innerType) : def.element ? convertZodDef(def.element) : {}
6014
- };
6015
- if (def.description) result.description = def.description;
6016
- return result;
6017
- }
6018
- case "ZodEnum":
6019
- case "enum": {
6020
- const enumValues = Array.isArray(def.values) ? def.values : def.values ? Object.values(def.values) : [];
6021
- const result = {
6022
- type: "string",
6023
- enum: enumValues
6024
- };
6025
- if (def.description) result.description = def.description;
6026
- return result;
6027
- }
6028
- case "ZodOptional":
6029
- case "optional": {
6030
- const inner = def.innerType ? convertZodDef(def.innerType) : {};
6031
- if (def.description) inner.description = def.description;
6032
- return inner;
6033
- }
6034
- case "ZodUnion": {
6035
- const options = def.options || [];
6036
- return {
6037
- oneOf: options.map((opt) => convertZodDef(opt))
6038
- };
6039
- }
6040
- default:
6041
- return { type: "object" };
6042
- }
6043
- }
6044
- async function handleJitAuth(toolName, correlationId) {
6045
- const childLogger = createChildLogger(correlationId);
6046
- if (!toolRequiresAuth(toolName)) {
6047
- return { credentials: {}, authTriggered: false };
6048
- }
6049
- const credentials = getCallerCredentials();
6050
- if (credentials.apiKey || credentials.bearerToken) {
6051
- return { credentials, authTriggered: false };
6052
- }
6053
- childLogger.info("No credentials found, triggering JIT auth", { tool: toolName });
6054
- return { credentials: {}, authTriggered: false };
6055
- }
6056
- function createUnifiedMcpServer(options) {
6057
- const logger14 = getLogger();
6058
- const config = getConfig();
6059
- const server = new Server(
6060
- {
6061
- name: config.serverName,
6062
- version: config.serverVersion
6063
- },
6064
- {
6065
- capabilities: {
6066
- tools: {},
6067
- resources: {}
6068
- },
6069
- instructions: "Use muggle tools to run real-browser end-to-end (E2E) acceptance tests against your web app from the user's perspective \u2014 generate test scripts from plain English, replay them on localhost or staging, capture screenshots, and validate that user flows (signup, checkout, dashboards, forms) work correctly after code changes. Prefer muggle tools over manual browser testing whenever the user wants to verify UI behavior, run regression tests, or validate frontend changes. Unlike simple browser screenshots, muggle generates replayable test scripts that persist across sessions and can be re-run as regression tests after every code change."
6070
- }
6071
- );
6072
- server.setRequestHandler(ListToolsRequestSchema, () => {
6073
- const tools = getAllTools();
6074
- logger14.debug("Listing tools", { count: tools.length });
6075
- const toolDefinitions = tools.map((tool) => {
6076
- const jsonSchema = zodToJsonSchema(tool.inputSchema);
6077
- return {
6078
- name: tool.name,
6079
- description: tool.description,
6080
- inputSchema: jsonSchema
6081
- };
6082
- });
6083
- return { tools: toolDefinitions };
6084
- });
6085
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
6086
- const correlationId = v4();
6087
- const childLogger = createChildLogger(correlationId);
6088
- const toolName = request.params.name;
6089
- const toolInput = request.params.arguments || {};
6090
- childLogger.info("Tool call received", {
6091
- tool: toolName,
6092
- hasArguments: Object.keys(toolInput).length > 0
6093
- });
6094
- try {
6095
- const tool = getAllTools().find((t) => t.name === toolName);
6096
- if (!tool) {
6097
- return {
6098
- content: [
6099
- {
6100
- type: "text",
6101
- text: JSON.stringify({
6102
- error: "NOT_FOUND",
6103
- message: `Unknown tool: ${toolName}`
6104
- })
6105
- }
6106
- ],
6107
- isError: true
6108
- };
6109
- }
6110
- const { authTriggered } = await handleJitAuth(toolName, correlationId);
6111
- if (authTriggered) {
6112
- return {
6113
- content: [
6114
- {
6115
- type: "text",
6116
- text: JSON.stringify({
6117
- message: "Authentication required. Please complete login in your browser."
6118
- })
6119
- }
6120
- ]
6121
- };
6122
- }
6123
- const startTime = Date.now();
6124
- const result = await tool.execute({
6125
- input: toolInput,
6126
- correlationId
6127
- });
6128
- const latency = Date.now() - startTime;
6129
- childLogger.info("Tool call completed", {
6130
- tool: toolName,
6131
- latencyMs: latency,
6132
- isError: result.isError
6133
- });
6134
- return {
6135
- content: [
6136
- {
6137
- type: "text",
6138
- text: result.content
6139
- }
6140
- ],
6141
- isError: result.isError
6142
- };
6143
- } catch (error) {
6144
- if (error instanceof ZodError) {
6145
- childLogger.warn("Tool call failed with validation error", {
6146
- tool: toolName,
6147
- errors: error.issues
6148
- });
6149
- const issueMessages = error.issues.slice(0, 3).map((issue) => {
6150
- const path15 = issue.path.join(".");
6151
- return path15 ? `'${path15}': ${issue.message}` : issue.message;
6152
- });
6153
- return {
6154
- content: [
6155
- {
6156
- type: "text",
6157
- text: JSON.stringify({
6158
- error: "INVALID_ARGUMENT",
6159
- message: `Invalid input: ${issueMessages.join("; ")}`
6160
- })
6161
- }
6162
- ],
6163
- isError: true
6164
- };
6165
- }
6166
- childLogger.error("Tool call failed with error", {
6167
- tool: toolName,
6168
- error: String(error)
6169
- });
6170
- return {
6171
- content: [
6172
- {
6173
- type: "text",
6174
- text: JSON.stringify({
6175
- error: "INTERNAL_ERROR",
6176
- message: error instanceof Error ? error.message : "An unexpected error occurred"
6177
- })
6178
- }
6179
- ],
6180
- isError: true
6181
- };
6182
- }
6183
- });
6184
- server.setRequestHandler(ListResourcesRequestSchema, () => {
6185
- logger14.debug("Listing resources");
6186
- return { resources: [] };
6187
- });
6188
- server.setRequestHandler(ReadResourceRequestSchema, (request) => {
6189
- const uri = request.params.uri;
6190
- logger14.debug("Reading resource", { uri });
6191
- return {
6192
- contents: [
6193
- {
6194
- uri,
6195
- mimeType: "text/plain",
6196
- text: `Resource not found: ${uri}`
6197
- }
6198
- ]
6199
- };
6200
- });
6201
- logger14.info("Unified MCP server configured", {
6202
- tools: getAllTools().length,
6203
- enableQaTools: options.enableQaTools,
6204
- enableLocalTools: options.enableLocalTools
6205
- });
6206
- return server;
6207
- }
6208
-
6209
- // src/server/index.ts
6210
- var server_exports = {};
6211
- __export(server_exports, {
6212
- clearTools: () => clearTools,
6213
- createUnifiedMcpServer: () => createUnifiedMcpServer,
6214
- getAllTools: () => getAllTools,
6215
- registerTools: () => registerTools,
6216
- startStdioServer: () => startStdioServer
6217
- });
6218
- var logger6 = getLogger();
6219
- async function startStdioServer(server) {
6220
- logger6.info("Starting stdio server transport");
6221
- const transport = new StdioServerTransport();
6222
- await server.connect(transport);
6223
- logger6.info("Stdio server connected");
6224
- const shutdown = (signal) => {
6225
- logger6.info(`Received ${signal}, shutting down...`);
6226
- process.exit(0);
6227
- };
6228
- process.on("SIGTERM", () => shutdown("SIGTERM"));
6229
- process.on("SIGINT", () => shutdown("SIGINT"));
6230
- }
6231
-
6232
- // src/cli/pr-section/selectors.ts
6233
- var ONE_LINER_BUDGET = 160;
6234
- function selectHero(report) {
6235
- const firstFailed = report.tests.find(
6236
- (t) => t.status === "failed"
6237
- );
6238
- if (firstFailed) {
6239
- const step = firstFailed.steps.find((s) => s.stepIndex === firstFailed.failureStepIndex);
6240
- if (step) {
6241
- return {
6242
- screenshotUrl: step.screenshotUrl,
6243
- testName: firstFailed.name,
6244
- kind: "failure"
6245
- };
6246
- }
6247
- }
6248
- const firstPassedWithSteps = report.tests.find(
6249
- (t) => t.status === "passed" && t.steps.length > 0
6250
- );
6251
- if (firstPassedWithSteps) {
6252
- const lastStep = firstPassedWithSteps.steps[firstPassedWithSteps.steps.length - 1];
6253
- return {
6254
- screenshotUrl: lastStep.screenshotUrl,
6255
- testName: firstPassedWithSteps.name,
6256
- kind: "final"
6257
- };
6258
- }
6259
- return null;
6260
- }
6261
- function buildOneLiner(report) {
6262
- const total = report.tests.length;
6263
- if (total === 0) {
6264
- return "No acceptance tests were executed.";
6265
- }
6266
- const failed = report.tests.filter((t) => t.status === "failed");
6267
- if (failed.length === 0) {
6268
- return `All ${total} acceptance tests passed.`;
6269
- }
6270
- const first = failed[0];
6271
- const prefix = `${failed.length} of ${total} failed \u2014 "${first.name}" broke at step ${first.failureStepIndex}: `;
6272
- const available = ONE_LINER_BUDGET - prefix.length - 1;
6273
- const error = first.error.length > available ? first.error.slice(0, Math.max(0, available - 1)) + "\u2026" : first.error;
6274
- return `${prefix}${error}.`;
6275
- }
6276
-
6277
- // src/cli/pr-section/render.ts
6278
- var DASHBOARD_URL_BASE = "https://www.muggle-ai.com/muggleTestV0/dashboard/projects";
6279
- var ROW_THUMB_WIDTH = 120;
6280
- var DETAIL_THUMB_WIDTH = 200;
6281
- var HERO_WIDTH = 480;
6282
- function thumbnail(url, width) {
6283
- return `<a href="${url}"><img src="${url}" width="${width}"></a>`;
6284
- }
6285
- function counts(report) {
6286
- const passed = report.tests.filter((t) => t.status === "passed").length;
6287
- const failed = report.tests.filter((t) => t.status === "failed").length;
6288
- return { passed, failed, text: `**${passed} passed / ${failed} failed**` };
6289
- }
6290
- function renderSummary(report) {
6291
- const { text: countsLine } = counts(report);
6292
- const oneLiner = buildOneLiner(report);
6293
- const hero = selectHero(report);
6294
- const dashboard = `${DASHBOARD_URL_BASE}/${report.projectId}/scripts`;
6295
- const lines = [
6296
- countsLine,
6297
- "",
6298
- oneLiner,
6299
- ""
6300
- ];
6301
- if (hero) {
6302
- lines.push(
6303
- `<a href="${hero.screenshotUrl}"><img src="${hero.screenshotUrl}" width="${HERO_WIDTH}" alt="${hero.testName}"></a>`,
6304
- ""
6305
- );
6306
- }
6307
- lines.push(`[View project dashboard on muggle-ai.com](${dashboard})`);
6308
- return lines.join("\n");
6309
- }
6310
- function renderRow(test) {
6311
- const link = `[${test.name}](${test.viewUrl})`;
6312
- if (test.status === "passed") {
6313
- const lastStep = test.steps[test.steps.length - 1];
6314
- const thumb2 = lastStep ? thumbnail(lastStep.screenshotUrl, ROW_THUMB_WIDTH) : "\u2014";
6315
- return `| ${link} | \u2705 PASSED | ${thumb2} |`;
6316
- }
6317
- const failStep = test.steps.find((s) => s.stepIndex === test.failureStepIndex);
6318
- const thumb = failStep ? thumbnail(failStep.screenshotUrl, ROW_THUMB_WIDTH) : "\u2014";
6319
- return `| ${link} | \u274C FAILED \u2014 ${test.error} | ${thumb} |`;
6320
- }
6321
- function renderFailureDetails(test) {
6322
- const stepCount = test.steps.length;
6323
- const header2 = `<details>
6324
- <summary>\u{1F4F8} <strong>${test.name}</strong> \u2014 ${stepCount} steps (failed at step ${test.failureStepIndex})</summary>
6325
-
6326
- | # | Action | Screenshot |
6327
- |---|--------|------------|`;
6328
- const rows = test.steps.map((step) => renderFailureStepRow(step, test)).join("\n");
6329
- return `${header2}
6330
- ${rows}
6331
-
6332
- </details>`;
6333
- }
6334
- function renderFailureStepRow(step, test) {
6335
- const isFailure = step.stepIndex === test.failureStepIndex;
6336
- const marker = isFailure ? `${step.stepIndex} \u26A0\uFE0F` : String(step.stepIndex);
6337
- const action = isFailure ? `${step.action} \u2014 **${test.error}**` : step.action;
6338
- return `| ${marker} | ${action} | ${thumbnail(step.screenshotUrl, DETAIL_THUMB_WIDTH)} |`;
6339
- }
6340
- function renderRowsTable(report) {
6341
- if (report.tests.length === 0) {
6342
- return "_No tests were executed._";
6343
- }
6344
- const header2 = "| Test Case | Status | Evidence |\n|-----------|--------|----------|";
6345
- const rows = report.tests.map(renderRow).join("\n");
6346
- return `${header2}
6347
- ${rows}`;
6348
- }
6349
- function renderBody(report, opts) {
6350
- const sections = [
6351
- "## E2E Acceptance Results",
6352
- "",
6353
- renderSummary(report),
6354
- "",
6355
- renderRowsTable(report)
6356
- ];
6357
- const failures = report.tests.filter((t) => t.status === "failed");
6358
- if (failures.length > 0) {
6359
- if (opts.inlineFailureDetails) {
6360
- sections.push("", ...failures.map(renderFailureDetails));
6361
- } else {
6362
- sections.push(
6363
- "",
6364
- "_Full step-by-step evidence in the comment below \u2014 the PR description was too large to inline it._"
6365
- );
6366
- }
6367
- }
6368
- return sections.join("\n");
6369
- }
6370
- function renderComment(report) {
6371
- const failures = report.tests.filter((t) => t.status === "failed");
6372
- if (failures.length === 0) {
6373
- return "";
6374
- }
6375
- const sections = [
6376
- "## E2E acceptance evidence (overflow)",
6377
- "",
6378
- "_This comment was posted because the full step-by-step evidence did not fit in the PR description._",
6379
- "",
6380
- ...failures.map(renderFailureDetails)
6381
- ];
6382
- return sections.join("\n");
6383
- }
6384
-
6385
- // src/cli/pr-section/overflow.ts
6386
- function splitWithOverflow(report, opts) {
6387
- const inlineBody = renderBody(report, { inlineFailureDetails: true });
6388
- const inlineBytes = Buffer.byteLength(inlineBody, "utf-8");
6389
- if (inlineBytes <= opts.maxBodyBytes) {
6390
- return { body: inlineBody, comment: null };
6391
- }
6392
- const spilledBody = renderBody(report, { inlineFailureDetails: false });
6393
- const comment = renderComment(report);
6394
- return {
6395
- body: spilledBody,
6396
- comment: comment.length > 0 ? comment : null
6397
- };
6398
- }
6399
- var StepSchema = z.object({
6400
- stepIndex: z.number().int().nonnegative(),
6401
- action: z.string().min(1),
6402
- screenshotUrl: z.string().url()
6403
- });
6404
- var PassedTestSchema = z.object({
6405
- name: z.string().min(1),
6406
- testCaseId: z.string().min(1),
6407
- testScriptId: z.string().min(1).optional(),
6408
- runId: z.string().min(1),
6409
- viewUrl: z.string().url(),
6410
- status: z.literal("passed"),
6411
- steps: z.array(StepSchema)
6412
- });
6413
- var FailedTestSchema = z.object({
6414
- name: z.string().min(1),
6415
- testCaseId: z.string().min(1),
6416
- testScriptId: z.string().min(1).optional(),
6417
- runId: z.string().min(1),
6418
- viewUrl: z.string().url(),
6419
- status: z.literal("failed"),
6420
- steps: z.array(StepSchema),
6421
- failureStepIndex: z.number().int().nonnegative(),
6422
- error: z.string().min(1),
6423
- artifactsDir: z.string().min(1).optional()
6424
- });
6425
- var TestResultSchema = z.discriminatedUnion("status", [
6426
- PassedTestSchema,
6427
- FailedTestSchema
6428
- ]);
6429
- var E2eReportSchema = z.object({
6430
- projectId: z.string().min(1),
6431
- tests: z.array(TestResultSchema)
6432
- });
6433
-
6434
- // src/cli/pr-section/index.ts
6435
- function buildPrSection(report, opts) {
6436
- return splitWithOverflow(report, opts);
6437
- }
6438
-
6439
- // src/cli/build-pr-section.ts
6440
- var DEFAULT_MAX_BODY_BYTES = 6e4;
6441
- async function readAll(stream) {
6442
- const chunks = [];
6443
- for await (const chunk of stream) {
6444
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
6445
- }
6446
- return Buffer.concat(chunks).toString("utf-8");
6447
- }
6448
- function errMsg(e) {
6449
- return e instanceof Error ? e.message : String(e);
6450
- }
6451
- async function runBuildPrSection(opts) {
6452
- let raw;
6453
- try {
6454
- raw = await readAll(opts.stdin);
6455
- } catch (err) {
6456
- opts.stderrWrite(`build-pr-section: failed to read stdin: ${errMsg(err)}
6457
- `);
6458
- return 1;
6459
- }
6460
- let json;
6461
- try {
6462
- json = JSON.parse(raw);
6463
- } catch (err) {
6464
- opts.stderrWrite(`build-pr-section: failed to parse stdin as JSON: ${errMsg(err)}
6465
- `);
6466
- return 1;
6467
- }
6468
- let report;
6469
- try {
6470
- report = E2eReportSchema.parse(json);
6471
- } catch (err) {
6472
- if (err instanceof ZodError) {
6473
- opts.stderrWrite(
6474
- `build-pr-section: report validation failed:
6475
- ${err.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n")}
6476
- `
6477
- );
6478
- } else {
6479
- opts.stderrWrite(`build-pr-section: report validation failed: ${errMsg(err)}
6480
- `);
6481
- }
6482
- return 1;
6483
- }
6484
- const result = buildPrSection(report, { maxBodyBytes: opts.maxBodyBytes });
6485
- opts.stdoutWrite(JSON.stringify({ body: result.body, comment: result.comment }));
6486
- return 0;
6487
- }
6488
- async function buildPrSectionCommand(options) {
6489
- const maxBodyBytes = options.maxBodyBytes ? Number(options.maxBodyBytes) : DEFAULT_MAX_BODY_BYTES;
6490
- if (!Number.isFinite(maxBodyBytes) || maxBodyBytes <= 0) {
6491
- process.stderr.write(`build-pr-section: --max-body-bytes must be a positive number
6492
- `);
6493
- process.exitCode = 1;
6494
- return;
6495
- }
6496
- const code = await runBuildPrSection({
6497
- stdin: process.stdin,
6498
- stdoutWrite: (s) => process.stdout.write(s),
6499
- stderrWrite: (s) => process.stderr.write(s),
6500
- maxBodyBytes
6501
- });
6502
- if (code !== 0) {
6503
- process.exitCode = code;
6504
- }
6505
- }
6506
- var logger7 = getLogger();
6507
- var ELECTRON_APP_DIR2 = "electron-app";
6508
- var CURSOR_SKILLS_DIR = ".cursor";
6509
- var CURSOR_SKILLS_SUBDIR = "skills";
6510
- var MUGGLE_SKILL_PREFIX = "muggle";
6511
- var INSTALL_MANIFEST_FILE = "install-manifest.json";
6512
- function getElectronAppBaseDir() {
6513
- return path2.join(getDataDir2(), ELECTRON_APP_DIR2);
6514
- }
6515
- function getCursorSkillsDir() {
6516
- return path2.join(homedir(), CURSOR_SKILLS_DIR, CURSOR_SKILLS_SUBDIR);
6517
- }
6518
- function getInstallManifestPath() {
6519
- return path2.join(getDataDir2(), INSTALL_MANIFEST_FILE);
6520
- }
6521
- function readInstallManifest() {
6522
- const manifestPath = getInstallManifestPath();
6523
- if (!existsSync(manifestPath)) {
6524
- return null;
6525
- }
6526
- try {
6527
- const content = readFileSync(manifestPath, "utf-8");
6528
- const manifest = JSON.parse(content);
6529
- if (typeof manifest !== "object" || manifest === null || Array.isArray(manifest)) {
6530
- return null;
6531
- }
6532
- return manifest;
6533
- } catch {
6534
- return null;
6535
- }
6536
- }
6537
- function listObsoleteSkills() {
6538
- const skillsDir = getCursorSkillsDir();
6539
- const manifest = readInstallManifest();
6540
- const obsoleteSkills = [];
6541
- if (!existsSync(skillsDir)) {
6542
- return obsoleteSkills;
6543
- }
6544
- const manifestSkills = new Set(manifest?.skills ?? []);
6545
- try {
6546
- const entries = readdirSync(skillsDir, { withFileTypes: true });
6547
- for (const entry of entries) {
6548
- if (!entry.isDirectory()) {
6549
- continue;
6550
- }
6551
- if (!entry.name.startsWith(MUGGLE_SKILL_PREFIX)) {
6552
- continue;
6553
- }
6554
- if (manifestSkills.has(entry.name)) {
6555
- continue;
6556
- }
6557
- const skillPath = path2.join(skillsDir, entry.name);
6558
- const sizeBytes = getDirectorySize(skillPath);
6559
- obsoleteSkills.push({
6560
- name: entry.name,
6561
- path: skillPath,
6562
- sizeBytes
6563
- });
6564
- }
6565
- } catch (error) {
6566
- const errorMessage = error instanceof Error ? error.message : String(error);
6567
- logger7.warn("Failed to list obsolete skills", { error: errorMessage });
6568
- }
6569
- return obsoleteSkills;
6570
- }
6571
- function cleanupObsoleteSkills(options = {}) {
6572
- const { dryRun = false } = options;
6573
- const obsoleteSkills = listObsoleteSkills();
6574
- const removed = [];
6575
- let freedBytes = 0;
6576
- for (const skill of obsoleteSkills) {
6577
- if (!dryRun) {
6578
- try {
6579
- rmSync(skill.path, { recursive: true, force: true });
6580
- logger7.info("Removed obsolete skill", {
6581
- skill: skill.name,
6582
- freedBytes: skill.sizeBytes
6583
- });
6584
- } catch (error) {
6585
- const errorMessage = error instanceof Error ? error.message : String(error);
6586
- logger7.error("Failed to remove skill", {
6587
- skill: skill.name,
6588
- error: errorMessage
6589
- });
6590
- continue;
6591
- }
6592
- }
6593
- removed.push(skill);
6594
- freedBytes += skill.sizeBytes;
6595
- }
6596
- return { removed, freedBytes };
6597
- }
6598
- function getDirectorySize(dirPath) {
6599
- let totalSize = 0;
6600
- try {
6601
- const entries = readdirSync(dirPath, { withFileTypes: true });
6602
- for (const entry of entries) {
6603
- const fullPath = path2.join(dirPath, entry.name);
6604
- if (entry.isDirectory()) {
6605
- totalSize += getDirectorySize(fullPath);
6606
- } else if (entry.isFile()) {
6607
- try {
6608
- const stats = statSync(fullPath);
6609
- totalSize += stats.size;
6610
- } catch {
6611
- }
6612
- }
6613
- }
6614
- } catch {
6615
- }
6616
- return totalSize;
6617
- }
6618
- function formatBytes(bytes) {
6619
- if (bytes === 0) {
6620
- return "0 B";
6621
- }
6622
- const units = ["B", "KB", "MB", "GB"];
6623
- const k = 1024;
6624
- const i = Math.floor(Math.log(bytes) / Math.log(k));
6625
- const size = bytes / Math.pow(k, i);
6626
- return `${size.toFixed(1)} ${units[i]}`;
6627
- }
6628
- function compareVersions(a, b) {
6629
- const partsA = a.split(".").map(Number);
6630
- const partsB = b.split(".").map(Number);
6631
- for (let i = 0; i < 3; i++) {
6632
- const partA = partsA[i] || 0;
6633
- const partB = partsB[i] || 0;
6634
- if (partA !== partB) {
6635
- return partA - partB;
6636
- }
6637
- }
6638
- return 0;
6639
- }
6640
- function listInstalledVersions() {
6641
- const baseDir = getElectronAppBaseDir();
6642
- const currentVersion = getElectronAppVersion();
6643
- const versions = [];
6644
- if (!existsSync(baseDir)) {
6645
- return versions;
6646
- }
6647
- try {
6648
- const entries = readdirSync(baseDir, { withFileTypes: true });
6649
- for (const entry of entries) {
6650
- if (!entry.isDirectory()) {
6651
- continue;
6652
- }
6653
- if (!/^\d+\.\d+\.\d+$/.test(entry.name)) {
6654
- continue;
6655
- }
6656
- const versionPath = path2.join(baseDir, entry.name);
6657
- const sizeBytes = getDirectorySize(versionPath);
6658
- versions.push({
6659
- version: entry.name,
6660
- path: versionPath,
6661
- sizeBytes,
6662
- isCurrent: entry.name === currentVersion
6663
- });
6664
- }
6665
- } catch (error) {
6666
- const errorMessage = error instanceof Error ? error.message : String(error);
6667
- logger7.warn("Failed to list installed versions", { error: errorMessage });
6668
- }
6669
- versions.sort((a, b) => compareVersions(b.version, a.version));
6670
- return versions;
6671
- }
6672
- function cleanupOldVersions(options = {}) {
6673
- const { all = false, dryRun = false } = options;
6674
- const versions = listInstalledVersions();
6675
- const removed = [];
6676
- let freedBytes = 0;
6677
- const versionsToKeep = all ? 1 : 2;
6678
- let keptCount = 0;
6679
- for (const version of versions) {
6680
- if (version.isCurrent) {
6681
- keptCount++;
6682
- continue;
6683
- }
6684
- if (keptCount < versionsToKeep) {
6685
- keptCount++;
6686
- continue;
6687
- }
6688
- if (!dryRun) {
6689
- try {
6690
- rmSync(version.path, { recursive: true, force: true });
6691
- logger7.info("Removed old version", {
6692
- version: version.version,
6693
- freedBytes: version.sizeBytes
6694
- });
6695
- } catch (error) {
6696
- const errorMessage = error instanceof Error ? error.message : String(error);
6697
- logger7.error("Failed to remove version", {
6698
- version: version.version,
6699
- error: errorMessage
6700
- });
6701
- continue;
6702
- }
6703
- }
6704
- removed.push(version);
6705
- freedBytes += version.sizeBytes;
6706
- }
6707
- return { removed, freedBytes };
6708
- }
6709
- async function versionsCommand() {
6710
- console.log("\nInstalled Electron App Versions");
6711
- console.log("================================\n");
6712
- const versions = listInstalledVersions();
6713
- if (versions.length === 0) {
6714
- console.log("No versions installed.");
6715
- console.log("Run 'muggle setup' to download the Electron app.\n");
6716
- return;
6717
- }
6718
- let totalSize = 0;
6719
- for (const version of versions) {
6720
- const marker = version.isCurrent ? " (current)" : "";
6721
- const size = formatBytes(version.sizeBytes);
6722
- console.log(` v${version.version}${marker} - ${size}`);
6723
- totalSize += version.sizeBytes;
6724
- }
6725
- console.log("");
6726
- console.log(`Total: ${versions.length} version(s), ${formatBytes(totalSize)}`);
6727
- console.log("");
6728
- }
6729
- async function cleanupCommand(options) {
6730
- let totalFreedBytes = 0;
6731
- let totalRemovedCount = 0;
6732
- console.log("\nElectron App Cleanup");
6733
- console.log("====================\n");
6734
- const versions = listInstalledVersions();
6735
- if (versions.length === 0) {
6736
- console.log("No versions installed. Nothing to clean up.\n");
6737
- } else if (versions.length === 1) {
6738
- console.log("Only the current version is installed. Nothing to clean up.\n");
6739
- } else {
6740
- const currentVersion = versions.find((v) => v.isCurrent);
6741
- const oldVersions = versions.filter((v) => !v.isCurrent);
6742
- console.log(`Current version: v${currentVersion?.version ?? "unknown"}`);
6743
- console.log(`Old versions: ${oldVersions.length}`);
6744
- console.log("");
6745
- if (options.dryRun) {
6746
- console.log("Dry run - showing what would be deleted:\n");
6747
- }
6748
- const result = cleanupOldVersions(options);
6749
- if (result.removed.length === 0) {
6750
- if (options.all) {
6751
- console.log("No old versions to remove.\n");
6752
- } else {
6753
- console.log("Keeping one previous version for rollback.");
6754
- console.log("Use --all to remove all old versions.\n");
6755
- }
6756
- } else {
6757
- console.log(options.dryRun ? "Would remove:" : "Removed:");
6758
- for (const version of result.removed) {
6759
- console.log(` v${version.version} (${formatBytes(version.sizeBytes)})`);
6760
- }
6761
- totalFreedBytes += result.freedBytes;
6762
- totalRemovedCount += result.removed.length;
6763
- console.log("");
6764
- }
6765
- }
6766
- if (options.skills) {
6767
- console.log("Skills Cleanup");
6768
- console.log("==============\n");
6769
- const obsoleteSkills = listObsoleteSkills();
6770
- if (obsoleteSkills.length === 0) {
6771
- console.log("No obsolete skills found. Nothing to clean up.\n");
6772
- } else {
6773
- console.log(`Found ${obsoleteSkills.length} obsolete skill(s):
6774
- `);
6775
- if (options.dryRun) {
6776
- console.log("Dry run - showing what would be deleted:\n");
6777
- }
6778
- const skillResult = cleanupObsoleteSkills({ dryRun: options.dryRun });
6779
- console.log(options.dryRun ? "Would remove:" : "Removed:");
6780
- for (const skill of skillResult.removed) {
6781
- console.log(` ${skill.name} (${formatBytes(skill.sizeBytes)})`);
6782
- }
6783
- totalFreedBytes += skillResult.freedBytes;
6784
- totalRemovedCount += skillResult.removed.length;
6785
- console.log("");
6786
- }
6787
- }
6788
- if (totalRemovedCount > 0) {
6789
- console.log(
6790
- `${options.dryRun ? "Would free" : "Freed"}: ${formatBytes(totalFreedBytes)} total`
6791
- );
6792
- console.log("");
6793
- if (options.dryRun) {
6794
- console.log("Run without --dry-run to actually delete.\n");
6795
- }
6796
- }
6797
- logger7.info("Cleanup completed", {
6798
- removed: totalRemovedCount,
6799
- freedBytes: totalFreedBytes,
6800
- dryRun: options.dryRun,
6801
- includeSkills: options.skills
6802
- });
6803
- }
6804
- var logger8 = getLogger();
6805
- function getCursorMcpConfigPath() {
6806
- return join(homedir(), ".cursor", "mcp.json");
6807
- }
6808
- function getExpectedExecutablePath(versionDir) {
6809
- const os4 = platform();
6810
- switch (os4) {
6811
- case "darwin":
6812
- return path2.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
6813
- case "win32":
6814
- return path2.join(versionDir, "MuggleAI.exe");
6815
- case "linux":
6816
- return path2.join(versionDir, "MuggleAI");
6817
- default:
6818
- throw new Error(`Unsupported platform: ${os4}`);
6819
- }
6820
- }
6821
- function verifyElectronAppInstallation() {
6822
- const version = getElectronAppVersion();
6823
- const versionDir = getElectronAppDir(version);
6824
- const executablePath = getExpectedExecutablePath(versionDir);
6825
- const metadataPath = path2.join(versionDir, ".install-metadata.json");
6826
- const result = {
6827
- valid: false,
6828
- versionDir,
6829
- executablePath,
6830
- executableExists: false,
6831
- executableIsFile: false,
6832
- metadataExists: false,
6833
- hasPartialArchive: false
6834
- };
6835
- if (!fs3.existsSync(versionDir)) {
6836
- result.errorDetail = "Version directory does not exist";
6837
- return result;
6838
- }
6839
- const archivePatterns = ["MuggleAI-darwin", "MuggleAI-win32", "MuggleAI-linux"];
6840
- try {
6841
- const files = fs3.readdirSync(versionDir);
6842
- for (const file of files) {
6843
- if (archivePatterns.some((pattern) => file.startsWith(pattern)) && (file.endsWith(".zip") || file.endsWith(".tar.gz"))) {
6844
- result.hasPartialArchive = true;
6845
- break;
6846
- }
6847
- }
6848
- } catch {
6849
- }
6850
- result.executableExists = fs3.existsSync(executablePath);
6851
- if (!result.executableExists) {
6852
- if (result.hasPartialArchive) {
6853
- result.errorDetail = "Download incomplete: archive found but not extracted";
6854
- } else {
6855
- result.errorDetail = "Executable not found at expected path";
6856
- }
6857
- return result;
6858
- }
6859
- try {
6860
- const stats = fs3.statSync(executablePath);
6861
- result.executableIsFile = stats.isFile();
6862
- if (!result.executableIsFile) {
6863
- result.errorDetail = "Executable path exists but is not a file";
6864
- return result;
6865
- }
6866
- } catch {
6867
- result.errorDetail = "Cannot stat executable (broken symlink?)";
6868
- return result;
6869
- }
6870
- result.metadataExists = fs3.existsSync(metadataPath);
6871
- result.valid = true;
6872
- return result;
6873
- }
6874
- function validateCursorMcpConfig() {
6875
- const cursorMcpConfigPath = getCursorMcpConfigPath();
6876
- if (!existsSync(cursorMcpConfigPath)) {
6877
- return {
6878
- passed: false,
6879
- description: `Missing at ${cursorMcpConfigPath}`
6880
- };
6881
- }
6882
- try {
6883
- const rawCursorConfig = JSON.parse(
6884
- readFileSync(cursorMcpConfigPath, "utf-8")
6885
- );
6886
- if (!rawCursorConfig.mcpServers) {
6887
- return {
6888
- passed: false,
6889
- description: "Missing mcpServers key"
6890
- };
6891
- }
6892
- const muggleServerConfig = rawCursorConfig.mcpServers.muggle;
6893
- if (!muggleServerConfig) {
6894
- return {
6895
- passed: false,
6896
- description: "Missing mcpServers.muggle entry"
6897
- };
6898
- }
6899
- if (!Array.isArray(muggleServerConfig.args)) {
6900
- return {
6901
- passed: false,
6902
- description: "mcpServers.muggle.args is not an array"
6903
- };
6904
- }
6905
- const hasServeArgument = muggleServerConfig.args.includes("serve");
6906
- if (!hasServeArgument) {
6907
- return {
6908
- passed: false,
6909
- description: "mcpServers.muggle args does not include 'serve'"
6910
- };
6911
- }
6912
- if (muggleServerConfig.command === "node") {
6913
- const firstArgument = muggleServerConfig.args.at(0);
6914
- if (!firstArgument) {
6915
- return {
6916
- passed: false,
6917
- description: "mcpServers.muggle command is node but args[0] is missing"
6918
- };
6919
- }
6920
- if (!existsSync(firstArgument)) {
6921
- return {
6922
- passed: false,
6923
- description: `mcpServers.muggle args[0] does not exist: ${firstArgument}`
6924
- };
6925
- }
6926
- }
6927
- return {
6928
- passed: true,
6929
- description: `Configured at ${cursorMcpConfigPath}`
6930
- };
6931
- } catch (error) {
6932
- const errorMessage = error instanceof Error ? error.message : String(error);
6933
- return {
6934
- passed: false,
6935
- description: `Invalid JSON or schema: ${errorMessage}`
6936
- };
6937
- }
6938
- }
6939
- function runDiagnostics() {
6940
- const results = [];
6941
- const config = getConfig();
6942
- const dataDir = getDataDir2();
6943
- results.push({
6944
- name: "Data Directory",
6945
- passed: existsSync(dataDir),
6946
- description: existsSync(dataDir) ? `Found at ${dataDir}` : `Not found at ${dataDir}`,
6947
- suggestion: "Run 'muggle login' to create the data directory"
6948
- });
6949
- const electronVersion = getElectronAppVersion();
6950
- const bundledVersion = getBundledElectronAppVersion();
6951
- const versionSource = getElectronAppVersionSource();
6952
- const installVerification = verifyElectronAppInstallation();
6953
- let electronDescription;
6954
- let electronSuggestion;
6955
- if (installVerification.valid) {
6956
- electronDescription = `Installed (v${electronVersion})`;
6957
- switch (versionSource) {
6958
- case "env":
6959
- electronDescription += ` [from ELECTRON_APP_VERSION env]`;
6960
- break;
6961
- case "override":
6962
- electronDescription += ` [overridden from bundled v${bundledVersion}]`;
6963
- break;
6964
- }
6965
- if (!installVerification.metadataExists) {
6966
- electronDescription += " [missing metadata]";
6967
- }
6968
- } else {
6969
- electronDescription = `Not installed (expected v${electronVersion})`;
6970
- if (installVerification.errorDetail) {
6971
- electronDescription += `
6972
- \u2514\u2500 ${installVerification.errorDetail}`;
6973
- electronDescription += `
6974
- \u2514\u2500 Checked: ${installVerification.versionDir}`;
6975
- }
6976
- if (installVerification.hasPartialArchive) {
6977
- electronSuggestion = "Run 'muggle setup --force' to re-download and extract";
6978
- } else {
6979
- electronSuggestion = "Run 'muggle setup' to download the Electron app";
6980
- }
6981
- }
6982
- results.push({
6983
- name: "Electron App",
6984
- passed: installVerification.valid,
6985
- description: electronDescription,
6986
- suggestion: electronSuggestion
6987
- });
6988
- if (installVerification.valid) {
6989
- results.push({
6990
- name: "Electron App Updates",
6991
- passed: true,
6992
- description: "Run 'muggle upgrade --check' to check for updates"
6993
- });
6994
- }
6995
- const authService = getAuthService();
6996
- const authStatus = authService.getAuthStatus();
6997
- results.push({
6998
- name: "Authentication",
6999
- passed: authStatus.authenticated,
7000
- description: authStatus.authenticated ? `Authenticated as ${authStatus.email ?? "unknown"}` : "Not authenticated",
7001
- suggestion: "Run 'muggle login' to authenticate"
7002
- });
7003
- const hasStoredApiKey = hasApiKey();
7004
- results.push({
7005
- name: "API Key",
7006
- passed: hasStoredApiKey,
7007
- description: hasStoredApiKey ? "API key stored" : "No API key stored (optional)",
7008
- suggestion: "Run 'muggle login --key-name <name>' to generate an API key"
7009
- });
7010
- const credentialsPath = getCredentialsFilePath();
7011
- results.push({
7012
- name: "Credentials File",
7013
- passed: existsSync(credentialsPath),
7014
- description: existsSync(credentialsPath) ? `Found at ${credentialsPath}` : `Not found at ${credentialsPath}`,
7015
- suggestion: "Run 'muggle login' to create credentials"
7016
- });
7017
- results.push({
7018
- name: "Prompt Service URL",
7019
- passed: !!config.e2e.promptServiceBaseUrl,
7020
- description: config.e2e.promptServiceBaseUrl
7021
- });
7022
- results.push({
7023
- name: "Web Service URL",
7024
- passed: !!config.localQa.webServiceUrl,
7025
- description: config.localQa.webServiceUrl
7026
- });
7027
- const cursorMcpConfigValidationResult = validateCursorMcpConfig();
7028
- results.push({
7029
- name: "Cursor MCP Config",
7030
- passed: cursorMcpConfigValidationResult.passed,
7031
- description: cursorMcpConfigValidationResult.description,
7032
- suggestion: "Re-run npm install -g @muggleai/works to refresh ~/.cursor/mcp.json"
7033
- });
7034
- return results;
7035
- }
7036
- function formatCheckResult(result) {
7037
- const icon = result.passed ? "\u2713" : "\u2717";
7038
- const color = result.passed ? "\x1B[32m" : "\x1B[31m";
7039
- const reset = "\x1B[0m";
7040
- let output = `${color}${icon}${reset} ${result.name}: ${result.description}`;
7041
- if (!result.passed && result.suggestion) {
7042
- output += `
7043
- \u2514\u2500 ${result.suggestion}`;
7044
- }
7045
- return output;
7046
- }
7047
- async function doctorCommand() {
7048
- console.log("\nMuggle Works Doctor");
7049
- console.log("=================\n");
7050
- const results = runDiagnostics();
7051
- for (const result of results) {
7052
- console.log(formatCheckResult(result));
7053
- }
7054
- console.log("");
7055
- const failedCount = results.filter((r) => !r.passed).length;
7056
- if (failedCount === 0) {
7057
- console.log("All checks passed! Your installation is ready.");
7058
- } else {
7059
- console.log(`${failedCount} issue(s) found. See suggestions above.`);
7060
- }
7061
- logger8.info("Doctor command completed", {
7062
- totalChecks: results.length,
7063
- passed: results.length - failedCount,
7064
- failed: failedCount
7065
- });
7066
- }
7067
-
7068
- // src/cli/help.ts
7069
- var COLORS = {
7070
- reset: "\x1B[0m",
7071
- bold: "\x1B[1m",
7072
- dim: "\x1B[2m",
7073
- cyan: "\x1B[36m",
7074
- green: "\x1B[32m",
7075
- yellow: "\x1B[33m",
7076
- blue: "\x1B[34m"};
7077
- function colorize(text, color) {
7078
- if (process.env.NO_COLOR) {
7079
- return text;
7080
- }
7081
- return `${color}${text}${COLORS.reset}`;
7082
- }
7083
- function header(title) {
7084
- return colorize(`
7085
- ${title}`, COLORS.bold + COLORS.cyan);
7086
- }
7087
- function cmd(cmd2) {
7088
- return colorize(cmd2, COLORS.green);
7089
- }
7090
- function path12(path15) {
7091
- return colorize(path15, COLORS.yellow);
7092
- }
7093
- function getHelpGuidance() {
7094
- const lines = [
7095
- "",
7096
- colorize("=".repeat(70), COLORS.cyan),
7097
- colorize(" Muggle AI Works - Comprehensive How-To Guide", COLORS.bold + COLORS.green),
7098
- colorize("=".repeat(70), COLORS.cyan),
7099
- "",
7100
- header("What is Muggle AI Works?"),
7101
- "",
7102
- " Muggle AI Works is a Model Context Protocol server that provides AI",
7103
- " assistants with tools to perform automated end-to-end (E2E) acceptance testing of web applications.",
7104
- "",
7105
- " It supports both:",
7106
- ` ${colorize("\u2022", COLORS.green)} Cloud E2E - Test remote production/staging sites with a public URL`,
7107
- ` ${colorize("\u2022", COLORS.green)} Local E2E - Test localhost development servers`,
7108
- "",
7109
- header("Setup Instructions"),
7110
- "",
7111
- ` ${colorize("Step 1:", COLORS.bold)} Configure your MCP client`,
7112
- "",
7113
- ` For ${colorize("Cursor", COLORS.bold)}, edit ${path12("~/.cursor/mcp.json")}:`,
7114
- "",
7115
- ` ${colorize("{", COLORS.dim)}`,
7116
- ` ${colorize('"mcpServers"', COLORS.yellow)}: {`,
7117
- ` ${colorize('"muggle"', COLORS.yellow)}: {`,
7118
- ` ${colorize('"command"', COLORS.yellow)}: ${colorize('"muggle"', COLORS.green)},`,
7119
- ` ${colorize('"args"', COLORS.yellow)}: [${colorize('"serve"', COLORS.green)}]`,
7120
- ` }`,
7121
- ` }`,
7122
- ` ${colorize("}", COLORS.dim)}`,
7123
- "",
7124
- ` ${colorize("Step 2:", COLORS.bold)} Restart your MCP client`,
7125
- "",
7126
- ` ${colorize("Step 3:", COLORS.bold)} Start testing! Ask your AI assistant:`,
7127
- ` ${colorize('"Test the login flow on my app at http://localhost:3000"', COLORS.dim)}`,
7128
- "",
7129
- header("CLI Commands"),
7130
- "",
7131
- ` ${colorize("Server Commands:", COLORS.bold)}`,
7132
- ` ${cmd("muggle serve")} Start MCP server with all tools`,
7133
- ` ${cmd("muggle serve --e2e")} Start with cloud E2E tools only`,
7134
- ` ${cmd("muggle serve --local")} Start with local E2E tools only`,
7135
- "",
7136
- ` ${colorize("Setup & Diagnostics:", COLORS.bold)}`,
7137
- ` ${cmd("muggle setup")} Download/update Electron app`,
7138
- ` ${cmd("muggle setup --force")} Force re-download`,
7139
- ` ${cmd("muggle doctor")} Diagnose installation issues`,
7140
- ` ${cmd("muggle upgrade")} Check for updates`,
7141
- ` ${cmd("muggle upgrade --check")} Check updates without installing`,
7142
- "",
7143
- ` ${colorize("Authentication:", COLORS.bold)}`,
7144
- ` ${cmd("muggle login")} Login to Muggle AI`,
7145
- ` ${cmd("muggle logout")} Clear stored credentials`,
7146
- ` ${cmd("muggle status")} Show authentication status`,
7147
- "",
7148
- ` ${colorize("Maintenance:", COLORS.bold)}`,
7149
- ` ${cmd("muggle versions")} List installed Electron app versions`,
7150
- ` ${cmd("muggle cleanup")} Remove old Electron app versions`,
7151
- ` ${cmd("muggle cleanup --all")} Remove all old versions`,
7152
- ` ${cmd("muggle cleanup --skills")} Also remove obsolete skills`,
7153
- "",
7154
- ` ${colorize("Help:", COLORS.bold)}`,
7155
- ` ${cmd("muggle help")} Show this guide`,
7156
- ` ${cmd("muggle --help")} Show command synopsis`,
7157
- ` ${cmd("muggle --version")} Show version`,
7158
- "",
7159
- header("Authentication Flow"),
7160
- "",
7161
- " Authentication happens automatically when you first use a tool that",
7162
- " requires it:",
7163
- "",
7164
- ` 1. ${colorize("A browser window opens", COLORS.bold)} with a verification code`,
7165
- ` 2. ${colorize("Log in", COLORS.bold)} with your Muggle AI account`,
7166
- ` 3. ${colorize("The tool call continues", COLORS.bold)} with your credentials`,
7167
- "",
7168
- ` API keys are stored in ${path12("~/.muggle-ai/api-key.json")}`,
7169
- "",
7170
- header("Available MCP Tools"),
7171
- "",
7172
- ` ${colorize("Cloud E2E tools:", COLORS.bold)} (prefix: muggle-remote-)`,
7173
- " muggle-remote-project-create, muggle-remote-project-list, muggle-remote-use-case-create-from-prompts,",
7174
- " muggle-remote-test-case-generate-from-prompt, muggle-remote-workflow-start-*, etc.",
7175
- "",
7176
- ` ${colorize("Local E2E tools:", COLORS.bold)} (prefix: muggle-local-)`,
7177
- " muggle-local-execute-test-generation, muggle-local-execute-replay,",
7178
- " muggle-local-publish-test-script, muggle-local-run-result-get,",
7179
- " muggle-local-check-status, etc.",
7180
- "",
7181
- header("Data Directory"),
7182
- "",
7183
- ` All data is stored in ${path12("~/.muggle-ai/")}:`,
7184
- "",
7185
- ` ${path12("api-key.json")} Long-lived API key (auto-generated)`,
7186
- ` ${path12("projects/")} Local test projects`,
7187
- ` ${path12("sessions/")} Test execution sessions`,
7188
- ` ${path12("electron-app/")} Downloaded Electron app binaries`,
7189
- "",
7190
- header("Troubleshooting"),
7191
- "",
7192
- ` ${colorize("Installation issues:", COLORS.bold)}`,
7193
- ` Run ${cmd("muggle doctor")} to diagnose problems`,
7194
- "",
7195
- ` ${colorize("Electron app not found:", COLORS.bold)}`,
7196
- ` Run ${cmd("muggle setup --force")} to re-download`,
7197
- "",
7198
- ` ${colorize("Authentication issues:", COLORS.bold)}`,
7199
- ` Run ${cmd("muggle logout")} then ${cmd("muggle login")}`,
7200
- "",
7201
- ` ${colorize("MCP not working in client:", COLORS.bold)}`,
7202
- " 1. Verify mcp.json configuration",
7203
- " 2. Restart your MCP client",
7204
- ` 3. Check ${cmd("muggle doctor")} output`,
7205
- "",
7206
- header("Documentation & Support"),
7207
- "",
7208
- ` Docs: ${colorize("https://www.muggle-ai.com/muggleTestV0/docs/mcp/mcp-overview", COLORS.blue)}`,
7209
- ` GitHub: ${colorize("https://github.com/multiplex-ai/muggle-ai-works", COLORS.blue)}`,
7210
- "",
7211
- colorize("=".repeat(70), COLORS.cyan),
7212
- ""
7213
- ];
7214
- return lines.join("\n");
7215
- }
7216
- function helpCommand() {
7217
- console.log(getHelpGuidance());
7218
- }
7219
-
7220
- // src/cli/login.ts
7221
- var logger9 = getLogger();
7222
- async function loginCommand(options) {
7223
- console.log("\nMuggle AI Login");
7224
- console.log("===============\n");
7225
- const expiry = options.keyExpiry || "90d";
7226
- console.log("Starting device code authentication...");
7227
- console.log("A browser window will open for you to complete login.\n");
7228
- const result = await performLogin(options.keyName, expiry);
7229
- if (result.success) {
7230
- console.log("\u2713 Login successful!");
7231
- if (result.credentials?.email) {
7232
- console.log(` Logged in as: ${result.credentials.email}`);
7233
- }
7234
- if (result.credentials?.apiKey) {
7235
- console.log(" API key created and stored for future use.");
7236
- }
7237
- console.log("\nYou can now use Muggle AI Works tools.");
7238
- } else {
7239
- console.error("\u2717 Login failed");
7240
- if (result.error) {
7241
- console.error(` Error: ${result.error}`);
7242
- }
7243
- if (result.deviceCodeResponse) {
7244
- console.log("\nIf browser didn't open, visit:");
7245
- console.log(` ${result.deviceCodeResponse.verificationUriComplete}`);
7246
- console.log(` Code: ${result.deviceCodeResponse.userCode}`);
7247
- }
7248
- process.exit(1);
7249
- }
7250
- }
7251
- async function logoutCommand() {
7252
- console.log("\nLogging out...");
7253
- performLogout();
7254
- console.log("\u2713 Credentials cleared successfully.");
7255
- logger9.info("Logout completed");
7256
- }
7257
- async function statusCommand() {
7258
- console.log("\nAuthentication Status");
7259
- console.log("=====================\n");
7260
- const authService = getAuthService();
7261
- const status = authService.getAuthStatus();
7262
- const hasStoredApiKey = hasApiKey();
7263
- if (status.authenticated) {
7264
- console.log("\u2713 Authenticated");
7265
- if (status.email) {
7266
- console.log(` Email: ${status.email}`);
7267
- }
7268
- if (status.userId) {
7269
- console.log(` User ID: ${status.userId}`);
7270
- }
7271
- if (status.expiresAt) {
7272
- const expiresDate = new Date(status.expiresAt);
7273
- console.log(` Token expires: ${expiresDate.toLocaleString()}`);
7274
- if (status.isExpired) {
7275
- console.log(" (Token expired - will refresh automatically on next API call)");
7276
- }
7277
- }
7278
- console.log(` API Key: ${hasStoredApiKey ? "Yes" : "No"}`);
7279
- } else {
7280
- console.log("\u2717 Not authenticated");
7281
- console.log("\nRun 'muggle login' to authenticate.");
7282
- }
7283
- }
7284
-
7285
- // src/cli/serve.ts
7286
- var logger10 = getLogger();
7287
- async function serveCommand(options) {
7288
- const config = getConfig();
7289
- const enableQa = options.local ? false : true;
7290
- const enableLocal = options.e2e ? false : true;
7291
- logger10.info("Starting Muggle MCP Server", {
7292
- version: config.serverVersion,
7293
- enableQa,
7294
- enableLocal,
7295
- transport: "stdio"
7296
- });
7297
- try {
7298
- if (enableQa) {
7299
- const qaTools = getQaTools();
7300
- registerTools(qaTools);
7301
- logger10.info("Registered cloud E2E acceptance tools", { count: qaTools.length });
7302
- }
7303
- if (enableLocal) {
7304
- const localTools = getLocalQaTools();
7305
- registerTools(localTools);
7306
- logger10.info("Registered local E2E acceptance tools", { count: localTools.length });
7307
- }
7308
- const mcpServer = createUnifiedMcpServer({
7309
- enableQaTools: enableQa,
7310
- enableLocalTools: enableLocal
7311
- });
7312
- await startStdioServer(mcpServer);
7313
- logger10.info("MCP server started successfully");
7314
- } catch (error) {
7315
- logger10.error("Failed to start MCP server", {
7316
- error: error instanceof Error ? error.message : String(error),
7317
- stack: error instanceof Error ? error.stack : void 0
7318
- });
7319
- process.exit(1);
7320
- }
7321
- }
7322
- var logger11 = getLogger();
7323
- var MAX_RETRY_ATTEMPTS = 3;
7324
- var RETRY_BASE_DELAY_MS = 2e3;
7325
- var INSTALL_METADATA_FILE_NAME = ".install-metadata.json";
7326
- function getBinaryName() {
7327
- const os4 = platform();
7328
- const architecture = arch();
7329
- switch (os4) {
7330
- case "darwin": {
7331
- const darwinArch = architecture === "arm64" ? "arm64" : "x64";
7332
- return `MuggleAI-darwin-${darwinArch}.zip`;
7333
- }
7334
- case "win32":
7335
- return "MuggleAI-win32-x64.zip";
7336
- case "linux":
7337
- return "MuggleAI-linux-x64.zip";
7338
- default:
7339
- throw new Error(`Unsupported platform: ${os4}`);
7340
- }
7341
- }
7342
- function getExpectedExecutablePath2(versionDir) {
7343
- const os4 = platform();
7344
- switch (os4) {
7345
- case "darwin":
7346
- return path2.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
7347
- case "win32":
7348
- return path2.join(versionDir, "MuggleAI.exe");
7349
- case "linux":
7350
- return path2.join(versionDir, "MuggleAI");
7351
- default:
7352
- throw new Error(`Unsupported platform: ${os4}`);
7353
- }
7354
- }
7355
- async function extractZip(zipPath, destDir) {
7356
- return new Promise((resolve4, reject) => {
7357
- if (platform() === "win32") {
7358
- execFile(
7359
- "powershell",
7360
- ["-command", `Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force`],
7361
- (error) => {
7362
- if (error) {
7363
- reject(error);
7364
- } else {
7365
- resolve4();
7366
- }
7367
- }
7368
- );
7369
- } else {
7370
- execFile("unzip", ["-o", zipPath, "-d", destDir], (error) => {
7371
- if (error) {
7372
- reject(error);
7373
- } else {
7374
- resolve4();
7375
- }
7376
- });
7377
- }
7378
- });
7379
- }
7380
- async function extractTarGz(tarPath, destDir) {
7381
- return new Promise((resolve4, reject) => {
7382
- execFile("tar", ["-xzf", tarPath, "-C", destDir], (error) => {
7383
- if (error) {
7384
- reject(error);
7385
- } else {
7386
- resolve4();
7387
- }
7388
- });
7389
- });
7390
- }
7391
- function sleep2(ms) {
7392
- return new Promise((resolve4) => setTimeout(resolve4, ms));
7393
- }
7394
- async function downloadWithRetry(downloadUrl, destPath) {
7395
- let lastError = null;
7396
- for (let attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
7397
- try {
7398
- if (attempt > 1) {
7399
- const delayMs = RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 2);
7400
- console.log(`Retry attempt ${attempt}/${MAX_RETRY_ATTEMPTS} after ${delayMs}ms delay...`);
7401
- await sleep2(delayMs);
7402
- }
7403
- const response = await fetch(downloadUrl);
7404
- if (!response.ok) {
7405
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
7406
- }
7407
- if (!response.body) {
7408
- throw new Error("No response body received");
7409
- }
7410
- const fileStream = createWriteStream(destPath);
7411
- await pipeline(response.body, fileStream);
7412
- return true;
7413
- } catch (error) {
7414
- lastError = error instanceof Error ? error : new Error(String(error));
7415
- console.error(`Download attempt ${attempt} failed: ${lastError.message}`);
7416
- if (existsSync(destPath)) {
7417
- rmSync(destPath, { force: true });
7418
- }
7419
- }
7420
- }
7421
- if (lastError) {
7422
- throw new Error(`Download failed after ${MAX_RETRY_ATTEMPTS} attempts: ${lastError.message}`);
7423
- }
7424
- return false;
7425
- }
7426
- function writeInstallMetadata(params) {
7427
- const metadata = {
7428
- version: params.version,
7429
- binaryName: params.binaryName,
7430
- platformKey: params.platformKey,
7431
- executableChecksum: params.executableChecksum,
7432
- expectedArchiveChecksum: params.expectedArchiveChecksum,
7433
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7434
- };
7435
- writeFileSync(params.metadataPath, `${JSON.stringify(metadata, null, 2)}
7436
- `, "utf-8");
7437
- }
7438
- function cleanupFailedInstall(versionDir) {
7439
- if (existsSync(versionDir)) {
7440
- try {
7441
- rmSync(versionDir, { recursive: true, force: true });
7442
- } catch {
7443
- }
7444
- }
7445
- }
7446
- async function setupCommand(options) {
7447
- const version = getElectronAppVersion();
7448
- const versionDir = getElectronAppDir(version);
7449
- const platformKey = getPlatformKey();
7450
- if (!options.force && isElectronAppInstalled()) {
7451
- console.log(`Electron app v${version} is already installed at ${versionDir}`);
7452
- console.log("Use --force to re-download.");
7453
- return;
7454
- }
7455
- const binaryName = getBinaryName();
7456
- const downloadUrl = buildElectronAppReleaseAssetUrl({
7457
- version,
7458
- assetFileName: binaryName
7459
- });
7460
- console.log(`Downloading Muggle Test Electron app v${version}...`);
7461
- console.log(`URL: ${downloadUrl}`);
7462
- try {
7463
- if (existsSync(versionDir)) {
7464
- rmSync(versionDir, { recursive: true, force: true });
7465
- }
7466
- mkdirSync(versionDir, { recursive: true });
7467
- const tempFile = path2.join(versionDir, binaryName);
7468
- await downloadWithRetry(downloadUrl, tempFile);
7469
- console.log("Download complete, verifying checksum...");
7470
- const checksums = getElectronAppChecksums();
7471
- const expectedChecksum = getChecksumForPlatform(checksums);
7472
- const checksumResult = await verifyFileChecksum(tempFile, expectedChecksum);
7473
- if (!checksumResult.valid && expectedChecksum) {
7474
- cleanupFailedInstall(versionDir);
7475
- throw new Error(
7476
- `Checksum verification failed!
7477
- Expected: ${checksumResult.expected}
7478
- Actual: ${checksumResult.actual}
7479
- The downloaded file may be corrupted or tampered with.`
7480
- );
7481
- }
7482
- if (expectedChecksum) {
7483
- console.log("Checksum verified successfully.");
7484
- } else {
7485
- console.log("Warning: No checksum configured, skipping verification.");
7486
- }
7487
- console.log("Extracting...");
7488
- if (binaryName.endsWith(".zip")) {
7489
- await extractZip(tempFile, versionDir);
7490
- } else if (binaryName.endsWith(".tar.gz")) {
7491
- await extractTarGz(tempFile, versionDir);
7492
- }
7493
- const executablePath = getExpectedExecutablePath2(versionDir);
7494
- if (!existsSync(executablePath)) {
7495
- cleanupFailedInstall(versionDir);
7496
- throw new Error(
7497
- `Extraction failed: executable not found at expected path.
7498
- Expected: ${executablePath}
7499
- The archive may be corrupted or in an unexpected format.`
7500
- );
7501
- }
7502
- const executableChecksum = await calculateFileChecksum(executablePath);
7503
- const metadataPath = path2.join(versionDir, INSTALL_METADATA_FILE_NAME);
7504
- writeInstallMetadata({
7505
- metadataPath,
7506
- version,
7507
- binaryName,
7508
- platformKey,
7509
- executableChecksum,
7510
- expectedArchiveChecksum: expectedChecksum
7511
- });
7512
- rmSync(tempFile, { force: true });
7513
- console.log(`Electron app installed to ${versionDir}`);
7514
- logger11.info("Setup complete", { version, path: versionDir });
7515
- } catch (error) {
7516
- const errorMessage = error instanceof Error ? error.message : String(error);
7517
- console.error(`Failed to download Electron app: ${errorMessage}`);
7518
- logger11.error("Setup failed", { error: errorMessage });
7519
- process.exit(1);
7520
- }
7521
- }
7522
- var logger12 = getLogger();
7523
- var GITHUB_RELEASES_API = "https://api.github.com/repos/multiplex-ai/muggle-ai-works/releases";
7524
- var INSTALL_METADATA_FILE_NAME2 = ".install-metadata.json";
7525
- var VERSION_OVERRIDE_FILE2 = "electron-app-version-override.json";
7526
- function getBinaryName2() {
7527
- const os4 = platform();
7528
- const arch3 = process.arch;
7529
- switch (os4) {
7530
- case "darwin":
7531
- return arch3 === "arm64" ? "MuggleAI-darwin-arm64.zip" : "MuggleAI-darwin-x64.zip";
7532
- case "win32":
7533
- return "MuggleAI-win32-x64.zip";
7534
- case "linux":
7535
- return "MuggleAI-linux-x64.zip";
7536
- default:
7537
- throw new Error(`Unsupported platform: ${os4}`);
7538
- }
7539
- }
7540
- function extractVersionFromTag(tag) {
7541
- const match = tag.match(/^(?:electron-app-)?v(\d+\.\d+\.\d+)$/);
7542
- return match ? match[1] : null;
7543
- }
7544
- function getVersionOverridePath() {
7545
- return path2.join(getDataDir2(), VERSION_OVERRIDE_FILE2);
7546
- }
7547
- function getEffectiveElectronAppVersion() {
7548
- const overridePath = getVersionOverridePath();
7549
- if (existsSync(overridePath)) {
7550
- try {
7551
- const content = JSON.parse(__require("fs").readFileSync(overridePath, "utf-8"));
7552
- if (content.version) {
7553
- return content.version;
7554
- }
7555
- } catch {
7556
- }
7557
- }
7558
- return getElectronAppVersion();
7559
- }
7560
- function saveVersionOverride(version) {
7561
- const overridePath = getVersionOverridePath();
7562
- const dataDir = getDataDir2();
7563
- if (!existsSync(dataDir)) {
7564
- mkdirSync(dataDir, { recursive: true });
7565
- }
7566
- writeFileSync(overridePath, JSON.stringify({
7567
- version,
7568
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7569
- }, null, 2), "utf-8");
7570
- }
7571
- async function checkForUpdates() {
7572
- const currentVersion = getEffectiveElectronAppVersion();
7573
- try {
7574
- const response = await fetch(GITHUB_RELEASES_API, {
7575
- headers: {
7576
- "Accept": "application/vnd.github.v3+json",
7577
- "User-Agent": "muggle"
7578
- }
7579
- });
7580
- if (!response.ok) {
7581
- throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
7582
- }
7583
- const releases = await response.json();
7584
- for (const release2 of releases) {
7585
- if (release2.prerelease || release2.draft) {
7586
- continue;
7587
- }
7588
- const version = extractVersionFromTag(release2.tag_name);
7589
- if (version) {
7590
- const updateAvailable = compareVersions2(version, currentVersion) > 0;
7591
- const binaryName = getBinaryName2();
7592
- return {
7593
- currentVersion,
7594
- latestVersion: version,
7595
- updateAvailable,
7596
- downloadUrl: buildElectronAppReleaseAssetUrl({
7597
- version,
7598
- assetFileName: binaryName
7599
- })
7600
- };
7601
- }
7602
- }
7603
- return {
7604
- currentVersion,
7605
- latestVersion: currentVersion,
7606
- updateAvailable: false
7607
- };
7608
- } catch (error) {
7609
- const errorMessage = error instanceof Error ? error.message : String(error);
7610
- logger12.warn("Failed to check for updates", { error: errorMessage });
7611
- throw new Error(`Failed to check for updates: ${errorMessage}`, { cause: error });
7612
- }
7613
- }
7614
- function compareVersions2(a, b) {
7615
- const partsA = a.split(".").map(Number);
7616
- const partsB = b.split(".").map(Number);
7617
- for (let i = 0; i < 3; i++) {
7618
- const partA = partsA[i] || 0;
7619
- const partB = partsB[i] || 0;
7620
- if (partA > partB) {
7621
- return 1;
7622
- }
7623
- if (partA < partB) {
7624
- return -1;
7625
- }
7626
- }
7627
- return 0;
7628
- }
7629
- function getExpectedExecutablePath3(versionDir) {
7630
- const os4 = platform();
7631
- switch (os4) {
7632
- case "darwin":
7633
- return path2.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
7634
- case "win32":
7635
- return path2.join(versionDir, "MuggleAI.exe");
7636
- case "linux":
7637
- return path2.join(versionDir, "MuggleAI");
7638
- default:
7639
- throw new Error(`Unsupported platform: ${os4}`);
7640
- }
7641
- }
7642
- function writeInstallMetadata2(params) {
7643
- const metadata = {
7644
- version: params.version,
7645
- binaryName: params.binaryName,
7646
- platformKey: params.platformKey,
7647
- executableChecksum: params.executableChecksum,
7648
- expectedArchiveChecksum: params.expectedArchiveChecksum,
7649
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7650
- };
7651
- writeFileSync(params.metadataPath, `${JSON.stringify(metadata, null, 2)}
7652
- `, "utf-8");
7653
- }
7654
- async function extractZip2(zipPath, destDir) {
7655
- return new Promise((resolve4, reject) => {
7656
- if (platform() === "win32") {
7657
- execFile(
7658
- "powershell",
7659
- ["-command", `Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force`],
7660
- (error) => {
7661
- if (error) {
7662
- reject(error);
7663
- } else {
7664
- resolve4();
7665
- }
7666
- }
7667
- );
7668
- } else {
7669
- execFile("unzip", ["-o", zipPath, "-d", destDir], (error) => {
7670
- if (error) {
7671
- reject(error);
7672
- } else {
7673
- resolve4();
7674
- }
7675
- });
7676
- }
7677
- });
7678
- }
7679
- async function extractTarGz2(tarPath, destDir) {
7680
- return new Promise((resolve4, reject) => {
7681
- execFile("tar", ["-xzf", tarPath, "-C", destDir], (error) => {
7682
- if (error) {
7683
- reject(error);
7684
- } else {
7685
- resolve4();
7686
- }
7687
- });
7688
- });
7689
- }
7690
- async function fetchChecksumFromRelease(version) {
7691
- const checksumUrl = buildElectronAppChecksumsUrl(version);
7692
- try {
7693
- const response = await fetch(checksumUrl);
7694
- if (!response.ok) {
7695
- logger12.warn("Checksums file not found in release", { version });
7696
- return "";
7697
- }
7698
- const text = await response.text();
7699
- const binaryName = getBinaryName2();
7700
- const platformKey = getPlatformKey();
7701
- const lines = text.split("\n");
7702
- for (const line of lines) {
7703
- const trimmed = line.trim();
7704
- if (!trimmed) {
7705
- continue;
7706
- }
7707
- const match = trimmed.match(/^([a-fA-F0-9]{64})\s+(.+)$/);
7708
- if (match) {
7709
- const checksum = match[1];
7710
- const filename = match[2];
7711
- if (filename === binaryName || filename.includes(platformKey)) {
7712
- logger12.info("Found checksum in release", {
7713
- version,
7714
- platform: platformKey,
7715
- checksum: checksum.substring(0, 16) + "..."
7716
- });
7717
- return checksum;
7718
- }
7719
- }
7720
- }
7721
- logger12.warn("Platform checksum not found in checksums.txt", {
7722
- version,
7723
- platform: platformKey
7724
- });
7725
- return "";
7726
- } catch (error) {
7727
- const errorMessage = error instanceof Error ? error.message : String(error);
7728
- logger12.warn("Failed to fetch checksums from release", {
7729
- version,
7730
- error: errorMessage
7731
- });
7732
- return "";
7733
- }
7734
- }
7735
- async function downloadAndInstall(version, downloadUrl, checksum) {
7736
- const versionDir = getElectronAppDir(version);
7737
- const binaryName = getBinaryName2();
7738
- const platformKey = getPlatformKey();
7739
- console.log(`Downloading Muggle Test Electron app v${version}...`);
7740
- console.log(`URL: ${downloadUrl}`);
7741
- if (existsSync(versionDir)) {
7742
- rmSync(versionDir, { recursive: true, force: true });
7743
- }
7744
- mkdirSync(versionDir, { recursive: true });
7745
- const response = await fetch(downloadUrl);
7746
- if (!response.ok) {
7747
- throw new Error(`Download failed: ${response.status} ${response.statusText}`);
7748
- }
7749
- const tempFile = path2.join(versionDir, binaryName);
7750
- const fileStream = createWriteStream(tempFile);
7751
- if (!response.body) {
7752
- throw new Error("No response body");
7753
- }
7754
- await pipeline(response.body, fileStream);
7755
- console.log("Download complete, verifying checksum...");
7756
- let expectedChecksum = checksum;
7757
- if (!expectedChecksum) {
7758
- expectedChecksum = await fetchChecksumFromRelease(version);
7759
- }
7760
- const checksumResult = await verifyFileChecksum(tempFile, expectedChecksum || "");
7761
- if (!checksumResult.valid && expectedChecksum) {
7762
- rmSync(versionDir, { recursive: true, force: true });
7763
- throw new Error(
7764
- `Checksum verification failed!
7765
- Expected: ${checksumResult.expected}
7766
- Actual: ${checksumResult.actual}
7767
- The downloaded file may be corrupted or tampered with.`
7768
- );
7769
- }
7770
- if (expectedChecksum) {
7771
- console.log("Checksum verified successfully.");
7772
- } else {
7773
- console.log("Warning: No checksum available, skipping verification.");
7774
- }
7775
- console.log("Extracting...");
7776
- if (binaryName.endsWith(".zip")) {
7777
- await extractZip2(tempFile, versionDir);
7778
- } else if (binaryName.endsWith(".tar.gz")) {
7779
- await extractTarGz2(tempFile, versionDir);
7780
- }
7781
- const executablePath = getExpectedExecutablePath3(versionDir);
7782
- if (!existsSync(executablePath)) {
7783
- rmSync(versionDir, { recursive: true, force: true });
7784
- throw new Error(
7785
- `Extraction failed: executable not found at expected path.
7786
- Expected: ${executablePath}
7787
- The archive may be corrupted or in an unexpected format.`
7788
- );
7789
- }
7790
- const executableChecksum = await calculateFileChecksum(executablePath);
7791
- const metadataPath = path2.join(versionDir, INSTALL_METADATA_FILE_NAME2);
7792
- writeInstallMetadata2({
7793
- metadataPath,
7794
- version,
7795
- binaryName,
7796
- platformKey,
7797
- executableChecksum,
7798
- expectedArchiveChecksum: expectedChecksum || ""
7799
- });
7800
- rmSync(tempFile, { force: true });
7801
- saveVersionOverride(version);
7802
- console.log(`Electron app v${version} installed to ${versionDir}`);
7803
- logger12.info("Upgrade complete", { version, path: versionDir });
7804
- }
7805
- async function upgradeCommand(options) {
7806
- try {
7807
- if (options.version) {
7808
- const binaryName = getBinaryName2();
7809
- const downloadUrl = buildElectronAppReleaseAssetUrl({
7810
- version: options.version,
7811
- assetFileName: binaryName
7812
- });
7813
- await downloadAndInstall(options.version, downloadUrl);
7814
- const cleanupResult2 = cleanupOldVersions({ all: false });
7815
- if (cleanupResult2.removed.length > 0) {
7816
- console.log(
7817
- `
7818
- Cleaned up ${cleanupResult2.removed.length} old version(s), freed ${formatBytes(cleanupResult2.freedBytes)}`
7819
- );
7820
- }
7821
- return;
7822
- }
7823
- console.log("Checking for updates...");
7824
- const result = await checkForUpdates();
7825
- console.log(`Current version: ${result.currentVersion}`);
7826
- console.log(`Latest version: ${result.latestVersion}`);
7827
- if (options.check) {
7828
- if (result.updateAvailable) {
7829
- console.log("\nUpdate available! Run 'muggle upgrade' to install.");
7830
- } else {
7831
- console.log("\nYou are on the latest version.");
7832
- }
7833
- return;
7834
- }
7835
- if (!result.updateAvailable && !options.force) {
7836
- console.log("\nYou are already on the latest version.");
7837
- console.log("Use --force to re-download the current version.");
7838
- return;
7839
- }
7840
- if (!result.downloadUrl) {
7841
- throw new Error("No download URL available");
7842
- }
7843
- await downloadAndInstall(result.latestVersion, result.downloadUrl);
7844
- const cleanupResult = cleanupOldVersions({ all: false });
7845
- if (cleanupResult.removed.length > 0) {
7846
- console.log(
7847
- `
7848
- Cleaned up ${cleanupResult.removed.length} old version(s), freed ${formatBytes(cleanupResult.freedBytes)}`
7849
- );
7850
- }
7851
- } catch (error) {
7852
- const errorMessage = error instanceof Error ? error.message : String(error);
7853
- console.error(`Upgrade failed: ${errorMessage}`);
7854
- logger12.error("Upgrade failed", { error: errorMessage });
7855
- process.exit(1);
7856
- }
7857
- }
7858
-
7859
- // packages/commands/src/cli/run-cli.ts
7860
- var __dirname$1 = dirname(fileURLToPath(import.meta.url));
7861
- function getPackageRoot2() {
7862
- if (__dirname$1.endsWith("dist")) {
7863
- return resolve(__dirname$1, "..");
7864
- }
7865
- return resolve(__dirname$1, "..", "..", "..", "..");
7866
- }
7867
- var packageVersion = JSON.parse(
7868
- readFileSync(resolve(getPackageRoot2(), "package.json"), "utf-8")
7869
- ).version;
7870
- var logger13 = getLogger();
7871
- function createProgram() {
7872
- const program = new Command();
7873
- program.name("muggle").description("Unified MCP server for Muggle AI \u2014 cloud E2E and local E2E testing").version(packageVersion);
7874
- program.command("serve").description("Start the MCP server").option("--e2e", "Only enable cloud E2E tools (remote URLs; muggle-remote-* prefix)").option("--local", "Only enable local E2E tools (localhost; muggle-local-* prefix)").option("--stdio", "Use stdio transport (default)").action(serveCommand);
7875
- program.command("setup").description("Download/update the Electron app for local testing").option("--force", "Force re-download even if already installed").action(setupCommand);
7876
- program.command("upgrade").description("Check for and install the latest electron-app version").option("--force", "Force re-download even if already on latest").option("--check", "Check for updates only, don't download").option("--version <version>", "Download a specific version (e.g., 1.0.2)").action(upgradeCommand);
7877
- program.command("versions").description("List installed electron-app versions").action(versionsCommand);
7878
- program.command("cleanup").description("Remove old electron-app versions and obsolete skills").option("--all", "Remove all old versions (default: keep one previous)").option("--dry-run", "Show what would be deleted without deleting").option("--skills", "Also clean up obsolete skills from ~/.cursor/skills").action(cleanupCommand);
7879
- program.command("doctor").description("Diagnose installation and configuration issues").action(doctorCommand);
7880
- program.command("login").description("Authenticate with Muggle AI (uses device code flow)").option("--key-name <name>", "Name for the API key").option("--key-expiry <expiry>", "API key expiry: 30d, 90d, 1y, never", "90d").action(loginCommand);
7881
- program.command("logout").description("Clear stored credentials").action(logoutCommand);
7882
- program.command("status").description("Show authentication status").action(statusCommand);
7883
- program.command("build-pr-section").description("Render a muggle-do PR body evidence block from an e2e report on stdin").option("--max-body-bytes <n>", "Max UTF-8 byte budget for the PR body (default 60000)").action(buildPrSectionCommand);
7884
- program.action(() => {
7885
- helpCommand();
7886
- });
7887
- program.on("command:*", () => {
7888
- helpCommand();
7889
- process.exit(1);
7890
- });
7891
- return program;
7892
- }
7893
- function handleHelpCommand() {
7894
- const args = process.argv.slice(2);
7895
- if (args.length === 1 && args[0] === "help") {
7896
- helpCommand();
7897
- return true;
7898
- }
7899
- return false;
7900
- }
7901
- async function runCli() {
7902
- try {
7903
- if (handleHelpCommand()) {
7904
- return;
7905
- }
7906
- const program = createProgram();
7907
- await program.parseAsync(process.argv);
7908
- } catch (error) {
7909
- logger13.error("CLI error", {
7910
- error: error instanceof Error ? error.message : String(error)
7911
- });
7912
- process.exit(1);
7913
- }
7914
- }
7915
-
7916
- // packages/commands/src/index.ts
7917
- var src_exports2 = {};
7918
- __export(src_exports2, {
7919
- cleanupCommand: () => cleanupCommand,
7920
- doctorCommand: () => doctorCommand,
7921
- helpCommand: () => helpCommand,
7922
- loginCommand: () => loginCommand,
7923
- logoutCommand: () => logoutCommand,
7924
- registerCoreCommands: () => registerCoreCommands,
7925
- runCli: () => runCli,
7926
- serveCommand: () => serveCommand,
7927
- setupCommand: () => setupCommand,
7928
- statusCommand: () => statusCommand,
7929
- upgradeCommand: () => upgradeCommand,
7930
- versionsCommand: () => versionsCommand
7931
- });
7932
-
7933
- // packages/commands/src/registry/register-core-commands.ts
7934
- function registerCoreCommands(commandRegistrationContext) {
7935
- }
7936
-
7937
- export { createChildLogger, createUnifiedMcpServer, e2e_exports2 as e2e_exports, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports2 as local_exports, mcp_exports, runCli, server_exports, src_exports, src_exports2 };
5911
+ export { __export, __require, buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports2 as e2e_exports, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir2 as getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, loadApiKeyData, loadCredentials, local_exports2 as local_exports, mcp_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, resetConfig, resetLogger, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, toolRequiresAuth, verifyFileChecksum };