@ait-co/devtools 0.1.85 → 0.1.86

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/mcp/cli.js CHANGED
@@ -3761,7 +3761,7 @@ const DEBUG_TOOL_DEFINITIONS = [
3761
3761
  },
3762
3762
  {
3763
3763
  name: "build_attach_url",
3764
- description: "The tool result already shows the QR to the user directly (Claude Code renders MCP tool output to the user's screen; they press Ctrl+O to expand if it's collapsed). Do NOT re-print or re-render the QR in your reply — that just wastes output tokens. Simply tell the user to scan the QR shown in this tool's output with their phone camera. Builds a self-attaching deep-link for the active relay environment and returns a QR code. Scan the QR with the phone camera to open the mini-app and attach it to this debug session (QR is the single entry path — no USB cable or platform CLI needed). Call list_pages first to confirm the relay/tunnel is up. If the tunnel is not up, restart: `npx @ait-co/devtools devtools-mcp`.\n\nEnvironment-specific behaviour:\n • env 3 / relay-staging (start_debug mode=\"relay-staging\"): requires scheme_url — the intoss-private://…?_deploymentId=<uuid> URL from `ait deploy --scheme-only`. Splices debug=1 + relay URL into the scheme URL to produce a self-attach deep-link.\n • env 2 / relay-sandbox (start_debug mode=\"relay-sandbox\"): scheme_url is NOT used. Instead, reads AIT_TUNNEL_BASE_URL (the https://*.trycloudflare.com app tunnel from `tunnel:{cdp:true}`) and builds a launcher PWA deep-link (https://devtools.aitc.dev/launcher/?url=…&debug=1&relay=…). When projectRoot is given, the app name from <projectRoot>/package.json is automatically added as name= so the launcher partner bar shows it. Scan the QR with the phone to open the launcher, which frames the tunnel URL and attaches CDP.\n\nSet wait_for_attach=true to block until a page attaches (polls up to 30 s). On timeout, call build_attach_url again to resume polling. The server automatically opens the QR dashboard in the OS default browser when running on a local GUI machine — headless/remote environments fall back to the text QR in the tool output.\n\nTOTP auth: when AIT_DEBUG_TOTP_SECRET is set on the MCP server, the returned attachUrl automatically includes the current one-time code (at=<code>). The code is valid for ~3 minutes (the relay gate accepts ±6 TOTP steps = 180–210 s of backwards acceptance). The response includes a `totp` field with `expiresAt` (ISO timestamp, ~3 min from issuance). If the phone scan happens after expiresAt, the relay will reject the code — just call build_attach_url again to get a fresh URL. Without AIT_DEBUG_TOTP_SECRET, the attachUrl has no expiry.\n\nselfdebug (env 2 / relay-sandbox only): pass selfdebug=true to add &selfdebug=1 to the launcher deep-link. The launcher PWA then registers its own document as the CDP target instead of the framed mini-app. SINGLE-ATTACH MODEL: attaching the launcher self-target evicts any currently-attached mini-app target — use this mode exclusively for diagnosing the launcher document itself (DOM, safe-area, console). Not applicable in env 3/4 (relay-staging/relay-live) — passing selfdebug=true there returns an error.",
3764
+ description: "The tool result already shows the QR to the user directly (Claude Code renders MCP tool output to the user's screen; they press Ctrl+O to expand if it's collapsed). Do NOT re-print or re-render the QR in your reply — that just wastes output tokens. Simply tell the user to scan the QR shown in this tool's output with their phone camera. Builds a self-attaching deep-link for the active relay environment and returns a QR code. Scan the QR with the phone camera to open the mini-app and attach it to this debug session (QR is the single entry path — no USB cable or platform CLI needed). Call list_pages first to confirm the relay/tunnel is up. If the tunnel is not up, restart: `npx @ait-co/devtools devtools-mcp`.\n\nEnvironment-specific behaviour:\n • env 3 / relay-staging (start_debug mode=\"relay-staging\"): requires scheme_url — the intoss-private://…?_deploymentId=<uuid> URL from `ait deploy --scheme-only`. Splices debug=1 + relay URL into the scheme URL to produce a self-attach deep-link.\n • env 2 / relay-sandbox (start_debug mode=\"relay-sandbox\"): scheme_url is NOT used. Instead, reads AIT_TUNNEL_BASE_URL (the https://*.trycloudflare.com app tunnel from `tunnel:{cdp:true}`) and builds a launcher PWA deep-link (https://devtools.aitc.dev/launcher/?url=…&debug=1&relay=…). When projectRoot is given, the app name from <projectRoot>/package.json is automatically added as name= so the launcher partner bar shows it. Scan the QR with the phone to open the launcher, which frames the tunnel URL and attaches CDP.\n\nSet wait_for_attach=true to block until a page attaches (default 60 s, adjustable via wait_timeout_seconds). On timeout, call build_attach_url again to resume polling. The server automatically opens the QR dashboard in the OS default browser when running on a local GUI machine — headless/remote environments fall back to the text QR in the tool output.\n\nTOTP auth: when AIT_DEBUG_TOTP_SECRET is set on the MCP server, the returned attachUrl automatically includes the current one-time code (at=<code>). The code is valid for ~3 minutes (the relay gate accepts ±6 TOTP steps = 180–210 s of backwards acceptance). The response includes a `totp` field with `expiresAt` (ISO timestamp, ~3 min from issuance). If the phone scan happens after expiresAt, the relay will reject the code — just call build_attach_url again to get a fresh URL. Without AIT_DEBUG_TOTP_SECRET, the attachUrl has no expiry.\n\nselfdebug (env 2 / relay-sandbox only): pass selfdebug=true to add &selfdebug=1 to the launcher deep-link. The launcher PWA then registers its own document as the CDP target instead of the framed mini-app. SINGLE-ATTACH MODEL: attaching the launcher self-target evicts any currently-attached mini-app target — use this mode exclusively for diagnosing the launcher document itself (DOM, safe-area, console). Not applicable in env 3/4 (relay-staging/relay-live) — passing selfdebug=true there returns an error.",
3765
3765
  inputSchema: {
3766
3766
  type: "object",
3767
3767
  properties: {
@@ -3771,7 +3771,11 @@ const DEBUG_TOOL_DEFINITIONS = [
3771
3771
  },
3772
3772
  wait_for_attach: {
3773
3773
  type: "boolean",
3774
- description: "If true, block after returning the QR until a page attaches to the relay (polls listTargets ~1 s interval, timeout 30 s). On attach, the response includes the attached page list. On timeout, call build_attach_url again to resume polling."
3774
+ description: "If true, block after returning the QR until a page attaches to the relay (polls listTargets ~1 s interval, default 60 s). On attach, the response includes the attached page list. On timeout, call build_attach_url again to resume polling."
3775
+ },
3776
+ wait_timeout_seconds: {
3777
+ type: "number",
3778
+ description: "Maximum seconds to wait when wait_for_attach=true (default 60, range 1–600). Values outside the range or invalid inputs (0, negative, NaN) fall back to the default silently. Only meaningful when wait_for_attach=true."
3775
3779
  },
3776
3780
  projectRoot: {
3777
3781
  type: "string",
@@ -4775,7 +4779,7 @@ async function readMcpSdkVersion() {
4775
4779
  * some test environments that skip the build step).
4776
4780
  */
4777
4781
  function readDevtoolsVersion() {
4778
- return "0.1.85";
4782
+ return "0.1.86";
4779
4783
  }
4780
4784
  /**
4781
4785
  * Derives the next recommended action from a completed diagnostics snapshot.
@@ -4851,9 +4855,14 @@ async function getDiagnostics(input) {
4851
4855
  reissueAttempts: tunnel.reissueAttempts ?? 0
4852
4856
  };
4853
4857
  let pages = null;
4854
- if (connection !== void 0) try {
4855
- pages = listPages(connection, tunnel);
4856
- } catch {}
4858
+ if (connection !== void 0) {
4859
+ try {
4860
+ await connection.refreshTargets?.();
4861
+ } catch {}
4862
+ try {
4863
+ pages = listPages(connection, tunnel);
4864
+ } catch {}
4865
+ }
4857
4866
  const limit = Math.min(Math.max(1, recentErrorsLimit), 50);
4858
4867
  const recentErrors = collector.getRecentErrors(limit);
4859
4868
  const authRejects = collector.getAuthRejects();
@@ -5271,7 +5280,7 @@ function waitForAttachWithEvents(connection, filterFn, timeoutMs, pollIntervalMs
5271
5280
  * naturally via `enableDomains`). The tier only controls visibility.
5272
5281
  */
5273
5282
  function createDebugServer(deps) {
5274
- const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs = 9e4, qrHttpServer, getEnvironment: getEnvDep, getEnvironmentReason: getEnvReasonDep, diagnosticsCollector: collectorDep, totpSecret, onAttachUrlBuilt } = deps;
5283
+ const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs = 6e4, qrHttpServer, getEnvironment: getEnvDep, getEnvironmentReason: getEnvReasonDep, diagnosticsCollector: collectorDep, totpSecret, onAttachUrlBuilt } = deps;
5275
5284
  const getTotpSecret = deps.getTotpSecret ?? (() => totpSecret);
5276
5285
  const router = routerDep ?? makeSingleConnectionRouter(connection);
5277
5286
  const resolveEnvironment = getEnvDep ?? (() => deriveEnvironment(router.active.kind, getLiveIntent(), router.activeRelayOrigin));
@@ -5279,7 +5288,7 @@ function createDebugServer(deps) {
5279
5288
  const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
5280
5289
  const server = new Server({
5281
5290
  name: "ait-debug",
5282
- version: "0.1.85"
5291
+ version: "0.1.86"
5283
5292
  }, { capabilities: { tools: { listChanged: true } } });
5284
5293
  server.setRequestHandler(ListToolsRequestSchema, () => {
5285
5294
  const conn = router.active;
@@ -5353,6 +5362,13 @@ function createDebugServer(deps) {
5353
5362
  if (name === "build_attach_url") {
5354
5363
  const waitForAttach = request.params.arguments?.wait_for_attach === true;
5355
5364
  const selfdebug = request.params.arguments?.selfdebug === true;
5365
+ const rawWaitTimeout = request.params.arguments?.wait_timeout_seconds;
5366
+ const callTimeoutMs = (() => {
5367
+ if (typeof rawWaitTimeout !== "number" || !Number.isFinite(rawWaitTimeout)) return waitForAttachTimeoutMs;
5368
+ const clamped = Math.max(1, Math.min(600, rawWaitTimeout));
5369
+ if (rawWaitTimeout <= 0) return waitForAttachTimeoutMs;
5370
+ return Math.round(clamped) * 1e3;
5371
+ })();
5356
5372
  if (selfdebug && env !== "relay-mobile") return mcpError("build_attach_url: selfdebug=true는 env 2 / relay-sandbox 전용 기능입니다. 현재 환경(env 3/4)에서는 launcher가 없어 self-target 모드를 지원하지 않습니다. launcher self-target이 필요하다면 relay-sandbox 모드로 재시작하세요.");
5357
5373
  if (env === "relay-mobile") {
5358
5374
  const rawBuildProjectRoot = request.params.arguments?.projectRoot;
@@ -5423,13 +5439,13 @@ function createDebugServer(deps) {
5423
5439
  }] };
5424
5440
  let attachedPagesHl = [];
5425
5441
  try {
5426
- attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5442
+ attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5427
5443
  } catch {
5428
5444
  attachedPagesHl = conn.listTargets();
5429
5445
  return {
5430
5446
  content: [{
5431
5447
  type: "text",
5432
- text: buildTimeoutError(headlessText, waitForAttachTimeoutMs / 1e3, attachedPagesHl)
5448
+ text: buildTimeoutError(headlessText, callTimeoutMs / 1e3, attachedPagesHl)
5433
5449
  }],
5434
5450
  isError: true
5435
5451
  };
@@ -5460,13 +5476,13 @@ function createDebugServer(deps) {
5460
5476
  }] };
5461
5477
  let attachedPages = [];
5462
5478
  try {
5463
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5479
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5464
5480
  } catch {
5465
5481
  attachedPages = conn.listTargets();
5466
5482
  return {
5467
5483
  content: [{
5468
5484
  type: "text",
5469
- text: buildTimeoutError(shortText, waitForAttachTimeoutMs / 1e3, attachedPages)
5485
+ text: buildTimeoutError(shortText, callTimeoutMs / 1e3, attachedPages)
5470
5486
  }],
5471
5487
  isError: true
5472
5488
  };
@@ -5499,13 +5515,13 @@ function createDebugServer(deps) {
5499
5515
  }] };
5500
5516
  let attachedPagesFb = [];
5501
5517
  try {
5502
- attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5518
+ attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5503
5519
  } catch {
5504
5520
  attachedPagesFb = conn.listTargets();
5505
5521
  return {
5506
5522
  content: [{
5507
5523
  type: "text",
5508
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPagesFb)
5524
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPagesFb)
5509
5525
  }],
5510
5526
  isError: true
5511
5527
  };
@@ -5528,13 +5544,13 @@ function createDebugServer(deps) {
5528
5544
  }] };
5529
5545
  let attachedPages = [];
5530
5546
  try {
5531
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5547
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5532
5548
  } catch {
5533
5549
  attachedPages = conn.listTargets();
5534
5550
  return {
5535
5551
  content: [{
5536
5552
  type: "text",
5537
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPages)
5553
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPages)
5538
5554
  }],
5539
5555
  isError: true
5540
5556
  };
@@ -5594,13 +5610,13 @@ function createDebugServer(deps) {
5594
5610
  }] };
5595
5611
  let attachedPagesHl = [];
5596
5612
  try {
5597
- attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5613
+ attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5598
5614
  } catch {
5599
5615
  attachedPagesHl = conn.listTargets();
5600
5616
  return {
5601
5617
  content: [{
5602
5618
  type: "text",
5603
- text: buildTimeoutError(headlessText, waitForAttachTimeoutMs / 1e3, attachedPagesHl)
5619
+ text: buildTimeoutError(headlessText, callTimeoutMs / 1e3, attachedPagesHl)
5604
5620
  }],
5605
5621
  isError: true
5606
5622
  };
@@ -5631,13 +5647,13 @@ function createDebugServer(deps) {
5631
5647
  }] };
5632
5648
  let attachedPages = [];
5633
5649
  try {
5634
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5650
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5635
5651
  } catch {
5636
5652
  attachedPages = conn.listTargets();
5637
5653
  return {
5638
5654
  content: [{
5639
5655
  type: "text",
5640
- text: buildTimeoutError(shortText, waitForAttachTimeoutMs / 1e3, attachedPages)
5656
+ text: buildTimeoutError(shortText, callTimeoutMs / 1e3, attachedPages)
5641
5657
  }],
5642
5658
  isError: true
5643
5659
  };
@@ -5671,13 +5687,13 @@ ${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stder
5671
5687
  }] };
5672
5688
  let attachedPagesFb = [];
5673
5689
  try {
5674
- attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5690
+ attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5675
5691
  } catch {
5676
5692
  attachedPagesFb = conn.listTargets();
5677
5693
  return {
5678
5694
  content: [{
5679
5695
  type: "text",
5680
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPagesFb)
5696
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPagesFb)
5681
5697
  }],
5682
5698
  isError: true
5683
5699
  };
@@ -5700,13 +5716,13 @@ ${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stder
5700
5716
  }] };
5701
5717
  let attachedPages = [];
5702
5718
  try {
5703
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5719
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5704
5720
  } catch {
5705
5721
  attachedPages = conn.listTargets();
5706
5722
  return {
5707
5723
  content: [{
5708
5724
  type: "text",
5709
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPages)
5725
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPages)
5710
5726
  }],
5711
5727
  isError: true
5712
5728
  };
@@ -7111,7 +7127,11 @@ const DEV_TOOL_DEFINITIONS = [
7111
7127
  },
7112
7128
  wait_for_attach: {
7113
7129
  type: "boolean",
7114
- description: "If true, block until a page attaches."
7130
+ description: "If true, block until a page attaches (default 60 s)."
7131
+ },
7132
+ wait_timeout_seconds: {
7133
+ type: "number",
7134
+ description: "Maximum seconds to wait when wait_for_attach=true (default 60, range 1–600). Invalid inputs fall back to default."
7115
7135
  }
7116
7136
  },
7117
7137
  required: ["scheme_url"]
@@ -7322,7 +7342,7 @@ function createDevServer(deps = {}) {
7322
7342
  const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
7323
7343
  const server = new Server({
7324
7344
  name: "ait-devtools",
7325
- version: "0.1.85"
7345
+ version: "0.1.86"
7326
7346
  }, { capabilities: { tools: {} } });
7327
7347
  server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
7328
7348
  server.setRequestHandler(CallToolRequestSchema, async (request) => {