@ait-co/devtools 0.1.84 → 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/README.en.md +1 -1
- package/README.md +1 -1
- package/dist/mcp/cli.js +52 -41
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +10 -10
- package/dist/mcp/server.js.map +1 -1
- package/dist/panel/index.js +2 -2
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -106,7 +106,7 @@ For environments 3 and 4 (intoss-private relay), the relay QR deep-link carries
|
|
|
106
106
|
|
|
107
107
|
**"QR window doesn't open"**
|
|
108
108
|
|
|
109
|
-
Either `build_attach_url` wasn't called first, or
|
|
109
|
+
Either `build_attach_url` wasn't called first, or the MCP server is running in a headless environment where no browser can be opened. The tool result always includes a text QR — scan it directly with your phone camera. On a local GUI machine, the dashboard opens automatically in the browser.
|
|
110
110
|
|
|
111
111
|
**"Page not attached" — list_pages returns an empty array**
|
|
112
112
|
|
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ import '@ait-co/devtools/in-app/auto';
|
|
|
106
106
|
|
|
107
107
|
**"QR 창이 안 열림"**
|
|
108
108
|
|
|
109
|
-
`build_attach_url`을 먼저 호출하지 않았거나, GUI 없는 headless
|
|
109
|
+
`build_attach_url`을 먼저 호출하지 않았거나, GUI 없는 headless 환경이라 대시보드를 열 수 없는 경우입니다. 도구 결과에 텍스트 QR이 출력되므로 폰 카메라로 직접 스캔하세요. 로컬 GUI 환경에서는 대시보드가 자동으로 브라우저에 열립니다.
|
|
110
110
|
|
|
111
111
|
**"page 미attach" — list_pages가 빈 배열 반환**
|
|
112
112
|
|
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 (
|
|
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,11 +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,
|
|
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
3775
|
},
|
|
3776
|
-
|
|
3777
|
-
type: "
|
|
3778
|
-
description: "
|
|
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."
|
|
3779
3779
|
},
|
|
3780
3780
|
projectRoot: {
|
|
3781
3781
|
type: "string",
|
|
@@ -4779,7 +4779,7 @@ async function readMcpSdkVersion() {
|
|
|
4779
4779
|
* some test environments that skip the build step).
|
|
4780
4780
|
*/
|
|
4781
4781
|
function readDevtoolsVersion() {
|
|
4782
|
-
return "0.1.
|
|
4782
|
+
return "0.1.86";
|
|
4783
4783
|
}
|
|
4784
4784
|
/**
|
|
4785
4785
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -4855,9 +4855,14 @@ async function getDiagnostics(input) {
|
|
|
4855
4855
|
reissueAttempts: tunnel.reissueAttempts ?? 0
|
|
4856
4856
|
};
|
|
4857
4857
|
let pages = null;
|
|
4858
|
-
if (connection !== void 0)
|
|
4859
|
-
|
|
4860
|
-
|
|
4858
|
+
if (connection !== void 0) {
|
|
4859
|
+
try {
|
|
4860
|
+
await connection.refreshTargets?.();
|
|
4861
|
+
} catch {}
|
|
4862
|
+
try {
|
|
4863
|
+
pages = listPages(connection, tunnel);
|
|
4864
|
+
} catch {}
|
|
4865
|
+
}
|
|
4861
4866
|
const limit = Math.min(Math.max(1, recentErrorsLimit), 50);
|
|
4862
4867
|
const recentErrors = collector.getRecentErrors(limit);
|
|
4863
4868
|
const authRejects = collector.getAuthRejects();
|
|
@@ -5275,7 +5280,7 @@ function waitForAttachWithEvents(connection, filterFn, timeoutMs, pollIntervalMs
|
|
|
5275
5280
|
* naturally via `enableDomains`). The tier only controls visibility.
|
|
5276
5281
|
*/
|
|
5277
5282
|
function createDebugServer(deps) {
|
|
5278
|
-
const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs =
|
|
5283
|
+
const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs = 6e4, qrHttpServer, getEnvironment: getEnvDep, getEnvironmentReason: getEnvReasonDep, diagnosticsCollector: collectorDep, totpSecret, onAttachUrlBuilt } = deps;
|
|
5279
5284
|
const getTotpSecret = deps.getTotpSecret ?? (() => totpSecret);
|
|
5280
5285
|
const router = routerDep ?? makeSingleConnectionRouter(connection);
|
|
5281
5286
|
const resolveEnvironment = getEnvDep ?? (() => deriveEnvironment(router.active.kind, getLiveIntent(), router.activeRelayOrigin));
|
|
@@ -5283,7 +5288,7 @@ function createDebugServer(deps) {
|
|
|
5283
5288
|
const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
|
|
5284
5289
|
const server = new Server({
|
|
5285
5290
|
name: "ait-debug",
|
|
5286
|
-
version: "0.1.
|
|
5291
|
+
version: "0.1.86"
|
|
5287
5292
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
5288
5293
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
5289
5294
|
const conn = router.active;
|
|
@@ -5356,8 +5361,14 @@ function createDebugServer(deps) {
|
|
|
5356
5361
|
}
|
|
5357
5362
|
if (name === "build_attach_url") {
|
|
5358
5363
|
const waitForAttach = request.params.arguments?.wait_for_attach === true;
|
|
5359
|
-
const openInBrowser = request.params.arguments?.open_in_browser !== false;
|
|
5360
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
|
+
})();
|
|
5361
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 모드로 재시작하세요.");
|
|
5362
5373
|
if (env === "relay-mobile") {
|
|
5363
5374
|
const rawBuildProjectRoot = request.params.arguments?.projectRoot;
|
|
@@ -5414,8 +5425,8 @@ function createDebugServer(deps) {
|
|
|
5414
5425
|
const header = "This tool result is shown to the user directly — do NOT re-print the QR below in your reply (it wastes output tokens). Just tell the user to scan the QR in this output (Ctrl+O to expand if collapsed).";
|
|
5415
5426
|
const warningPrefix = "";
|
|
5416
5427
|
const guiAvailable = canOpenBrowser();
|
|
5417
|
-
if (
|
|
5418
|
-
const headlessNote = "
|
|
5428
|
+
if (!guiAvailable) {
|
|
5429
|
+
const headlessNote = "GUI 환경이 감지되지 않았습니다 (headless/remote 환경). 텍스트 QR을 폰 카메라로 스캔하거나, 로컬 GUI 환경에서 실행하세요.\n\n";
|
|
5419
5430
|
const qrHeadless = await renderQr(attachUrl);
|
|
5420
5431
|
const headlessText = `${warningPrefix}${headlessNote}${header}\n${JSON.stringify({
|
|
5421
5432
|
attachUrl,
|
|
@@ -5428,13 +5439,13 @@ function createDebugServer(deps) {
|
|
|
5428
5439
|
}] };
|
|
5429
5440
|
let attachedPagesHl = [];
|
|
5430
5441
|
try {
|
|
5431
|
-
attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5442
|
+
attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5432
5443
|
} catch {
|
|
5433
5444
|
attachedPagesHl = conn.listTargets();
|
|
5434
5445
|
return {
|
|
5435
5446
|
content: [{
|
|
5436
5447
|
type: "text",
|
|
5437
|
-
text: buildTimeoutError(headlessText,
|
|
5448
|
+
text: buildTimeoutError(headlessText, callTimeoutMs / 1e3, attachedPagesHl)
|
|
5438
5449
|
}],
|
|
5439
5450
|
isError: true
|
|
5440
5451
|
};
|
|
@@ -5445,7 +5456,7 @@ function createDebugServer(deps) {
|
|
|
5445
5456
|
text: `${headlessText}\n\n${JSON.stringify(pagesResultHl, null, 2)}`
|
|
5446
5457
|
}] };
|
|
5447
5458
|
}
|
|
5448
|
-
if (
|
|
5459
|
+
if (guiAvailable && qrHttpServer) {
|
|
5449
5460
|
const browserResult = await openQrInBrowser(qrHttpServer.buildAttachPageUrl(attachUrl), `http://127.0.0.1:${qrHttpServer.port}/qr.png?u=${encodeURIComponent(attachUrl)}`);
|
|
5450
5461
|
if (browserResult.opened) {
|
|
5451
5462
|
const retriedNote = browserResult.retried ? " (1회 retry 후 성공)" : "";
|
|
@@ -5465,13 +5476,13 @@ function createDebugServer(deps) {
|
|
|
5465
5476
|
}] };
|
|
5466
5477
|
let attachedPages = [];
|
|
5467
5478
|
try {
|
|
5468
|
-
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5479
|
+
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5469
5480
|
} catch {
|
|
5470
5481
|
attachedPages = conn.listTargets();
|
|
5471
5482
|
return {
|
|
5472
5483
|
content: [{
|
|
5473
5484
|
type: "text",
|
|
5474
|
-
text: buildTimeoutError(shortText,
|
|
5485
|
+
text: buildTimeoutError(shortText, callTimeoutMs / 1e3, attachedPages)
|
|
5475
5486
|
}],
|
|
5476
5487
|
isError: true
|
|
5477
5488
|
};
|
|
@@ -5490,7 +5501,7 @@ function createDebugServer(deps) {
|
|
|
5490
5501
|
...browserResult.stderrSummary ? { stderrSummary: browserResult.stderrSummary } : {}
|
|
5491
5502
|
};
|
|
5492
5503
|
const stderrNote = browserResult.stderrSummary ? `\nstderr: ${browserResult.stderrSummary}` : "";
|
|
5493
|
-
const fallbackNote =
|
|
5504
|
+
const fallbackNote = `브라우저 자동 열기에 실패했습니다. 다음 URL을 직접 브라우저에서 여세요:\n${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stderrNote + "\n\n";
|
|
5494
5505
|
const qr = await renderQr(attachUrl);
|
|
5495
5506
|
const baseText = `${warningPrefix}${fallbackNote}${header}\n${JSON.stringify({
|
|
5496
5507
|
attachUrl,
|
|
@@ -5504,13 +5515,13 @@ function createDebugServer(deps) {
|
|
|
5504
5515
|
}] };
|
|
5505
5516
|
let attachedPagesFb = [];
|
|
5506
5517
|
try {
|
|
5507
|
-
attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5518
|
+
attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5508
5519
|
} catch {
|
|
5509
5520
|
attachedPagesFb = conn.listTargets();
|
|
5510
5521
|
return {
|
|
5511
5522
|
content: [{
|
|
5512
5523
|
type: "text",
|
|
5513
|
-
text: buildTimeoutError(baseText,
|
|
5524
|
+
text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPagesFb)
|
|
5514
5525
|
}],
|
|
5515
5526
|
isError: true
|
|
5516
5527
|
};
|
|
@@ -5533,13 +5544,13 @@ function createDebugServer(deps) {
|
|
|
5533
5544
|
}] };
|
|
5534
5545
|
let attachedPages = [];
|
|
5535
5546
|
try {
|
|
5536
|
-
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5547
|
+
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5537
5548
|
} catch {
|
|
5538
5549
|
attachedPages = conn.listTargets();
|
|
5539
5550
|
return {
|
|
5540
5551
|
content: [{
|
|
5541
5552
|
type: "text",
|
|
5542
|
-
text: buildTimeoutError(baseText,
|
|
5553
|
+
text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPages)
|
|
5543
5554
|
}],
|
|
5544
5555
|
isError: true
|
|
5545
5556
|
};
|
|
@@ -5585,8 +5596,8 @@ function createDebugServer(deps) {
|
|
|
5585
5596
|
const warningPrefix = authorityWarning ? `⚠️ scheme_url 경고: ${authorityWarning}\n\n` : "";
|
|
5586
5597
|
const header = "This tool result is shown to the user directly — do NOT re-print the QR below in your reply (it wastes output tokens). Just tell the user to scan the QR in this output (Ctrl+O to expand if collapsed).";
|
|
5587
5598
|
const guiAvailable = canOpenBrowser();
|
|
5588
|
-
if (
|
|
5589
|
-
const headlessNote = "
|
|
5599
|
+
if (!guiAvailable) {
|
|
5600
|
+
const headlessNote = "GUI 환경이 감지되지 않았습니다 (headless/remote 환경). 텍스트 QR을 폰 카메라로 스캔하거나, 로컬 GUI 환경에서 실행하세요.\n\n";
|
|
5590
5601
|
const qrHeadless = await renderQr(attachUrl);
|
|
5591
5602
|
const headlessText = `${warningPrefix}${headlessNote}${header}\n${JSON.stringify({
|
|
5592
5603
|
attachUrl,
|
|
@@ -5599,13 +5610,13 @@ function createDebugServer(deps) {
|
|
|
5599
5610
|
}] };
|
|
5600
5611
|
let attachedPagesHl = [];
|
|
5601
5612
|
try {
|
|
5602
|
-
attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5613
|
+
attachedPagesHl = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5603
5614
|
} catch {
|
|
5604
5615
|
attachedPagesHl = conn.listTargets();
|
|
5605
5616
|
return {
|
|
5606
5617
|
content: [{
|
|
5607
5618
|
type: "text",
|
|
5608
|
-
text: buildTimeoutError(headlessText,
|
|
5619
|
+
text: buildTimeoutError(headlessText, callTimeoutMs / 1e3, attachedPagesHl)
|
|
5609
5620
|
}],
|
|
5610
5621
|
isError: true
|
|
5611
5622
|
};
|
|
@@ -5616,7 +5627,7 @@ function createDebugServer(deps) {
|
|
|
5616
5627
|
text: `${headlessText}\n\n${JSON.stringify(pagesResultHl, null, 2)}`
|
|
5617
5628
|
}] };
|
|
5618
5629
|
}
|
|
5619
|
-
if (
|
|
5630
|
+
if (guiAvailable && qrHttpServer) {
|
|
5620
5631
|
const browserResult = await openQrInBrowser(qrHttpServer.buildAttachPageUrl(attachUrl), `http://127.0.0.1:${qrHttpServer.port}/qr.png?u=${encodeURIComponent(attachUrl)}`);
|
|
5621
5632
|
if (browserResult.opened) {
|
|
5622
5633
|
const retriedNote = browserResult.retried ? " (1회 retry 후 성공)" : "";
|
|
@@ -5636,13 +5647,13 @@ function createDebugServer(deps) {
|
|
|
5636
5647
|
}] };
|
|
5637
5648
|
let attachedPages = [];
|
|
5638
5649
|
try {
|
|
5639
|
-
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5650
|
+
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5640
5651
|
} catch {
|
|
5641
5652
|
attachedPages = conn.listTargets();
|
|
5642
5653
|
return {
|
|
5643
5654
|
content: [{
|
|
5644
5655
|
type: "text",
|
|
5645
|
-
text: buildTimeoutError(shortText,
|
|
5656
|
+
text: buildTimeoutError(shortText, callTimeoutMs / 1e3, attachedPages)
|
|
5646
5657
|
}],
|
|
5647
5658
|
isError: true
|
|
5648
5659
|
};
|
|
@@ -5661,7 +5672,7 @@ function createDebugServer(deps) {
|
|
|
5661
5672
|
...browserResult.stderrSummary ? { stderrSummary: browserResult.stderrSummary } : {}
|
|
5662
5673
|
};
|
|
5663
5674
|
const stderrNote = browserResult.stderrSummary ? `\nstderr: ${browserResult.stderrSummary}` : "";
|
|
5664
|
-
const fallbackNote =
|
|
5675
|
+
const fallbackNote = `브라우저 자동 열기에 실패했습니다. 다음 URL을 직접 브라우저에서 여세요:
|
|
5665
5676
|
${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stderrNote + "\n\n";
|
|
5666
5677
|
const qr = await renderQr(attachUrl);
|
|
5667
5678
|
const baseText = `${warningPrefix}${fallbackNote}${header}\n${JSON.stringify({
|
|
@@ -5676,13 +5687,13 @@ ${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stder
|
|
|
5676
5687
|
}] };
|
|
5677
5688
|
let attachedPagesFb = [];
|
|
5678
5689
|
try {
|
|
5679
|
-
attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5690
|
+
attachedPagesFb = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5680
5691
|
} catch {
|
|
5681
5692
|
attachedPagesFb = conn.listTargets();
|
|
5682
5693
|
return {
|
|
5683
5694
|
content: [{
|
|
5684
5695
|
type: "text",
|
|
5685
|
-
text: buildTimeoutError(baseText,
|
|
5696
|
+
text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPagesFb)
|
|
5686
5697
|
}],
|
|
5687
5698
|
isError: true
|
|
5688
5699
|
};
|
|
@@ -5705,13 +5716,13 @@ ${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stder
|
|
|
5705
5716
|
}] };
|
|
5706
5717
|
let attachedPages = [];
|
|
5707
5718
|
try {
|
|
5708
|
-
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage,
|
|
5719
|
+
attachedPages = await waitForAttachWithEvents(conn, isMatchingPage, callTimeoutMs);
|
|
5709
5720
|
} catch {
|
|
5710
5721
|
attachedPages = conn.listTargets();
|
|
5711
5722
|
return {
|
|
5712
5723
|
content: [{
|
|
5713
5724
|
type: "text",
|
|
5714
|
-
text: buildTimeoutError(baseText,
|
|
5725
|
+
text: buildTimeoutError(baseText, callTimeoutMs / 1e3, attachedPages)
|
|
5715
5726
|
}],
|
|
5716
5727
|
isError: true
|
|
5717
5728
|
};
|
|
@@ -7116,11 +7127,11 @@ const DEV_TOOL_DEFINITIONS = [
|
|
|
7116
7127
|
},
|
|
7117
7128
|
wait_for_attach: {
|
|
7118
7129
|
type: "boolean",
|
|
7119
|
-
description: "If true, block until a page attaches."
|
|
7130
|
+
description: "If true, block until a page attaches (default 60 s)."
|
|
7120
7131
|
},
|
|
7121
|
-
|
|
7122
|
-
type: "
|
|
7123
|
-
description: "
|
|
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."
|
|
7124
7135
|
}
|
|
7125
7136
|
},
|
|
7126
7137
|
required: ["scheme_url"]
|
|
@@ -7331,7 +7342,7 @@ function createDevServer(deps = {}) {
|
|
|
7331
7342
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
7332
7343
|
const server = new Server({
|
|
7333
7344
|
name: "ait-devtools",
|
|
7334
|
-
version: "0.1.
|
|
7345
|
+
version: "0.1.86"
|
|
7335
7346
|
}, { capabilities: { tools: {} } });
|
|
7336
7347
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
7337
7348
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|