@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 CHANGED
@@ -375,8 +375,8 @@ Data directory structure (~/.muggle-ai/)
375
375
 
376
376
  ```
377
377
  ~/.muggle-ai/
378
- ├── auth.json # OAuth tokens
379
- ├── credentials.json # API key for service calls
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/auth.json ~/.muggle-ai/credentials.json
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 CREDENTIALS_FILE = "credentials.json";
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 === "dev") {
243
- return DEFAULT_AUTH0_DEV_CLIENT_ID;
242
+ if (runtimeTarget === "production") {
243
+ return DEFAULT_AUTH0_PRODUCTION_CLIENT_ID;
244
244
  }
245
- return DEFAULT_AUTH0_PRODUCTION_CLIENT_ID;
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
- credentialsFilePath: path2.join(dataDir, CREDENTIALS_FILE),
288
- authFilePath: path2.join(dataDir, "auth.json"),
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 auth file. */
614
- authFilePath;
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.authFilePath = config.localQa.authFilePath;
622
+ this.oauthSessionFilePath = config.localQa.oauthSessionFilePath;
623
623
  this.pendingDeviceCodePath = path2.join(
624
- path2.dirname(config.localQa.authFilePath),
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.authFilePath);
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.authFilePath, JSON.stringify(storedAuth, null, 2), {
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.authFilePath)) {
949
+ if (!fs3.existsSync(this.oauthSessionFilePath)) {
950
950
  return null;
951
951
  }
952
952
  try {
953
- const content = fs3.readFileSync(this.authFilePath, "utf-8");
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.authFilePath);
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.authFilePath, JSON.stringify(updatedAuth, null, 2), {
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.authFilePath)) {
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.authFilePath);
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 CREDENTIALS_FILE2 = "credentials.json";
2384
- function getCredentialsFilePath() {
2385
- return path2.join(getDataDir(), CREDENTIALS_FILE2);
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 loadCredentials() {
2393
+ function loadApiKeyData() {
2394
2394
  const logger14 = getLogger();
2395
- const credentialsPath = getCredentialsFilePath();
2395
+ const apiKeyPath = getApiKeyFilePath();
2396
2396
  try {
2397
- if (!fs3.existsSync(credentialsPath)) {
2398
- logger14.debug("No credentials file found", { path: credentialsPath });
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(credentialsPath, "utf-8");
2402
- const credentials = JSON.parse(content);
2403
- if (!credentials.accessToken || !credentials.expiresAt) {
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 credentials", {
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 saveCredentials(credentials) {
2411
+ function saveApiKeyData(data) {
2416
2412
  const logger14 = getLogger();
2417
- const credentialsPath = getCredentialsFilePath();
2413
+ const apiKeyPath = getApiKeyFilePath();
2418
2414
  try {
2419
2415
  ensureDataDir();
2420
- const content = JSON.stringify(credentials, null, 2);
2421
- fs3.writeFileSync(credentialsPath, content, { mode: 384 });
2422
- logger14.info("Credentials saved", { path: credentialsPath });
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 credentials", {
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 deleteCredentials() {
2426
+ function deleteApiKeyData() {
2431
2427
  const logger14 = getLogger();
2432
- const credentialsPath = getCredentialsFilePath();
2428
+ const apiKeyPath = getApiKeyFilePath();
2433
2429
  try {
2434
- if (fs3.existsSync(credentialsPath)) {
2435
- fs3.unlinkSync(credentialsPath);
2436
- logger14.info("Credentials deleted", { path: credentialsPath });
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 credentials", {
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 isCredentialsExpired(credentials) {
2445
- const expiresAt = new Date(credentials.expiresAt);
2446
- const now = /* @__PURE__ */ new Date();
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 (credentials.apiKey) {
2456
- return credentials;
2445
+ if (data.apiKey) {
2446
+ return data;
2457
2447
  }
2458
2448
  return null;
2459
2449
  }
2460
2450
  function hasApiKey() {
2461
- const credentials = loadCredentials();
2462
- return !!credentials?.apiKey;
2451
+ const data = loadApiKeyData();
2452
+ return !!data?.apiKey;
2463
2453
  }
2464
2454
  function getApiKey() {
2465
- const credentials = loadCredentials();
2466
- return credentials?.apiKey ?? null;
2455
+ const data = loadApiKeyData();
2456
+ return data?.apiKey ?? null;
2467
2457
  }
2468
2458
  function saveApiKey(params) {
2469
2459
  const logger14 = getLogger();
2470
- const credentialsPath = getCredentialsFilePath();
2460
+ const apiKeyPath = getApiKeyFilePath();
2471
2461
  try {
2472
2462
  ensureDataDir();
2473
- const credentials = {
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(credentials, null, 2);
2480
- fs3.writeFileSync(credentialsPath, content, { mode: 384 });
2481
- logger14.info("API key saved", { path: credentialsPath });
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
- saveCredentials(credentials);
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
- deleteCredentials();
2729
+ deleteApiKeyData();
2735
2730
  logger4.info("[Auth] Logged out successfully");
2736
2731
  }
2737
2732
  function getCallerCredentials() {
2738
- const credentials = getValidCredentials();
2739
- if (credentials?.apiKey) {
2740
- return { apiKey: credentials.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 credentials = getValidCredentials();
2751
- if (credentials?.apiKey) {
2752
- return { apiKey: credentials.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
- ` Credentials are stored in ${path12("~/.muggle-ai/credentials.json")}`,
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("credentials.json")} Auth credentials (auto-generated)`,
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from './chunk-BQZQDOXI.js';
2
+ import { runCli } from './chunk-PV76IWEX.js';
3
3
 
4
4
  // src/cli/main.ts
5
5
  runCli().catch((error) => {
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-BQZQDOXI.js';
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.3",
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.3",
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.3",
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.3",
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.3",
5
+ "version": "4.1.0",
6
6
  "author": {
7
7
  "name": "Muggle AI",
8
8
  "email": "support@muggle-ai.com"