@debugg-ai/debugg-ai-mcp 1.0.42 → 1.0.44

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.
@@ -9,6 +9,7 @@ import { handleExternalServiceError } from '../utils/errors.js';
9
9
  import { fetchImageAsBase64, imageContentBlock } from '../utils/imageUtils.js';
10
10
  import { DebuggAIServerClient } from '../services/index.js';
11
11
  import { resolveTargetUrl, buildContext, findExistingTunnel, ensureTunnel, sanitizeResponseUrls, touchTunnelById, } from '../utils/tunnelContext.js';
12
+ import { detectRepoName } from '../utils/gitContext.js';
12
13
  import { tunnelManager } from '../services/ngrok/tunnelManager.js';
13
14
  const logger = new Logger({ module: 'testPageChangesHandler' });
14
15
  // Cache the template UUID and project UUIDs within a server session to avoid re-fetching
@@ -80,23 +81,25 @@ export async function testPageChangesHandler(input, context, progressCallback) {
80
81
  logger.info(`Using workflow template: ${template.name} (${template.uuid})`);
81
82
  }
82
83
  // --- Resolve project UUID (best-effort, non-blocking) ---
84
+ // Use explicit repoName if provided, otherwise auto-detect from git remote
85
+ const repoName = input.repoName || detectRepoName();
83
86
  let projectUuid;
84
- if (input.repoName) {
85
- projectUuid = projectUuidCache.get(input.repoName);
87
+ if (repoName) {
88
+ projectUuid = projectUuidCache.get(repoName);
86
89
  if (!projectUuid) {
87
90
  try {
88
- const project = await client.findProjectByRepoName(input.repoName);
91
+ const project = await client.findProjectByRepoName(repoName);
89
92
  if (project) {
90
93
  projectUuid = project.uuid;
91
- projectUuidCache.set(input.repoName, projectUuid);
94
+ projectUuidCache.set(repoName, projectUuid);
92
95
  logger.info(`Resolved project: ${project.name} (${project.uuid})`);
93
96
  }
94
97
  else {
95
- logger.info(`No project found for repo "${input.repoName}" — proceeding without project_id`);
98
+ logger.info(`No project found for repo "${repoName}" — proceeding without project_id`);
96
99
  }
97
100
  }
98
101
  catch (err) {
99
- logger.warn(`Failed to look up project for repo "${input.repoName}": ${err}`);
102
+ logger.warn(`Failed to look up project for repo "${repoName}": ${err}`);
100
103
  }
101
104
  }
102
105
  }
@@ -197,14 +200,6 @@ export async function testPageChangesHandler(input, context, progressCallback) {
197
200
  }
198
201
  }, abortController.signal);
199
202
  const duration = Date.now() - startTime;
200
- // If the execution failed because the tunnel URL was unreachable, evict the dead tunnel
201
- // so the next call re-provisions a fresh one instead of reusing a dead entry.
202
- const tunnelErrorMsg = finalExecution.errorMessage ?? finalExecution.state?.error ?? '';
203
- if (ctx.tunnelId && tunnelErrorMsg.includes('unreachable') && tunnelErrorMsg.includes('ngrok')) {
204
- logger.warn(`Tunnel ${ctx.tunnelId} appears dead (unreachable) — evicting from cache`);
205
- tunnelManager.stopTunnel(ctx.tunnelId).catch(() => { });
206
- ctx = { ...ctx, tunnelId: undefined };
207
- }
208
203
  // --- Format result ---
209
204
  const outcome = finalExecution.state?.outcome ?? finalExecution.status;
210
205
  const nodes = finalExecution.nodeExecutions ?? [];
@@ -352,12 +347,12 @@ export async function testPageChangesHandler(input, context, progressCallback) {
352
347
  }
353
348
  finally {
354
349
  process.stdin.removeListener('close', onStdinClose);
355
- // Tunnels stay alive for reuse the 55-min auto-shutoff on TunnelManager
356
- // fires revokeKey when the tunnel actually stops.
357
- //
358
- // Only revoke explicitly when we provisioned a key but tunnel creation failed
359
- // (keyId set, ctx.tunnelId not set → key was never attached to a tunnel).
360
- if (keyId && !ctx.tunnelId) {
350
+ // Always tear down the tunnel when the request completes.
351
+ if (ctx.tunnelId) {
352
+ tunnelManager.stopTunnel(ctx.tunnelId).catch(err => logger.warn(`Failed to stop tunnel ${ctx.tunnelId}: ${err}`));
353
+ }
354
+ else if (keyId) {
355
+ // Provisioned a key but tunnel creation failed — revoke the orphaned key.
361
356
  client.revokeNgrokKey(keyId).catch(err => logger.warn(`Failed to revoke unused ngrok key ${keyId}: ${err}`));
362
357
  }
363
358
  }
@@ -45,7 +45,7 @@ export const testPageChangesTool = {
45
45
  },
46
46
  repoName: {
47
47
  type: "string",
48
- description: "GitHub repository name (e.g. 'my-org/my-repo' or 'my-repo'). Used to link this test to a DebuggAI project for tracking and history."
48
+ description: "GitHub repository name (e.g. 'my-org/my-repo'). Auto-detected from the current git repo only provide this if you want to run against a different project than the one you're in."
49
49
  },
50
50
  },
51
51
  required: ["description", "url"],
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Auto-detect git repo name from the current working directory.
3
+ * Parses the origin remote URL into "owner/repo" format.
4
+ */
5
+ import { execSync } from 'child_process';
6
+ let cached; // undefined = not yet checked
7
+ /**
8
+ * Detect the repo name (e.g. "debugg-ai/debugg-ai-frontend") from git remote origin.
9
+ * Returns null if not inside a git repo or no origin is configured.
10
+ * Result is cached for the process lifetime.
11
+ */
12
+ export function detectRepoName() {
13
+ if (cached !== undefined)
14
+ return cached;
15
+ try {
16
+ const raw = execSync('git remote get-url origin', {
17
+ encoding: 'utf-8',
18
+ timeout: 5000,
19
+ stdio: ['ignore', 'pipe', 'ignore'],
20
+ }).trim();
21
+ cached = parseRepoName(raw);
22
+ }
23
+ catch {
24
+ cached = null;
25
+ }
26
+ return cached;
27
+ }
28
+ /**
29
+ * Parse an origin URL into "owner/repo" format.
30
+ * Handles SSH (git@github.com:owner/repo.git) and HTTPS (https://github.com/owner/repo.git).
31
+ */
32
+ function parseRepoName(remoteUrl) {
33
+ // SSH: git@github.com:owner/repo.git
34
+ const sshMatch = remoteUrl.match(/[:\/]([^/]+\/[^/]+?)(?:\.git)?$/);
35
+ if (sshMatch)
36
+ return sshMatch[1];
37
+ return null;
38
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugg-ai/debugg-ai-mcp",
3
- "version": "1.0.42",
3
+ "version": "1.0.44",
4
4
  "description": "Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.",
5
5
  "type": "module",
6
6
  "bin": {