@debugg-ai/debugg-ai-mcp 6.2.2 → 6.2.4

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
@@ -63,6 +63,8 @@ Verified 2026-11-10: servers bound to `127.0.0.1` only (IPv4 Caddy gateways on :
63
63
 
64
64
  `probe_page` accepts `browser: "local"` to capture with Chrome on your own machine: no tunnel, no cloud browser, nothing uploaded (screenshots also saved under `~/.debugg-ai/captures`, mode 0600). stdio server only; Chrome is found automatically or via `DEBUGGAI_CHROME_PATH`; `playwright-core` is an optional dependency. Local runs are headless by default and never open a visible window; `headless: false` is opt-in and launches minimized and off-screen without taking focus. `useProfile: true` uses an MCP-owned persistent profile (`~/.debugg-ai/chrome-profile`, never your everyday Chrome profile): sign in once with `headless: false, loginWindowMs: 120000`, later local probes are logged in. Profile sessions require `allowedDomains` (or the owner's `DEBUGGAI_PROFILE_ALLOWED_DOMAINS`, which overrides it); top-level navigations off the list, redirect hops included, are blocked.
65
65
 
66
+ Local runs mark themselves as our own automation: the browser gets the cookie `analytics_ignore=1` on every origin it visits, which apps using the shared analytics traffic filter treat as "do not count". Add `?analytics=on` to the URL for a deliberate real analytics test.
67
+
66
68
  Get an API key at [debugg.ai](https://debugg.ai), then add to your MCP client config:
67
69
 
68
70
  ```json
@@ -12,6 +12,7 @@
12
12
  * DEBUGGAI_CHROME_PROFILE_DIR) the user logs into once via `loginToLocalProfile`;
13
13
  * the user's everyday Chrome profile is never touched.
14
14
  */
15
+ import { markAnalyticsIgnore } from '../utils/analyticsMarker.js';
15
16
  import fs from 'node:fs';
16
17
  import os from 'node:os';
17
18
  import path from 'node:path';
@@ -161,6 +162,7 @@ export async function localCapture(o) {
161
162
  const len = Number(r.headers()['content-length'] ?? 0);
162
163
  net.push({ url: r.url(), status: r.status(), bytes: Number.isFinite(len) ? len : 0 });
163
164
  });
165
+ await markAnalyticsIgnore(context, o.url);
164
166
  const t0 = Date.now();
165
167
  let resp;
166
168
  try {
@@ -4,6 +4,7 @@
4
4
  * do the work; this layer serialises, enforces the allowlist on every navigation hop,
5
5
  * substitutes credential placeholders locally, redacts, and reports page events.
6
6
  */
7
+ import { markAnalyticsIgnore } from '../../utils/analyticsMarker.js';
7
8
  import fs from 'node:fs';
8
9
  import path from 'node:path';
9
10
  import { launch, localBrowserEnabled, LocalBrowserError, LOCAL_UNAVAILABLE_MESSAGE, hostAllowed, resolveAllowedDomains, debuggHome, ensurePrivateDir } from '../localBrowser.js';
@@ -199,6 +200,7 @@ export class OpRunner {
199
200
  }
200
201
  if (!hostAllowed(host, this.opts.allowedDomains) && !isLoopbackHost(host))
201
202
  throw new OpError('domain_blocked', `${host} is not in this session's allowlist (${this.opts.allowedDomains.join(', ')}).`, false);
203
+ await markAnalyticsIgnore(this.context, url);
202
204
  const resp = await pg.goto(url, { waitUntil: a.wait_until ?? 'domcontentloaded', timeout });
203
205
  return { kind: 'action', value: { status: resp?.status() ?? null } };
204
206
  }
@@ -318,6 +320,7 @@ export class OpRunner {
318
320
  await pg.close();
319
321
  throw new OpError('domain_blocked', `${h} is not in this session's allowlist.`);
320
322
  }
323
+ await markAnalyticsIgnore(this.context, String(a.url));
321
324
  await pg.goto(String(a.url), { waitUntil: 'domcontentloaded', timeout });
322
325
  }
323
326
  const id = this.ids.get(pg) ?? this.register(pg);
@@ -144,6 +144,12 @@ export const createWorkflowsService = (tx) => {
144
144
  // 56kd.3).
145
145
  let lastExecution;
146
146
  let consecutivePollFailures = 0;
147
+ // Bead (dogfood 2026-11-13): the no-progress clock restarts whenever the
148
+ // run records a new workflow node. A run the backend queued for 3+ minutes
149
+ // before starting (env.resolve at +202s) was declared never-started at +245s
150
+ // and then completed with pass at +398s.
151
+ let lastProgressAt = pollStart;
152
+ let lastNodeCount = 0;
147
153
  while (Date.now() < deadline) {
148
154
  if (signal?.aborted) {
149
155
  throw new Error(`Polling cancelled for execution ${executionUuid}`);
@@ -186,7 +192,12 @@ export const createWorkflowsService = (tx) => {
186
192
  }
187
193
  const hasSteps = (execution.state?.stepsTaken ?? 0) > 0 ||
188
194
  (execution.nodeExecutions ?? []).some(n => n.nodeType === 'brain.step');
189
- if (!hasSteps && Date.now() - pollStart >= noProgressTimeoutMs()) {
195
+ const nodeCount = (execution.nodeExecutions ?? []).length;
196
+ if (nodeCount > lastNodeCount) {
197
+ lastNodeCount = nodeCount;
198
+ lastProgressAt = Date.now();
199
+ }
200
+ if (!hasSteps && Date.now() - lastProgressAt >= noProgressTimeoutMs()) {
190
201
  Telemetry.capture(TelemetryEvents.WORKFLOW_EXECUTED, {
191
202
  status: execution.status,
192
203
  success: false,
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Marks MCP-driven browsing as our own automation so the apps' analytics
3
+ * (shared `@startsimpli/analytics/traffic` heuristic) do not count it.
4
+ * The apps honour a non-HttpOnly cookie `analytics_ignore=1` on their own host
5
+ * (the same cookie `?analytics=off` sets). `?analytics=on` in the URL still
6
+ * overrides it for a deliberate real test.
7
+ */
8
+ export const ANALYTICS_IGNORE_COOKIE = 'analytics_ignore';
9
+ const ONE_YEAR_S = 365 * 24 * 60 * 60;
10
+ /** Set the marker cookie for the origin of `url`. Never throws: a cookie we could not set must not fail a run. */
11
+ export async function markAnalyticsIgnore(context, url) {
12
+ try {
13
+ const u = new URL(url);
14
+ if (u.protocol !== 'http:' && u.protocol !== 'https:')
15
+ return;
16
+ await context.addCookies([{
17
+ name: ANALYTICS_IGNORE_COOKIE,
18
+ value: '1',
19
+ url: u.origin + '/',
20
+ expires: Math.floor(Date.now() / 1000) + ONE_YEAR_S,
21
+ }]);
22
+ }
23
+ catch { /* best effort */ }
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugg-ai/debugg-ai-mcp",
3
- "version": "6.2.2",
3
+ "version": "6.2.4",
4
4
  "description": "Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.",
5
5
  "type": "module",
6
6
  "caddy": "2.11.3",