@muggleai/works 4.0.3 → 4.1.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.
- package/README.md +3 -3
- package/dist/{chunk-BQZQDOXI.js → chunk-PV76IWEX.js} +75 -76
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -375,8 +375,8 @@ Data directory structure (~/.muggle-ai/)
|
|
|
375
375
|
|
|
376
376
|
```
|
|
377
377
|
~/.muggle-ai/
|
|
378
|
-
├──
|
|
379
|
-
├──
|
|
378
|
+
├── oauth-session.json # OAuth tokens (short-lived, auto-refresh)
|
|
379
|
+
├── api-key.json # Long-lived API key for service calls
|
|
380
380
|
├── projects/ # Local project cache
|
|
381
381
|
├── sessions/ # QA sessions
|
|
382
382
|
│ └── {runId}/
|
|
@@ -424,7 +424,7 @@ muggle doctor # Diagnose
|
|
|
424
424
|
|
|
425
425
|
```bash
|
|
426
426
|
muggle logout # Clear all credentials
|
|
427
|
-
rm ~/.muggle-ai/
|
|
427
|
+
rm ~/.muggle-ai/oauth-session.json ~/.muggle-ai/api-key.json
|
|
428
428
|
muggle login # Fresh login
|
|
429
429
|
```
|
|
430
430
|
|
|
@@ -39,7 +39,7 @@ var DEFAULT_PROMPT_SERVICE_PRODUCTION_URL = "https://promptservice.muggle-ai.com
|
|
|
39
39
|
var DEFAULT_PROMPT_SERVICE_DEV_URL = "http://localhost:5050";
|
|
40
40
|
var DEFAULT_WEB_SERVICE_URL = "http://localhost:3001";
|
|
41
41
|
var ELECTRON_APP_DIR = "electron-app";
|
|
42
|
-
var
|
|
42
|
+
var API_KEY_FILE = "api-key.json";
|
|
43
43
|
var DEFAULT_AUTH0_PRODUCTION_DOMAIN = "login.muggle-ai.com";
|
|
44
44
|
var DEFAULT_AUTH0_PRODUCTION_CLIENT_ID = "UgG5UjoyLksxMciWWKqVpwfWrJ4rFvtT";
|
|
45
45
|
var DEFAULT_AUTH0_PRODUCTION_AUDIENCE = "https://muggleai.us.auth0.com/api/v2/";
|
|
@@ -239,10 +239,10 @@ function getDefaultAuth0Domain() {
|
|
|
239
239
|
}
|
|
240
240
|
function getDefaultAuth0ClientId() {
|
|
241
241
|
const runtimeTarget = getPromptServiceRuntimeTarget();
|
|
242
|
-
if (runtimeTarget === "
|
|
243
|
-
return
|
|
242
|
+
if (runtimeTarget === "production") {
|
|
243
|
+
return DEFAULT_AUTH0_PRODUCTION_CLIENT_ID;
|
|
244
244
|
}
|
|
245
|
-
return
|
|
245
|
+
return DEFAULT_AUTH0_DEV_CLIENT_ID;
|
|
246
246
|
}
|
|
247
247
|
function getDefaultAuth0Audience() {
|
|
248
248
|
const runtimeTarget = getPromptServiceRuntimeTarget();
|
|
@@ -284,8 +284,8 @@ function buildLocalQaConfig() {
|
|
|
284
284
|
sessionsDir: path2.join(dataDir, "sessions"),
|
|
285
285
|
projectsDir: path2.join(dataDir, "projects"),
|
|
286
286
|
tempDir: path2.join(dataDir, "temp"),
|
|
287
|
-
|
|
288
|
-
|
|
287
|
+
apiKeyFilePath: path2.join(dataDir, API_KEY_FILE),
|
|
288
|
+
oauthSessionFilePath: path2.join(dataDir, "oauth-session.json"),
|
|
289
289
|
electronAppPath: resolveElectronAppPathOrNull(),
|
|
290
290
|
webServicePath: resolveWebServicePath(),
|
|
291
291
|
webServicePidFile: path2.join(dataDir, "web-service.pid"),
|
|
@@ -610,8 +610,8 @@ var TestResultStatus = /* @__PURE__ */ ((TestResultStatus2) => {
|
|
|
610
610
|
// packages/mcps/src/mcp/local/services/auth-service.ts
|
|
611
611
|
var DEFAULT_LOGIN_WAIT_TIMEOUT_MS = 12e4;
|
|
612
612
|
var AuthService = class {
|
|
613
|
-
/** Path to the
|
|
614
|
-
|
|
613
|
+
/** Path to the OAuth session file. */
|
|
614
|
+
oauthSessionFilePath;
|
|
615
615
|
/** Path to the pending device code file. */
|
|
616
616
|
pendingDeviceCodePath;
|
|
617
617
|
/**
|
|
@@ -619,9 +619,9 @@ var AuthService = class {
|
|
|
619
619
|
*/
|
|
620
620
|
constructor() {
|
|
621
621
|
const config = getConfig();
|
|
622
|
-
this.
|
|
622
|
+
this.oauthSessionFilePath = config.localQa.oauthSessionFilePath;
|
|
623
623
|
this.pendingDeviceCodePath = path2.join(
|
|
624
|
-
path2.dirname(config.localQa.
|
|
624
|
+
path2.dirname(config.localQa.oauthSessionFilePath),
|
|
625
625
|
"pending-device-code.json"
|
|
626
626
|
);
|
|
627
627
|
}
|
|
@@ -931,11 +931,11 @@ var AuthService = class {
|
|
|
931
931
|
email,
|
|
932
932
|
userId
|
|
933
933
|
};
|
|
934
|
-
const dir = path2.dirname(this.
|
|
934
|
+
const dir = path2.dirname(this.oauthSessionFilePath);
|
|
935
935
|
if (!fs3.existsSync(dir)) {
|
|
936
936
|
fs3.mkdirSync(dir, { recursive: true });
|
|
937
937
|
}
|
|
938
|
-
fs3.writeFileSync(this.
|
|
938
|
+
fs3.writeFileSync(this.oauthSessionFilePath, JSON.stringify(storedAuth, null, 2), {
|
|
939
939
|
encoding: "utf-8",
|
|
940
940
|
mode: 384
|
|
941
941
|
});
|
|
@@ -946,11 +946,11 @@ var AuthService = class {
|
|
|
946
946
|
*/
|
|
947
947
|
loadStoredAuth() {
|
|
948
948
|
const logger14 = getLogger();
|
|
949
|
-
if (!fs3.existsSync(this.
|
|
949
|
+
if (!fs3.existsSync(this.oauthSessionFilePath)) {
|
|
950
950
|
return null;
|
|
951
951
|
}
|
|
952
952
|
try {
|
|
953
|
-
const content = fs3.readFileSync(this.
|
|
953
|
+
const content = fs3.readFileSync(this.oauthSessionFilePath, "utf-8");
|
|
954
954
|
return JSON.parse(content);
|
|
955
955
|
} catch (error) {
|
|
956
956
|
logger14.error("Failed to load stored auth", {
|
|
@@ -1034,11 +1034,11 @@ var AuthService = class {
|
|
|
1034
1034
|
email: storedAuth.email,
|
|
1035
1035
|
userId: storedAuth.userId
|
|
1036
1036
|
};
|
|
1037
|
-
const dir = path2.dirname(this.
|
|
1037
|
+
const dir = path2.dirname(this.oauthSessionFilePath);
|
|
1038
1038
|
if (!fs3.existsSync(dir)) {
|
|
1039
1039
|
fs3.mkdirSync(dir, { recursive: true });
|
|
1040
1040
|
}
|
|
1041
|
-
fs3.writeFileSync(this.
|
|
1041
|
+
fs3.writeFileSync(this.oauthSessionFilePath, JSON.stringify(updatedAuth, null, 2), {
|
|
1042
1042
|
encoding: "utf-8",
|
|
1043
1043
|
mode: 384
|
|
1044
1044
|
});
|
|
@@ -1090,12 +1090,12 @@ var AuthService = class {
|
|
|
1090
1090
|
*/
|
|
1091
1091
|
logout() {
|
|
1092
1092
|
const logger14 = getLogger();
|
|
1093
|
-
if (!fs3.existsSync(this.
|
|
1093
|
+
if (!fs3.existsSync(this.oauthSessionFilePath)) {
|
|
1094
1094
|
logger14.debug("No auth to clear");
|
|
1095
1095
|
return false;
|
|
1096
1096
|
}
|
|
1097
1097
|
try {
|
|
1098
|
-
fs3.unlinkSync(this.
|
|
1098
|
+
fs3.unlinkSync(this.oauthSessionFilePath);
|
|
1099
1099
|
logger14.info("Auth cleared successfully");
|
|
1100
1100
|
return true;
|
|
1101
1101
|
} catch (error) {
|
|
@@ -2380,9 +2380,9 @@ function listActiveExecutions() {
|
|
|
2380
2380
|
status: process2.status
|
|
2381
2381
|
}));
|
|
2382
2382
|
}
|
|
2383
|
-
var
|
|
2384
|
-
function
|
|
2385
|
-
return path2.join(getDataDir(),
|
|
2383
|
+
var API_KEY_FILE2 = "api-key.json";
|
|
2384
|
+
function getApiKeyFilePath() {
|
|
2385
|
+
return path2.join(getDataDir(), API_KEY_FILE2);
|
|
2386
2386
|
}
|
|
2387
2387
|
function ensureDataDir() {
|
|
2388
2388
|
const dataDir = getDataDir();
|
|
@@ -2390,95 +2390,85 @@ function ensureDataDir() {
|
|
|
2390
2390
|
fs3.mkdirSync(dataDir, { recursive: true });
|
|
2391
2391
|
}
|
|
2392
2392
|
}
|
|
2393
|
-
function
|
|
2393
|
+
function loadApiKeyData() {
|
|
2394
2394
|
const logger14 = getLogger();
|
|
2395
|
-
const
|
|
2395
|
+
const apiKeyPath = getApiKeyFilePath();
|
|
2396
2396
|
try {
|
|
2397
|
-
if (!fs3.existsSync(
|
|
2398
|
-
logger14.debug("No
|
|
2397
|
+
if (!fs3.existsSync(apiKeyPath)) {
|
|
2398
|
+
logger14.debug("No API key file found", { path: apiKeyPath });
|
|
2399
2399
|
return null;
|
|
2400
2400
|
}
|
|
2401
|
-
const content = fs3.readFileSync(
|
|
2402
|
-
const
|
|
2403
|
-
|
|
2404
|
-
logger14.warn("Invalid credentials file - missing required fields");
|
|
2405
|
-
return null;
|
|
2406
|
-
}
|
|
2407
|
-
return credentials;
|
|
2401
|
+
const content = fs3.readFileSync(apiKeyPath, "utf-8");
|
|
2402
|
+
const data = JSON.parse(content);
|
|
2403
|
+
return data;
|
|
2408
2404
|
} catch (error) {
|
|
2409
|
-
logger14.warn("Failed to load
|
|
2405
|
+
logger14.warn("Failed to load API key data", {
|
|
2410
2406
|
error: error instanceof Error ? error.message : String(error)
|
|
2411
2407
|
});
|
|
2412
2408
|
return null;
|
|
2413
2409
|
}
|
|
2414
2410
|
}
|
|
2415
|
-
function
|
|
2411
|
+
function saveApiKeyData(data) {
|
|
2416
2412
|
const logger14 = getLogger();
|
|
2417
|
-
const
|
|
2413
|
+
const apiKeyPath = getApiKeyFilePath();
|
|
2418
2414
|
try {
|
|
2419
2415
|
ensureDataDir();
|
|
2420
|
-
const content = JSON.stringify(
|
|
2421
|
-
fs3.writeFileSync(
|
|
2422
|
-
logger14.info("
|
|
2416
|
+
const content = JSON.stringify(data, null, 2);
|
|
2417
|
+
fs3.writeFileSync(apiKeyPath, content, { mode: 384 });
|
|
2418
|
+
logger14.info("API key saved", { path: apiKeyPath });
|
|
2423
2419
|
} catch (error) {
|
|
2424
|
-
logger14.error("Failed to save
|
|
2420
|
+
logger14.error("Failed to save API key", {
|
|
2425
2421
|
error: error instanceof Error ? error.message : String(error)
|
|
2426
2422
|
});
|
|
2427
2423
|
throw error;
|
|
2428
2424
|
}
|
|
2429
2425
|
}
|
|
2430
|
-
function
|
|
2426
|
+
function deleteApiKeyData() {
|
|
2431
2427
|
const logger14 = getLogger();
|
|
2432
|
-
const
|
|
2428
|
+
const apiKeyPath = getApiKeyFilePath();
|
|
2433
2429
|
try {
|
|
2434
|
-
if (fs3.existsSync(
|
|
2435
|
-
fs3.unlinkSync(
|
|
2436
|
-
logger14.info("
|
|
2430
|
+
if (fs3.existsSync(apiKeyPath)) {
|
|
2431
|
+
fs3.unlinkSync(apiKeyPath);
|
|
2432
|
+
logger14.info("API key deleted", { path: apiKeyPath });
|
|
2437
2433
|
}
|
|
2438
2434
|
} catch (error) {
|
|
2439
|
-
logger14.warn("Failed to delete
|
|
2435
|
+
logger14.warn("Failed to delete API key", {
|
|
2440
2436
|
error: error instanceof Error ? error.message : String(error)
|
|
2441
2437
|
});
|
|
2442
2438
|
}
|
|
2443
2439
|
}
|
|
2444
|
-
function
|
|
2445
|
-
const
|
|
2446
|
-
|
|
2447
|
-
const bufferMs = 5 * 60 * 1e3;
|
|
2448
|
-
return now.getTime() >= expiresAt.getTime() - bufferMs;
|
|
2449
|
-
}
|
|
2450
|
-
function getValidCredentials() {
|
|
2451
|
-
const credentials = loadCredentials();
|
|
2452
|
-
if (!credentials) {
|
|
2440
|
+
function getValidApiKeyData() {
|
|
2441
|
+
const data = loadApiKeyData();
|
|
2442
|
+
if (!data) {
|
|
2453
2443
|
return null;
|
|
2454
2444
|
}
|
|
2455
|
-
if (
|
|
2456
|
-
return
|
|
2445
|
+
if (data.apiKey) {
|
|
2446
|
+
return data;
|
|
2457
2447
|
}
|
|
2458
2448
|
return null;
|
|
2459
2449
|
}
|
|
2460
2450
|
function hasApiKey() {
|
|
2461
|
-
const
|
|
2462
|
-
return !!
|
|
2451
|
+
const data = loadApiKeyData();
|
|
2452
|
+
return !!data?.apiKey;
|
|
2463
2453
|
}
|
|
2464
2454
|
function getApiKey() {
|
|
2465
|
-
const
|
|
2466
|
-
return
|
|
2455
|
+
const data = loadApiKeyData();
|
|
2456
|
+
return data?.apiKey ?? null;
|
|
2467
2457
|
}
|
|
2468
2458
|
function saveApiKey(params) {
|
|
2469
2459
|
const logger14 = getLogger();
|
|
2470
|
-
const
|
|
2460
|
+
const apiKeyPath = getApiKeyFilePath();
|
|
2471
2461
|
try {
|
|
2472
2462
|
ensureDataDir();
|
|
2473
|
-
const
|
|
2463
|
+
const data = {
|
|
2474
2464
|
accessToken: "",
|
|
2475
2465
|
expiresAt: "",
|
|
2476
2466
|
apiKey: params.apiKey,
|
|
2477
2467
|
apiKeyId: params.apiKeyId
|
|
2478
2468
|
};
|
|
2479
|
-
const content = JSON.stringify(
|
|
2480
|
-
fs3.writeFileSync(
|
|
2481
|
-
logger14.info("API key saved", { path:
|
|
2469
|
+
const content = JSON.stringify(data, null, 2);
|
|
2470
|
+
fs3.writeFileSync(apiKeyPath, content, { mode: 384 });
|
|
2471
|
+
logger14.info("API key saved", { path: apiKeyPath });
|
|
2482
2472
|
} catch (error) {
|
|
2483
2473
|
logger14.error("Failed to save API key", {
|
|
2484
2474
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -2486,6 +2476,11 @@ function saveApiKey(params) {
|
|
|
2486
2476
|
throw error;
|
|
2487
2477
|
}
|
|
2488
2478
|
}
|
|
2479
|
+
var loadCredentials = loadApiKeyData;
|
|
2480
|
+
var saveCredentials = saveApiKeyData;
|
|
2481
|
+
var deleteCredentials = deleteApiKeyData;
|
|
2482
|
+
var getValidCredentials = getValidApiKeyData;
|
|
2483
|
+
var getCredentialsFilePath = getApiKeyFilePath;
|
|
2489
2484
|
|
|
2490
2485
|
// packages/mcps/src/shared/auth.ts
|
|
2491
2486
|
var logger4 = getLogger();
|
|
@@ -2694,7 +2689,7 @@ async function performLogin(keyName, keyExpiry = "90d", timeoutMs = 12e4) {
|
|
|
2694
2689
|
);
|
|
2695
2690
|
credentials.apiKey = apiKeyResult.key;
|
|
2696
2691
|
credentials.apiKeyId = apiKeyResult.id;
|
|
2697
|
-
|
|
2692
|
+
saveApiKeyData(credentials);
|
|
2698
2693
|
}
|
|
2699
2694
|
return {
|
|
2700
2695
|
success: true,
|
|
@@ -2731,13 +2726,13 @@ async function performLogin(keyName, keyExpiry = "90d", timeoutMs = 12e4) {
|
|
|
2731
2726
|
function performLogout() {
|
|
2732
2727
|
const authService = getAuthService();
|
|
2733
2728
|
authService.logout();
|
|
2734
|
-
|
|
2729
|
+
deleteApiKeyData();
|
|
2735
2730
|
logger4.info("[Auth] Logged out successfully");
|
|
2736
2731
|
}
|
|
2737
2732
|
function getCallerCredentials() {
|
|
2738
|
-
const
|
|
2739
|
-
if (
|
|
2740
|
-
return { apiKey:
|
|
2733
|
+
const apiKeyData = getValidApiKeyData();
|
|
2734
|
+
if (apiKeyData?.apiKey) {
|
|
2735
|
+
return { apiKey: apiKeyData.apiKey };
|
|
2741
2736
|
}
|
|
2742
2737
|
const authService = getAuthService();
|
|
2743
2738
|
const accessToken = authService.getAccessToken();
|
|
@@ -2747,9 +2742,9 @@ function getCallerCredentials() {
|
|
|
2747
2742
|
return {};
|
|
2748
2743
|
}
|
|
2749
2744
|
async function getCallerCredentialsAsync() {
|
|
2750
|
-
const
|
|
2751
|
-
if (
|
|
2752
|
-
return { apiKey:
|
|
2745
|
+
const apiKeyData = getValidApiKeyData();
|
|
2746
|
+
if (apiKeyData?.apiKey) {
|
|
2747
|
+
return { apiKey: apiKeyData.apiKey };
|
|
2753
2748
|
}
|
|
2754
2749
|
const authService = getAuthService();
|
|
2755
2750
|
const accessToken = await authService.getValidAccessToken();
|
|
@@ -5466,8 +5461,10 @@ __export(src_exports, {
|
|
|
5466
5461
|
calculateFileChecksum: () => calculateFileChecksum,
|
|
5467
5462
|
createApiKeyWithToken: () => createApiKeyWithToken,
|
|
5468
5463
|
createChildLogger: () => createChildLogger,
|
|
5464
|
+
deleteApiKeyData: () => deleteApiKeyData,
|
|
5469
5465
|
deleteCredentials: () => deleteCredentials,
|
|
5470
5466
|
getApiKey: () => getApiKey,
|
|
5467
|
+
getApiKeyFilePath: () => getApiKeyFilePath,
|
|
5471
5468
|
getAuthService: () => getAuthService,
|
|
5472
5469
|
getBundledElectronAppVersion: () => getBundledElectronAppVersion,
|
|
5473
5470
|
getCallerCredentials: () => getCallerCredentials,
|
|
@@ -5485,10 +5482,11 @@ __export(src_exports, {
|
|
|
5485
5482
|
getLogger: () => getLogger,
|
|
5486
5483
|
getPlatformKey: () => getPlatformKey,
|
|
5487
5484
|
getQaTools: () => getQaTools,
|
|
5485
|
+
getValidApiKeyData: () => getValidApiKeyData,
|
|
5488
5486
|
getValidCredentials: () => getValidCredentials,
|
|
5489
5487
|
hasApiKey: () => hasApiKey,
|
|
5490
|
-
isCredentialsExpired: () => isCredentialsExpired,
|
|
5491
5488
|
isElectronAppInstalled: () => isElectronAppInstalled,
|
|
5489
|
+
loadApiKeyData: () => loadApiKeyData,
|
|
5492
5490
|
loadCredentials: () => loadCredentials,
|
|
5493
5491
|
localQa: () => local_exports2,
|
|
5494
5492
|
mcp: () => mcp_exports,
|
|
@@ -5500,6 +5498,7 @@ __export(src_exports, {
|
|
|
5500
5498
|
resetConfig: () => resetConfig,
|
|
5501
5499
|
resetLogger: () => resetLogger,
|
|
5502
5500
|
saveApiKey: () => saveApiKey,
|
|
5501
|
+
saveApiKeyData: () => saveApiKeyData,
|
|
5503
5502
|
saveCredentials: () => saveCredentials,
|
|
5504
5503
|
startDeviceCodeFlow: () => startDeviceCodeFlow,
|
|
5505
5504
|
toolRequiresAuth: () => toolRequiresAuth,
|
|
@@ -6438,7 +6437,7 @@ function getHelpGuidance() {
|
|
|
6438
6437
|
` 2. ${colorize("Log in", COLORS.bold)} with your Muggle AI account`,
|
|
6439
6438
|
` 3. ${colorize("The tool call continues", COLORS.bold)} with your credentials`,
|
|
6440
6439
|
"",
|
|
6441
|
-
`
|
|
6440
|
+
` API keys are stored in ${path12("~/.muggle-ai/api-key.json")}`,
|
|
6442
6441
|
"",
|
|
6443
6442
|
header("Available MCP Tools"),
|
|
6444
6443
|
"",
|
|
@@ -6455,7 +6454,7 @@ function getHelpGuidance() {
|
|
|
6455
6454
|
"",
|
|
6456
6455
|
` All data is stored in ${path12("~/.muggle-ai/")}:`,
|
|
6457
6456
|
"",
|
|
6458
|
-
` ${path12("
|
|
6457
|
+
` ${path12("api-key.json")} Long-lived API key (auto-generated)`,
|
|
6459
6458
|
` ${path12("projects/")} Local test projects`,
|
|
6460
6459
|
` ${path12("sessions/")} Test execution sessions`,
|
|
6461
6460
|
` ${path12("electron-app/")} Downloaded Electron app binaries`,
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { src_exports2 as commands, createChildLogger, createUnifiedMcpServer, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, qa_exports as qa, server_exports as server, src_exports as shared } from './chunk-
|
|
1
|
+
export { src_exports2 as commands, createChildLogger, createUnifiedMcpServer, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, qa_exports as qa, server_exports as server, src_exports as shared } from './chunk-PV76IWEX.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"description": "Run real-browser QA tests on your web app from any AI coding agent. Generate test scripts from plain English, replay them on localhost, capture screenshots, and validate user flows like signup, checkout, and dashboards. Works across Claude Code, Cursor, Codex, and Windsurf.",
|
|
4
|
-
"version": "4.0
|
|
4
|
+
"version": "4.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"displayName": "Muggle AI",
|
|
4
4
|
"description": "Ship quality products with AI-powered QA that validates your app's user experience — from Claude Code and Cursor to PR.",
|
|
5
|
-
"version": "4.0
|
|
5
|
+
"version": "4.1.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muggleai/works",
|
|
3
3
|
"mcpName": "io.github.multiplex-ai/muggle",
|
|
4
|
-
"version": "4.0
|
|
4
|
+
"version": "4.1.0",
|
|
5
5
|
"description": "Ship quality products with AI-powered QA that validates your app's user experience — from Claude Code and Cursor to PR.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"description": "Run real-browser QA tests on your web app from any AI coding agent. Generate test scripts from plain English, replay them on localhost, capture screenshots, and validate user flows like signup, checkout, and dashboards. Works across Claude Code, Cursor, Codex, and Windsurf.",
|
|
4
|
-
"version": "4.0
|
|
4
|
+
"version": "4.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"displayName": "Muggle AI",
|
|
4
4
|
"description": "Ship quality products with AI-powered QA that validates your app's user experience — from Claude Code and Cursor to PR.",
|
|
5
|
-
"version": "4.0
|
|
5
|
+
"version": "4.1.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|