@debugg-ai/debugg-ai-mcp 4.3.0 → 4.4.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/dist/handlers/probePageHandler.js +14 -7
- package/dist/handlers/runTestSuiteHandler.js +6 -5
- package/dist/handlers/testPageChangesHandler.js +8 -6
- package/dist/handlers/triggerCrawlHandler.js +7 -6
- package/dist/services/ngrok/tunnelManager.js +157 -161
- package/dist/services/tunnel/debuggTransport.js +439 -0
- package/dist/services/tunnel/ngrokTransport.js +165 -0
- package/dist/services/tunnel/probeRegistry.js +52 -0
- package/dist/services/tunnel/protocol/codec.js +327 -0
- package/dist/services/tunnel/protocol/conformance.js +171 -0
- package/dist/services/tunnel/protocol/constants.js +178 -0
- package/dist/services/tunnel/protocol/errors.js +51 -0
- package/dist/services/tunnel/protocol/handshake.js +134 -0
- package/dist/services/tunnel/protocol/index.js +17 -0
- package/dist/services/tunnel/protocol/session.js +668 -0
- package/dist/services/tunnel/protocol/stream.js +109 -0
- package/dist/services/tunnel/protocol/transport.js +106 -0
- package/dist/services/tunnel/transport.js +15 -0
- package/dist/services/tunnels.js +71 -3
- package/dist/utils/localReachability.js +160 -3
- package/dist/utils/tunnelContext.js +6 -3
- package/dist/utils/tunnelDisposition.js +16 -1
- package/dist/utils/tunnelDomains.js +113 -0
- package/dist/utils/urlParser.js +8 -2
- package/package.json +3 -1
|
@@ -104,7 +104,10 @@ export async function probePageHandler(input, context, rawProgressCallback) {
|
|
|
104
104
|
// Per-target tunnel contexts. Index aligns with input.targets[].
|
|
105
105
|
const targetContexts = [];
|
|
106
106
|
// Tunnel keys we provisioned this call (for cleanup if creation fails after key acquired).
|
|
107
|
-
|
|
107
|
+
// The provisions this call acquired, in target order. Provisions rather than
|
|
108
|
+
// bare key ids because revoking follows the transport the backend picked
|
|
109
|
+
// (ngrok keys and debugg tunnels have different endpoints).
|
|
110
|
+
const acquiredProvisions = [];
|
|
108
111
|
// Progress budget: 1 pre-flight + 1 template + 1 execute + N per-target captures + 1 done
|
|
109
112
|
const TOTAL_STEPS = 3 + input.targets.length + 1;
|
|
110
113
|
let progressStep = 0;
|
|
@@ -157,10 +160,10 @@ export async function probePageHandler(input, context, rawProgressCallback) {
|
|
|
157
160
|
throw new Error(`Failed to provision tunnel for ${ctx.originalUrl}. ` +
|
|
158
161
|
`(Detail: ${msg})${diag}`);
|
|
159
162
|
}
|
|
160
|
-
|
|
163
|
+
acquiredProvisions.push(tunnel);
|
|
161
164
|
let tunneled;
|
|
162
165
|
try {
|
|
163
|
-
tunneled = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.
|
|
166
|
+
tunneled = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.tunnels.revoke(tunnel), tunnel);
|
|
164
167
|
}
|
|
165
168
|
catch (tunnelError) {
|
|
166
169
|
const msg = tunnelError instanceof Error ? tunnelError.message : String(tunnelError);
|
|
@@ -463,11 +466,15 @@ export async function probePageHandler(input, context, rawProgressCallback) {
|
|
|
463
466
|
// Tunnels intentionally NOT torn down — reuse pattern (bead vwd) +
|
|
464
467
|
// 55-min idle auto-shutoff. Revoke only orphaned keys (we acquired the
|
|
465
468
|
// key but tunnel creation failed before ensureTunnel completed).
|
|
466
|
-
|
|
467
|
-
|
|
469
|
+
// NOTE (bead debugg_ai_mcp-xkoh.9): `tc` is undefined when ensureTunnel
|
|
470
|
+
// threw, so this condition does not fire on the very path it describes.
|
|
471
|
+
// That is a pre-existing defect with its own bead and is deliberately NOT
|
|
472
|
+
// fixed here; only the endpoint the revoke goes to changed.
|
|
473
|
+
for (let i = 0; i < acquiredProvisions.length; i++) {
|
|
474
|
+
const provision = acquiredProvisions[i];
|
|
468
475
|
const tc = targetContexts[i];
|
|
469
|
-
if (tc && !tc.tunnelId &&
|
|
470
|
-
client.
|
|
476
|
+
if (tc && !tc.tunnelId && provision) {
|
|
477
|
+
client.tunnels.revoke(provision).catch(err => logger.warn(`Failed to revoke unused tunnel ${provision.tunnelId}: ${err}`));
|
|
471
478
|
}
|
|
472
479
|
}
|
|
473
480
|
}
|
|
@@ -18,7 +18,7 @@ export async function runTestSuiteHandler(input, _context) {
|
|
|
18
18
|
logger.toolStart('run_test_suite', input);
|
|
19
19
|
const client = new DebuggAIServerClient(config.api.key);
|
|
20
20
|
await client.init();
|
|
21
|
-
let
|
|
21
|
+
let acquiredProvision = null;
|
|
22
22
|
let tunnelId;
|
|
23
23
|
// Used ONLY to scope the defensive sanitizeResponseUrls call below (bead
|
|
24
24
|
// debugg_ai_mcp-6cfv.7) — never fed into acquirePortRoute/PortLock. This
|
|
@@ -79,10 +79,10 @@ export async function runTestSuiteHandler(input, _context) {
|
|
|
79
79
|
const diag = provisionError instanceof TunnelProvisionError ? ` ${provisionError.diagnosticSuffix()}` : '';
|
|
80
80
|
return errorResp('TunnelProvisionFailed', `Failed to provision tunnel for ${input.targetUrl}. (Detail: ${msg})${diag}`);
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
acquiredProvision = tunnel;
|
|
83
83
|
let dedicated;
|
|
84
84
|
try {
|
|
85
|
-
dedicated = await tunnelManager.acquireDedicatedTunnel(ctx.originalUrl, tunnel.tunnelKey, tunnel.keyId, () => client.
|
|
85
|
+
dedicated = await tunnelManager.acquireDedicatedTunnel(ctx.originalUrl, tunnel.tunnelKey, tunnel.keyId, () => client.tunnels.revoke(tunnel), tunnel);
|
|
86
86
|
}
|
|
87
87
|
catch (tunnelError) {
|
|
88
88
|
const msg = tunnelError instanceof Error ? tunnelError.message : String(tunnelError);
|
|
@@ -142,8 +142,9 @@ export async function runTestSuiteHandler(input, _context) {
|
|
|
142
142
|
finally {
|
|
143
143
|
// Tunnels are NOT torn down — reuse pattern + 55-min idle auto-shutoff.
|
|
144
144
|
// Only revoke an orphaned key (acquired but tunnel creation failed).
|
|
145
|
-
if (
|
|
146
|
-
|
|
145
|
+
if (acquiredProvision && !tunnelId) {
|
|
146
|
+
const orphan = acquiredProvision;
|
|
147
|
+
client.tunnels.revoke(orphan).catch((err) => logger.warn(`Failed to revoke unused tunnel ${orphan.tunnelId}: ${err}`));
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
}
|
|
@@ -135,7 +135,7 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
|
|
|
135
135
|
await client.init();
|
|
136
136
|
const originalUrl = resolveTargetUrl(input);
|
|
137
137
|
let ctx = buildContext(originalUrl);
|
|
138
|
-
let
|
|
138
|
+
let provision;
|
|
139
139
|
// Cancellation is driven by the MCP request/transport lifecycle, not
|
|
140
140
|
// process.stdin. The SDK aborts context.signal when the client cancels the
|
|
141
141
|
// call OR the transport closes — e.g. an HTTP client drops the connection.
|
|
@@ -249,7 +249,7 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
|
|
|
249
249
|
`Make sure your dev server is running on the specified port and try again. ` +
|
|
250
250
|
`(Detail: ${msg})${diag}`);
|
|
251
251
|
}
|
|
252
|
-
|
|
252
|
+
provision = tunnel;
|
|
253
253
|
try {
|
|
254
254
|
ctx = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.revokeNgrokKey(tunnel.keyId));
|
|
255
255
|
}
|
|
@@ -309,7 +309,7 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
|
|
|
309
309
|
// owned path already revokes it; if we kept the tunnel, the key is that
|
|
310
310
|
// live tunnel's own credential and revoking it would kill what we just
|
|
311
311
|
// decided to preserve.
|
|
312
|
-
|
|
312
|
+
provision = undefined;
|
|
313
313
|
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }], isError: true };
|
|
314
314
|
}
|
|
315
315
|
}
|
|
@@ -993,9 +993,11 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
|
|
|
993
993
|
// subsequent calls to the same port and auto-shutoffs after 55 min idle.
|
|
994
994
|
// Process-exit cleanup happens via stopAllTunnels() in the SIGINT/SIGTERM
|
|
995
995
|
// handlers in index.ts.
|
|
996
|
-
if (!ctx.tunnelId &&
|
|
997
|
-
// Provisioned a
|
|
998
|
-
|
|
996
|
+
if (!ctx.tunnelId && provision) {
|
|
997
|
+
// Provisioned a tunnel but creation failed — revoke it, through whichever
|
|
998
|
+
// endpoint its transport uses.
|
|
999
|
+
const orphan = provision;
|
|
1000
|
+
client.tunnels.revoke(orphan).catch(err => logger.warn(`Failed to revoke unused tunnel ${orphan.tunnelId}: ${err}`));
|
|
999
1001
|
}
|
|
1000
1002
|
}
|
|
1001
1003
|
}
|
|
@@ -60,7 +60,7 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
|
|
|
60
60
|
await client.init();
|
|
61
61
|
const originalUrl = resolveTargetUrl(input);
|
|
62
62
|
let ctx = buildContext(originalUrl);
|
|
63
|
-
let
|
|
63
|
+
let provision;
|
|
64
64
|
// Bead 56kd.7: cancellation is driven by the MCP request/transport lifecycle
|
|
65
65
|
// (context.signal), NOT process.stdin. The SDK aborts context.signal when the
|
|
66
66
|
// client cancels the call OR the transport closes — e.g. an HTTP client drops
|
|
@@ -123,8 +123,8 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
|
|
|
123
123
|
`The remote browser needs a secure tunnel to reach your local dev server. ` +
|
|
124
124
|
`(Detail: ${msg})${diag}`);
|
|
125
125
|
}
|
|
126
|
-
|
|
127
|
-
ctx = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.
|
|
126
|
+
provision = tunnel;
|
|
127
|
+
ctx = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.tunnels.revoke(tunnel), tunnel);
|
|
128
128
|
}
|
|
129
129
|
// §2.4: acquire this session's shared Caddy route for our port BEFORE
|
|
130
130
|
// probing/dispatching — trigger_crawl polls (pollExecution below) for
|
|
@@ -169,7 +169,7 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
|
|
|
169
169
|
// owned path already revokes it; if we kept the tunnel, the key is that
|
|
170
170
|
// live tunnel's own credential and revoking it would kill what we just
|
|
171
171
|
// decided to preserve.
|
|
172
|
-
|
|
172
|
+
provision = undefined;
|
|
173
173
|
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }], isError: true };
|
|
174
174
|
}
|
|
175
175
|
}
|
|
@@ -363,8 +363,9 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
|
|
|
363
363
|
releasePortRoute(ctx);
|
|
364
364
|
// Tunnel intentionally NOT torn down (reuse path per bead vwd).
|
|
365
365
|
// If tunnel creation failed after key provision, revoke the orphaned key.
|
|
366
|
-
if (!ctx.tunnelId &&
|
|
367
|
-
|
|
366
|
+
if (!ctx.tunnelId && provision) {
|
|
367
|
+
const orphan = provision;
|
|
368
|
+
client.tunnels.revoke(orphan).catch(err => logger.warn(`Failed to revoke unused tunnel ${orphan.tunnelId}: ${err}`));
|
|
368
369
|
}
|
|
369
370
|
}
|
|
370
371
|
}
|