@muggleai/mcp 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/bin/muggle-mcp.js +0 -0
  2. package/dist/{chunk-UKMTQHS2.js → chunk-DGEO3CP2.js} +3345 -2489
  3. package/dist/chunk-DGEO3CP2.js.map +1 -0
  4. package/dist/cli/doctor.d.ts.map +1 -1
  5. package/dist/cli/login.d.ts.map +1 -1
  6. package/dist/cli.js +15 -8
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.js +6 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/local-qa/contracts/auth-schemas.d.ts +2 -2
  11. package/dist/local-qa/contracts/project-schemas.d.ts +66 -26
  12. package/dist/local-qa/contracts/project-schemas.d.ts.map +1 -1
  13. package/dist/local-qa/services/auth-service.d.ts +17 -0
  14. package/dist/local-qa/services/auth-service.d.ts.map +1 -1
  15. package/dist/local-qa/services/execution-service.d.ts.map +1 -1
  16. package/dist/local-qa/services/run-result-storage-service.d.ts +37 -0
  17. package/dist/local-qa/services/run-result-storage-service.d.ts.map +1 -1
  18. package/dist/local-qa/tools/tool-registry.d.ts.map +1 -1
  19. package/dist/qa/contracts/index.d.ts +394 -74
  20. package/dist/qa/contracts/index.d.ts.map +1 -1
  21. package/dist/qa/contracts/local-run-schemas.d.ts +123 -0
  22. package/dist/qa/contracts/local-run-schemas.d.ts.map +1 -0
  23. package/dist/qa/tools/tool-registry.d.ts.map +1 -1
  24. package/dist/shared/auth.d.ts +15 -6
  25. package/dist/shared/auth.d.ts.map +1 -1
  26. package/dist/shared/config.d.ts.map +1 -1
  27. package/dist/shared/credentials.d.ts +19 -10
  28. package/dist/shared/credentials.d.ts.map +1 -1
  29. package/dist/shared/types.d.ts +2 -0
  30. package/dist/shared/types.d.ts.map +1 -1
  31. package/package.json +3 -2
  32. package/scripts/postinstall.mjs +268 -9
  33. package/dist/chunk-UKMTQHS2.js.map +0 -1
@@ -0,0 +1,123 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Local execution context schema for local-run upload.
4
+ */
5
+ export declare const LocalExecutionContextInputSchema: z.ZodObject<{
6
+ originalUrl: z.ZodString;
7
+ productionUrl: z.ZodString;
8
+ runByUserId: z.ZodString;
9
+ machineHostname: z.ZodOptional<z.ZodString>;
10
+ osInfo: z.ZodOptional<z.ZodString>;
11
+ electronAppVersion: z.ZodOptional<z.ZodString>;
12
+ mcpServerVersion: z.ZodOptional<z.ZodString>;
13
+ localExecutionCompletedAt: z.ZodNumber;
14
+ uploadedAt: z.ZodOptional<z.ZodNumber>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ productionUrl: string;
17
+ originalUrl: string;
18
+ runByUserId: string;
19
+ localExecutionCompletedAt: number;
20
+ electronAppVersion?: string | undefined;
21
+ osInfo?: string | undefined;
22
+ mcpServerVersion?: string | undefined;
23
+ machineHostname?: string | undefined;
24
+ uploadedAt?: number | undefined;
25
+ }, {
26
+ productionUrl: string;
27
+ originalUrl: string;
28
+ runByUserId: string;
29
+ localExecutionCompletedAt: number;
30
+ electronAppVersion?: string | undefined;
31
+ osInfo?: string | undefined;
32
+ mcpServerVersion?: string | undefined;
33
+ machineHostname?: string | undefined;
34
+ uploadedAt?: number | undefined;
35
+ }>;
36
+ /**
37
+ * Input schema for remote local-run upload tool.
38
+ */
39
+ export declare const LocalRunUploadInputSchema: z.ZodObject<{
40
+ projectId: z.ZodString;
41
+ useCaseId: z.ZodString;
42
+ testCaseId: z.ZodString;
43
+ runType: z.ZodEnum<["generation", "replay"]>;
44
+ productionUrl: z.ZodString;
45
+ localExecutionContext: z.ZodObject<{
46
+ originalUrl: z.ZodString;
47
+ productionUrl: z.ZodString;
48
+ runByUserId: z.ZodString;
49
+ machineHostname: z.ZodOptional<z.ZodString>;
50
+ osInfo: z.ZodOptional<z.ZodString>;
51
+ electronAppVersion: z.ZodOptional<z.ZodString>;
52
+ mcpServerVersion: z.ZodOptional<z.ZodString>;
53
+ localExecutionCompletedAt: z.ZodNumber;
54
+ uploadedAt: z.ZodOptional<z.ZodNumber>;
55
+ }, "strip", z.ZodTypeAny, {
56
+ productionUrl: string;
57
+ originalUrl: string;
58
+ runByUserId: string;
59
+ localExecutionCompletedAt: number;
60
+ electronAppVersion?: string | undefined;
61
+ osInfo?: string | undefined;
62
+ mcpServerVersion?: string | undefined;
63
+ machineHostname?: string | undefined;
64
+ uploadedAt?: number | undefined;
65
+ }, {
66
+ productionUrl: string;
67
+ originalUrl: string;
68
+ runByUserId: string;
69
+ localExecutionCompletedAt: number;
70
+ electronAppVersion?: string | undefined;
71
+ osInfo?: string | undefined;
72
+ mcpServerVersion?: string | undefined;
73
+ machineHostname?: string | undefined;
74
+ uploadedAt?: number | undefined;
75
+ }>;
76
+ actionScript: z.ZodArray<z.ZodUnknown, "many">;
77
+ status: z.ZodEnum<["passed", "failed"]>;
78
+ executionTimeMs: z.ZodNumber;
79
+ errorMessage: z.ZodOptional<z.ZodString>;
80
+ }, "strip", z.ZodTypeAny, {
81
+ status: "failed" | "passed";
82
+ runType: "generation" | "replay";
83
+ projectId: string;
84
+ useCaseId: string;
85
+ productionUrl: string;
86
+ localExecutionContext: {
87
+ productionUrl: string;
88
+ originalUrl: string;
89
+ runByUserId: string;
90
+ localExecutionCompletedAt: number;
91
+ electronAppVersion?: string | undefined;
92
+ osInfo?: string | undefined;
93
+ mcpServerVersion?: string | undefined;
94
+ machineHostname?: string | undefined;
95
+ uploadedAt?: number | undefined;
96
+ };
97
+ executionTimeMs: number;
98
+ actionScript: unknown[];
99
+ testCaseId: string;
100
+ errorMessage?: string | undefined;
101
+ }, {
102
+ status: "failed" | "passed";
103
+ runType: "generation" | "replay";
104
+ projectId: string;
105
+ useCaseId: string;
106
+ productionUrl: string;
107
+ localExecutionContext: {
108
+ productionUrl: string;
109
+ originalUrl: string;
110
+ runByUserId: string;
111
+ localExecutionCompletedAt: number;
112
+ electronAppVersion?: string | undefined;
113
+ osInfo?: string | undefined;
114
+ mcpServerVersion?: string | undefined;
115
+ machineHostname?: string | undefined;
116
+ uploadedAt?: number | undefined;
117
+ };
118
+ executionTimeMs: number;
119
+ actionScript: unknown[];
120
+ testCaseId: string;
121
+ errorMessage?: string | undefined;
122
+ }>;
123
+ //# sourceMappingURL=local-run-schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-run-schemas.d.ts","sourceRoot":"","sources":["../../../src/qa/contracts/local-run-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAU3C,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWpC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../../src/qa/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,EAAgB,iBAAiB,EAAqB,MAAM,aAAa,CAAC;AA6zCjF,+BAA+B;AAC/B,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAanD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAE3E;AAWD;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,CAAC,CAyEzB"}
1
+ {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../../src/qa/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,EAAgB,iBAAiB,EAAqB,MAAM,aAAa,CAAC;AA+1CjF,+BAA+B;AAC/B,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAanD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAE3E;AAWD;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,CAAC,CAyEzB"}
@@ -42,10 +42,11 @@ export declare function createApiKeyWithToken(accessToken: string, keyName: stri
42
42
  }>;
43
43
  /**
44
44
  * Complete the full device code login flow.
45
- * Starts the flow, waits for user authorization, and creates credentials.
45
+ * Starts the flow, waits for user authorization, and stores credentials.
46
+ * API key creation is optional - only created when keyName is provided.
46
47
  *
47
- * @param keyName - Optional name for the API key.
48
- * @param keyExpiry - Expiry option for API key.
48
+ * @param keyName - Optional name for the API key. If provided, creates an API key.
49
+ * @param keyExpiry - Expiry option for API key (only used if keyName is provided).
49
50
  * @param timeoutMs - Maximum time to wait for authorization.
50
51
  * @returns Result of the login flow.
51
52
  */
@@ -56,15 +57,23 @@ export declare function performLogin(keyName?: string, keyExpiry?: "30d" | "90d"
56
57
  error?: string;
57
58
  }>;
58
59
  /**
59
- * Perform logout by clearing credentials.
60
+ * Perform logout by clearing all credentials (auth tokens and API keys).
60
61
  */
61
62
  export declare function performLogout(): void;
62
63
  /**
63
- * Get caller credentials for API requests.
64
- * Returns credentials from storage if available and valid.
64
+ * Get caller credentials for API requests (sync version).
65
+ * Checks for API key first, then falls back to access token.
66
+ * Does NOT auto-refresh - use getCallerCredentialsAsync() for that.
65
67
  * @returns Caller credentials or empty object.
66
68
  */
67
69
  export declare function getCallerCredentials(): ICallerCredentials;
70
+ /**
71
+ * Get caller credentials for API requests (async version with auto-refresh).
72
+ * This is the preferred method - automatically refreshes expired access tokens.
73
+ * Priority: 1) API key (if explicitly set), 2) Access token (with auto-refresh)
74
+ * @returns Caller credentials or empty object.
75
+ */
76
+ export declare function getCallerCredentialsAsync(): Promise<ICallerCredentials>;
68
77
  /**
69
78
  * Check if authentication is required for the given tool.
70
79
  * Local-only tools don't require auth.
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/shared/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAIpB;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAkE5F;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,uBAAuB,CAAC,CA2ElC;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,MAAM,GAAE,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,OAAe,GAC7C,OAAO,CAAC;IACT,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC,CAyCD;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,GAAE,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,OAAe,EACjD,SAAS,GAAE,MAAe,GACzB,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,mBAAmB,CAAC;IACzC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAiFD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,kBAAkB,CAczD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAwD1D"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/shared/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAIpB;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAkE5F;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,uBAAuB,CAAC,CA2ElC;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,MAAM,GAAE,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,OAAe,GAC7C,OAAO,CAAC;IACT,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC,CAyCD;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,GAAE,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,OAAe,EACjD,SAAS,GAAE,MAAe,GACzB,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,mBAAmB,CAAC;IACzC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CA4FD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CASpC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,kBAAkB,CAiBzD;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAiB7E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAuD1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/shared/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAEV,OAAO,EAGP,sBAAsB,EAEvB,MAAM,YAAY,CAAC;AAkIpB;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAsLD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAenC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAGlC;AAQD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAuB9C;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,KAAK,GAAG,UAAU,GAAG,SAAS,CAqB5E;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,sBAAsB,GAAG,SAAS,CAE5E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAG1D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/shared/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAEV,OAAO,EAGP,sBAAsB,EAEvB,MAAM,YAAY,CAAC;AAgKpB;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AA8QD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAenC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAGlC;AAQD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAuB9C;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,KAAK,GAAG,UAAU,GAAG,SAAS,CAqB5E;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,sBAAsB,GAAG,SAAS,CAE5E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAG1D"}
@@ -30,19 +30,28 @@ export declare function deleteCredentials(): void;
30
30
  export declare function isCredentialsExpired(credentials: IStoredCredentials): boolean;
31
31
  /**
32
32
  * Get valid credentials if available.
33
- * Returns null if no credentials or if expired.
33
+ * This is primarily used for API key storage.
34
+ * Access token authentication is handled by AuthService.
34
35
  * @returns Valid credentials or null.
35
36
  */
36
37
  export declare function getValidCredentials(): IStoredCredentials | null;
37
38
  /**
38
- * Get authentication status.
39
- * @returns Object with authentication status information.
39
+ * Check if an API key is stored.
40
+ * @returns True if API key exists.
40
41
  */
41
- export declare function getAuthStatus(): {
42
- authenticated: boolean;
43
- email?: string;
44
- userId?: string;
45
- expiresAt?: string;
46
- hasApiKey: boolean;
47
- };
42
+ export declare function hasApiKey(): boolean;
43
+ /**
44
+ * Get stored API key if available.
45
+ * @returns API key or null.
46
+ */
47
+ export declare function getApiKey(): string | null;
48
+ /**
49
+ * Save API key to credentials.
50
+ * @param apiKey - The API key to save.
51
+ * @param apiKeyId - The API key ID.
52
+ */
53
+ export declare function saveApiKey(params: {
54
+ apiKey: string;
55
+ apiKeyId: string;
56
+ }): void;
48
57
  //# sourceMappingURL=credentials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/shared/credentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKrD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAYD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,kBAAkB,GAAG,IAAI,CA0B3D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,kBAAkB,GAAG,IAAI,CAiBrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAcxC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,kBAAkB,GAAG,OAAO,CAO7E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,GAAG,IAAI,CAa/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB,CAmBA"}
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/shared/credentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKrD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAYD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,kBAAkB,GAAG,IAAI,CA0B3D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,kBAAkB,GAAG,IAAI,CAiBrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAcxC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,kBAAkB,GAAG,OAAO,CAO7E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,GAAG,IAAI,CAa/D;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,GAAG,IAAI,CAGzC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAwB7E"}
@@ -24,6 +24,8 @@ export interface IMuggleConfig {
24
24
  downloadBaseUrl: string;
25
25
  /** SHA256 checksums for each platform binary. */
26
26
  checksums?: IMuggleConfigChecksums;
27
+ /** Default runtime target baked into the package at build/publish time. */
28
+ runtimeTargetDefault?: "production" | "dev";
27
29
  }
28
30
  /**
29
31
  * Auth0 configuration for device code flow.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muggleai/mcp",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Unified MCP server for Muggle AI - Cloud QA and Local Testing tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,8 +26,9 @@
26
26
  "test:watch": "vitest"
27
27
  },
28
28
  "muggleConfig": {
29
- "electronAppVersion": "1.0.6",
29
+ "electronAppVersion": "1.0.8",
30
30
  "downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download",
31
+ "runtimeTargetDefault": "production",
31
32
  "checksums": {
32
33
  "darwin-arm64": "",
33
34
  "darwin-x64": "",
@@ -7,12 +7,14 @@
7
7
  import { createHash } from "crypto";
8
8
  import { exec } from "child_process";
9
9
  import {
10
+ readFileSync,
10
11
  createReadStream,
11
12
  createWriteStream,
12
13
  existsSync,
13
14
  mkdirSync,
14
15
  readdirSync,
15
16
  rmSync,
17
+ writeFileSync,
16
18
  } from "fs";
17
19
  import { homedir, platform } from "os";
18
20
  import { join } from "path";
@@ -21,6 +23,86 @@ import { createRequire } from "module";
21
23
 
22
24
  const require = createRequire(import.meta.url);
23
25
  const VERSION_DIRECTORY_NAME_PATTERN = /^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/;
26
+ const CURSOR_SERVER_NAME = "muggle";
27
+ const INSTALL_METADATA_FILE_NAME = ".install-metadata.json";
28
+
29
+ /**
30
+ * Get the Cursor MCP config path.
31
+ * @returns {string} Path to ~/.cursor/mcp.json
32
+ */
33
+ function getCursorMcpConfigPath() {
34
+ return join(homedir(), ".cursor", "mcp.json");
35
+ }
36
+
37
+ /**
38
+ * Build the default Cursor server configuration for this package.
39
+ * @returns {{command: string, args: string[]}} Server configuration
40
+ */
41
+ function buildCursorServerConfig() {
42
+ const localCliPath = join(process.cwd(), "bin", "muggle-mcp.js");
43
+ return {
44
+ command: "node",
45
+ args: [localCliPath, "serve"],
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Read and parse Cursor mcp.json.
51
+ * @param {string} configPath - Path to mcp.json
52
+ * @returns {Record<string, unknown>} Parsed config object
53
+ */
54
+ function readCursorConfig(configPath) {
55
+ if (!existsSync(configPath)) {
56
+ return {};
57
+ }
58
+
59
+ const rawConfig = readFileSync(configPath, "utf-8");
60
+ const parsedConfig = JSON.parse(rawConfig);
61
+
62
+ if (typeof parsedConfig !== "object" || parsedConfig === null || Array.isArray(parsedConfig)) {
63
+ throw new Error(`Invalid JSON structure in ${configPath}: expected an object at root`);
64
+ }
65
+
66
+ return parsedConfig;
67
+ }
68
+
69
+ /**
70
+ * Update ~/.cursor/mcp.json with the muggle server entry.
71
+ * Existing server configurations are preserved.
72
+ */
73
+ function updateCursorMcpConfig() {
74
+ const configPath = getCursorMcpConfigPath();
75
+ const cursorDir = join(homedir(), ".cursor");
76
+
77
+ try {
78
+ const parsedConfig = readCursorConfig(configPath);
79
+ const currentMcpServers = parsedConfig.mcpServers;
80
+ let normalizedMcpServers = {};
81
+
82
+ if (currentMcpServers !== undefined) {
83
+ if (typeof currentMcpServers !== "object" || currentMcpServers === null || Array.isArray(currentMcpServers)) {
84
+ throw new Error(`Invalid mcpServers in ${configPath}: expected an object`);
85
+ }
86
+ normalizedMcpServers = currentMcpServers;
87
+ }
88
+
89
+ normalizedMcpServers[CURSOR_SERVER_NAME] = buildCursorServerConfig();
90
+ parsedConfig.mcpServers = normalizedMcpServers;
91
+
92
+ mkdirSync(cursorDir, { recursive: true });
93
+ const prettyJson = `${JSON.stringify(parsedConfig, null, 2)}\n`;
94
+ writeFileSync(configPath, prettyJson, "utf-8");
95
+ console.log(`Updated Cursor MCP config: ${configPath}`);
96
+ } catch (error) {
97
+ console.error("\n========================================");
98
+ console.error("ERROR: Failed to update Cursor MCP config");
99
+ console.error("========================================\n");
100
+ console.error("Path:", configPath);
101
+ console.error("\nFull error details:");
102
+ console.error(error instanceof Error ? error.stack || error : error);
103
+ console.error("");
104
+ }
105
+ }
24
106
 
25
107
  /**
26
108
  * Get the Muggle AI data directory.
@@ -180,6 +262,141 @@ function getBinaryName() {
180
262
  }
181
263
  }
182
264
 
265
+ /**
266
+ * Get the expected extracted executable path for the current platform.
267
+ * @param {string} versionDir - Version directory path
268
+ * @returns {string} Expected executable path
269
+ */
270
+ function getExpectedExecutablePath(versionDir) {
271
+ const os = platform();
272
+
273
+ switch (os) {
274
+ case "darwin":
275
+ return join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
276
+ case "win32":
277
+ return join(versionDir, "MuggleAI.exe");
278
+ case "linux":
279
+ return join(versionDir, "MuggleAI");
280
+ default:
281
+ throw new Error(`Unsupported platform: ${os}`);
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Get the metadata file path for an installed version.
287
+ * @param {string} versionDir - Version directory path
288
+ * @returns {string} Metadata file path
289
+ */
290
+ function getInstallMetadataPath(versionDir) {
291
+ return join(versionDir, INSTALL_METADATA_FILE_NAME);
292
+ }
293
+
294
+ /**
295
+ * Read install metadata from disk.
296
+ * @param {string} metadataPath - Metadata file path
297
+ * @returns {Record<string, unknown> | null} Parsed metadata, or null if missing/invalid
298
+ */
299
+ function readInstallMetadata(metadataPath) {
300
+ if (!existsSync(metadataPath)) {
301
+ return null;
302
+ }
303
+
304
+ try {
305
+ const metadataContent = readFileSync(metadataPath, "utf-8");
306
+ const parsedMetadata = JSON.parse(metadataContent);
307
+ if (typeof parsedMetadata !== "object" || parsedMetadata === null || Array.isArray(parsedMetadata)) {
308
+ return null;
309
+ }
310
+ return parsedMetadata;
311
+ } catch {
312
+ return null;
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Persist install metadata to disk.
318
+ * @param {object} params - Metadata fields
319
+ * @param {string} params.metadataPath - Metadata file path
320
+ * @param {string} params.version - Installed version
321
+ * @param {string} params.binaryName - Archive filename
322
+ * @param {string} params.platformKey - Platform key
323
+ * @param {string} params.executableChecksum - Checksum of extracted executable
324
+ * @param {string} params.expectedArchiveChecksum - Configured archive checksum for platform
325
+ */
326
+ function writeInstallMetadata({
327
+ metadataPath,
328
+ version,
329
+ binaryName,
330
+ platformKey,
331
+ executableChecksum,
332
+ expectedArchiveChecksum,
333
+ }) {
334
+ const metadata = {
335
+ version: version,
336
+ binaryName: binaryName,
337
+ platformKey: platformKey,
338
+ executableChecksum: executableChecksum,
339
+ expectedArchiveChecksum: expectedArchiveChecksum,
340
+ updatedAt: new Date().toISOString(),
341
+ };
342
+
343
+ writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, "utf-8");
344
+ }
345
+
346
+ /**
347
+ * Verify existing installed executable and metadata.
348
+ * @param {object} params - Verification params
349
+ * @param {string} params.versionDir - Installed version directory
350
+ * @param {string} params.executablePath - Expected executable path
351
+ * @param {string} params.version - Version string
352
+ * @param {string} params.binaryName - Archive filename
353
+ * @param {string} params.platformKey - Platform key
354
+ * @param {string} params.expectedArchiveChecksum - Configured checksum for downloaded archive
355
+ * @returns {Promise<{valid: boolean, reason: string}>} Verification result
356
+ */
357
+ async function verifyExistingInstall({
358
+ versionDir,
359
+ executablePath,
360
+ version,
361
+ binaryName,
362
+ platformKey,
363
+ expectedArchiveChecksum,
364
+ }) {
365
+ const metadataPath = getInstallMetadataPath(versionDir);
366
+ const metadata = readInstallMetadata(metadataPath);
367
+
368
+ if (!metadata) {
369
+ return { valid: false, reason: "install metadata is missing or invalid" };
370
+ }
371
+
372
+ if (metadata.version !== version) {
373
+ return { valid: false, reason: "installed metadata version does not match configured version" };
374
+ }
375
+
376
+ if (metadata.binaryName !== binaryName) {
377
+ return { valid: false, reason: "installed metadata binary name does not match current platform archive" };
378
+ }
379
+
380
+ if (metadata.platformKey !== platformKey) {
381
+ return { valid: false, reason: "installed metadata platform key does not match current platform" };
382
+ }
383
+
384
+ if ((metadata.expectedArchiveChecksum || "") !== expectedArchiveChecksum) {
385
+ return { valid: false, reason: "configured archive checksum changed since previous install" };
386
+ }
387
+
388
+ if (typeof metadata.executableChecksum !== "string" || metadata.executableChecksum === "") {
389
+ return { valid: false, reason: "installed metadata executable checksum is missing" };
390
+ }
391
+
392
+ const currentExecutableChecksum = await calculateFileChecksum(executablePath);
393
+ if (currentExecutableChecksum !== metadata.executableChecksum) {
394
+ return { valid: false, reason: "installed executable checksum does not match recorded checksum" };
395
+ }
396
+
397
+ return { valid: true, reason: "installed executable checksum is valid" };
398
+ }
399
+
183
400
  /**
184
401
  * Download and extract the Electron app.
185
402
  */
@@ -192,16 +409,44 @@ async function downloadElectronApp() {
192
409
  const baseUrl = config.downloadBaseUrl || "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download";
193
410
 
194
411
  const binaryName = getBinaryName();
412
+ const checksums = config.checksums || {};
413
+ const platformKey = getPlatformKey();
414
+ const expectedChecksum = checksums[platformKey] || "";
195
415
  const downloadUrl = `${baseUrl}/v${version}/${binaryName}`;
196
416
 
197
417
  const appDir = getElectronAppDir();
198
418
  const versionDir = join(appDir, version);
419
+ const metadataPath = getInstallMetadataPath(versionDir);
199
420
 
200
- // Check if already downloaded
421
+ // Check if already downloaded and extracted correctly
422
+ const expectedExecutablePath = getExpectedExecutablePath(versionDir);
201
423
  if (existsSync(versionDir)) {
202
- cleanupNonCurrentVersions({ appDir: appDir, currentVersion: version });
203
- console.log(`Electron app v${version} already installed at ${versionDir}`);
204
- return;
424
+ if (existsSync(expectedExecutablePath)) {
425
+ const existingInstallVerification = await verifyExistingInstall({
426
+ versionDir: versionDir,
427
+ executablePath: expectedExecutablePath,
428
+ version: version,
429
+ binaryName: binaryName,
430
+ platformKey: platformKey,
431
+ expectedArchiveChecksum: expectedChecksum,
432
+ });
433
+
434
+ if (existingInstallVerification.valid) {
435
+ cleanupNonCurrentVersions({ appDir: appDir, currentVersion: version });
436
+ console.log(`Electron app v${version} already installed at ${versionDir}`);
437
+ return;
438
+ }
439
+
440
+ console.log(
441
+ `Installed Electron app v${version} failed verification (${existingInstallVerification.reason}). Re-downloading...`,
442
+ );
443
+ rmSync(versionDir, { recursive: true, force: true });
444
+ } else {
445
+ console.log(
446
+ `Electron app v${version} directory exists but executable is missing. Re-downloading...`,
447
+ );
448
+ rmSync(versionDir, { recursive: true, force: true });
449
+ }
205
450
  }
206
451
 
207
452
  console.log(`Downloading Muggle Test Electron app v${version}...`);
@@ -229,11 +474,6 @@ async function downloadElectronApp() {
229
474
 
230
475
  console.log("Download complete, verifying checksum...");
231
476
 
232
- // Get expected checksum from config
233
- const checksums = config.checksums || {};
234
- const platformKey = getPlatformKey();
235
- const expectedChecksum = checksums[platformKey] || "";
236
-
237
477
  // Verify checksum
238
478
  const checksumResult = await verifyFileChecksum(tempFile, expectedChecksum);
239
479
 
@@ -265,6 +505,24 @@ async function downloadElectronApp() {
265
505
  // Clean up temp file
266
506
  rmSync(tempFile, { force: true });
267
507
 
508
+ if (!existsSync(expectedExecutablePath)) {
509
+ throw new Error(
510
+ `Extraction completed but executable was not found.\n` +
511
+ `Expected path: ${expectedExecutablePath}\n` +
512
+ `Version directory: ${versionDir}`,
513
+ );
514
+ }
515
+
516
+ const executableChecksum = await calculateFileChecksum(expectedExecutablePath);
517
+ writeInstallMetadata({
518
+ metadataPath: metadataPath,
519
+ version: version,
520
+ binaryName: binaryName,
521
+ platformKey: platformKey,
522
+ executableChecksum: executableChecksum,
523
+ expectedArchiveChecksum: expectedChecksum,
524
+ });
525
+
268
526
  cleanupNonCurrentVersions({ appDir: appDir, currentVersion: version });
269
527
 
270
528
  console.log(`Electron app installed to ${versionDir}`);
@@ -334,4 +592,5 @@ async function extractTarGz(tarPath, destDir) {
334
592
  }
335
593
 
336
594
  // Run postinstall
595
+ updateCursorMcpConfig();
337
596
  downloadElectronApp().catch(console.error);