@ait-co/devtools 0.1.85 → 0.1.87

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
@@ -2188,6 +2188,7 @@ const en = {
2188
2188
  "launcher.diagYes": "yes",
2189
2189
  "launcher.diagNo": "no",
2190
2190
  "launcher.letterboxDetected": "Letterbox correction +{pt}pt applied — using full screen height.",
2191
+ "launcher.letterboxClipped": "An iOS viewport bug makes the bottom {pt}pt unusable — rotating to landscape and back to portrait may recover it.",
2191
2192
  "launcher.navbar.defaultTitle": "Mini App",
2192
2193
  "launcher.navbar.back": "Back",
2193
2194
  "launcher.navbar.menu": "Menu",
@@ -2442,6 +2443,7 @@ const tables = {
2442
2443
  "launcher.diagYes": "예",
2443
2444
  "launcher.diagNo": "아니요",
2444
2445
  "launcher.letterboxDetected": "letterbox 보정 +{pt}pt 적용됨 — 화면 전체를 사용합니다.",
2446
+ "launcher.letterboxClipped": "iOS 뷰포트 버그로 화면 아래 {pt}pt를 쓸 수 없습니다 — 기기를 가로로 돌렸다 세로로 돌리면 복구될 수 있어요.",
2445
2447
  "launcher.navbar.defaultTitle": "미니앱",
2446
2448
  "launcher.navbar.back": "뒤로가기",
2447
2449
  "launcher.navbar.menu": "메뉴",
@@ -3761,7 +3763,7 @@ const DEBUG_TOOL_DEFINITIONS = [
3761
3763
  },
3762
3764
  {
3763
3765
  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.",
3766
+ 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
3767
  inputSchema: {
3766
3768
  type: "object",
3767
3769
  properties: {
@@ -3771,7 +3773,11 @@ const DEBUG_TOOL_DEFINITIONS = [
3771
3773
  },
3772
3774
  wait_for_attach: {
3773
3775
  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."
3776
+ 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."
3777
+ },
3778
+ wait_timeout_seconds: {
3779
+ type: "number",
3780
+ 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
3781
  },
3776
3782
  projectRoot: {
3777
3783
  type: "string",
@@ -4775,7 +4781,7 @@ async function readMcpSdkVersion() {
4775
4781
  * some test environments that skip the build step).
4776
4782
  */
4777
4783
  function readDevtoolsVersion() {
4778
- return "0.1.85";
4784
+ return "0.1.87";
4779
4785
  }
4780
4786
  /**
4781
4787
  * Derives the next recommended action from a completed diagnostics snapshot.
@@ -4851,9 +4857,14 @@ async function getDiagnostics(input) {
4851
4857
  reissueAttempts: tunnel.reissueAttempts ?? 0
4852
4858
  };
4853
4859
  let pages = null;
4854
- if (connection !== void 0) try {
4855
- pages = listPages(connection, tunnel);
4856
- } catch {}
4860
+ if (connection !== void 0) {
4861
+ try {
4862
+ await connection.refreshTargets?.();
4863
+ } catch {}
4864
+ try {
4865
+ pages = listPages(connection, tunnel);
4866
+ } catch {}
4867
+ }
4857
4868
  const limit = Math.min(Math.max(1, recentErrorsLimit), 50);
4858
4869
  const recentErrors = collector.getRecentErrors(limit);
4859
4870
  const authRejects = collector.getAuthRejects();
@@ -5271,7 +5282,7 @@ function waitForAttachWithEvents(connection, filterFn, timeoutMs, pollIntervalMs
5271
5282
  * naturally via `enableDomains`). The tier only controls visibility.
5272
5283
  */
5273
5284
  function createDebugServer(deps) {
5274
- const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs = 9e4, qrHttpServer, getEnvironment: getEnvDep, getEnvironmentReason: getEnvReasonDep, diagnosticsCollector: collectorDep, totpSecret, onAttachUrlBuilt } = deps;
5285
+ const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs = 6e4, qrHttpServer, getEnvironment: getEnvDep, getEnvironmentReason: getEnvReasonDep, diagnosticsCollector: collectorDep, totpSecret, onAttachUrlBuilt } = deps;
5275
5286
  const getTotpSecret = deps.getTotpSecret ?? (() => totpSecret);
5276
5287
  const router = routerDep ?? makeSingleConnectionRouter(connection);
5277
5288
  const resolveEnvironment = getEnvDep ?? (() => deriveEnvironment(router.active.kind, getLiveIntent(), router.activeRelayOrigin));
@@ -5279,7 +5290,7 @@ function createDebugServer(deps) {
5279
5290
  const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
5280
5291
  const server = new Server({
5281
5292
  name: "ait-debug",
5282
- version: "0.1.85"
5293
+ version: "0.1.87"
5283
5294
  }, { capabilities: { tools: { listChanged: true } } });
5284
5295
  server.setRequestHandler(ListToolsRequestSchema, () => {
5285
5296
  const conn = router.active;
@@ -5353,6 +5364,13 @@ function createDebugServer(deps) {
5353
5364
  if (name === "build_attach_url") {
5354
5365
  const waitForAttach = request.params.arguments?.wait_for_attach === true;
5355
5366
  const selfdebug = request.params.arguments?.selfdebug === true;
5367
+ const rawWaitTimeout = request.params.arguments?.wait_timeout_seconds;
5368
+ const callTimeoutMs = (() => {
5369
+ if (typeof rawWaitTimeout !== "number" || !Number.isFinite(rawWaitTimeout)) return waitForAttachTimeoutMs;
5370
+ const clamped = Math.max(1, Math.min(600, rawWaitTimeout));
5371
+ if (rawWaitTimeout <= 0) return waitForAttachTimeoutMs;
5372
+ return Math.round(clamped) * 1e3;
5373
+ })();
5356
5374
  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
5375
  if (env === "relay-mobile") {
5358
5376
  const rawBuildProjectRoot = request.params.arguments?.projectRoot;
@@ -5423,13 +5441,13 @@ function createDebugServer(deps) {
5423
5441
  }] };
5424
5442
  let attachedPagesHl = [];
5425
5443
  try {
5426
- attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5444
+ attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5427
5445
  } catch {
5428
5446
  attachedPagesHl = conn.listTargets();
5429
5447
  return {
5430
5448
  content: [{
5431
5449
  type: "text",
5432
- text: buildTimeoutError(headlessText, waitForAttachTimeoutMs / 1e3, attachedPagesHl)
5450
+ text: buildTimeoutError(headlessText, callTimeoutMs / 1e3, attachedPagesHl)
5433
5451
  }],
5434
5452
  isError: true
5435
5453
  };
@@ -5460,13 +5478,13 @@ function createDebugServer(deps) {
5460
5478
  }] };
5461
5479
  let attachedPages = [];
5462
5480
  try {
5463
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5481
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5464
5482
  } catch {
5465
5483
  attachedPages = conn.listTargets();
5466
5484
  return {
5467
5485
  content: [{
5468
5486
  type: "text",
5469
- text: buildTimeoutError(shortText, waitForAttachTimeoutMs / 1e3, attachedPages)
5487
+ text: buildTimeoutError(shortText, callTimeoutMs / 1e3, attachedPages)
5470
5488
  }],
5471
5489
  isError: true
5472
5490
  };
@@ -5499,13 +5517,13 @@ function createDebugServer(deps) {
5499
5517
  }] };
5500
5518
  let attachedPagesFb = [];
5501
5519
  try {
5502
- attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5520
+ attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5503
5521
  } catch {
5504
5522
  attachedPagesFb = conn.listTargets();
5505
5523
  return {
5506
5524
  content: [{
5507
5525
  type: "text",
5508
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPagesFb)
5526
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPagesFb)
5509
5527
  }],
5510
5528
  isError: true
5511
5529
  };
@@ -5528,13 +5546,13 @@ function createDebugServer(deps) {
5528
5546
  }] };
5529
5547
  let attachedPages = [];
5530
5548
  try {
5531
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5549
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5532
5550
  } catch {
5533
5551
  attachedPages = conn.listTargets();
5534
5552
  return {
5535
5553
  content: [{
5536
5554
  type: "text",
5537
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPages)
5555
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPages)
5538
5556
  }],
5539
5557
  isError: true
5540
5558
  };
@@ -5594,13 +5612,13 @@ function createDebugServer(deps) {
5594
5612
  }] };
5595
5613
  let attachedPagesHl = [];
5596
5614
  try {
5597
- attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5615
+ attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5598
5616
  } catch {
5599
5617
  attachedPagesHl = conn.listTargets();
5600
5618
  return {
5601
5619
  content: [{
5602
5620
  type: "text",
5603
- text: buildTimeoutError(headlessText, waitForAttachTimeoutMs / 1e3, attachedPagesHl)
5621
+ text: buildTimeoutError(headlessText, callTimeoutMs / 1e3, attachedPagesHl)
5604
5622
  }],
5605
5623
  isError: true
5606
5624
  };
@@ -5631,13 +5649,13 @@ function createDebugServer(deps) {
5631
5649
  }] };
5632
5650
  let attachedPages = [];
5633
5651
  try {
5634
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5652
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5635
5653
  } catch {
5636
5654
  attachedPages = conn.listTargets();
5637
5655
  return {
5638
5656
  content: [{
5639
5657
  type: "text",
5640
- text: buildTimeoutError(shortText, waitForAttachTimeoutMs / 1e3, attachedPages)
5658
+ text: buildTimeoutError(shortText, callTimeoutMs / 1e3, attachedPages)
5641
5659
  }],
5642
5660
  isError: true
5643
5661
  };
@@ -5671,13 +5689,13 @@ ${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stder
5671
5689
  }] };
5672
5690
  let attachedPagesFb = [];
5673
5691
  try {
5674
- attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5692
+ attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5675
5693
  } catch {
5676
5694
  attachedPagesFb = conn.listTargets();
5677
5695
  return {
5678
5696
  content: [{
5679
5697
  type: "text",
5680
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPagesFb)
5698
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPagesFb)
5681
5699
  }],
5682
5700
  isError: true
5683
5701
  };
@@ -5700,13 +5718,13 @@ ${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stder
5700
5718
  }] };
5701
5719
  let attachedPages = [];
5702
5720
  try {
5703
- attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, waitForAttachTimeoutMs);
5721
+ attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
5704
5722
  } catch {
5705
5723
  attachedPages = conn.listTargets();
5706
5724
  return {
5707
5725
  content: [{
5708
5726
  type: "text",
5709
- text: buildTimeoutError(baseText, waitForAttachTimeoutMs / 1e3, attachedPages)
5727
+ text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPages)
5710
5728
  }],
5711
5729
  isError: true
5712
5730
  };
@@ -7111,7 +7129,11 @@ const DEV_TOOL_DEFINITIONS = [
7111
7129
  },
7112
7130
  wait_for_attach: {
7113
7131
  type: "boolean",
7114
- description: "If true, block until a page attaches."
7132
+ description: "If true, block until a page attaches (default 60 s)."
7133
+ },
7134
+ wait_timeout_seconds: {
7135
+ type: "number",
7136
+ description: "Maximum seconds to wait when wait_for_attach=true (default 60, range 1–600). Invalid inputs fall back to default."
7115
7137
  }
7116
7138
  },
7117
7139
  required: ["scheme_url"]
@@ -7322,7 +7344,7 @@ function createDevServer(deps = {}) {
7322
7344
  const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
7323
7345
  const server = new Server({
7324
7346
  name: "ait-devtools",
7325
- version: "0.1.85"
7347
+ version: "0.1.87"
7326
7348
  }, { capabilities: { tools: {} } });
7327
7349
  server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
7328
7350
  server.setRequestHandler(CallToolRequestSchema, async (request) => {