@ait-co/devtools 0.1.81 → 0.1.85
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/{chii-relay-DSVG4Ui1.js → chii-relay-BASitNMw.js} +1 -1
- package/dist/{chii-relay-DSVG4Ui1.js.map → chii-relay-BASitNMw.js.map} +1 -1
- package/dist/{chii-relay-BcnVJBqm.cjs → chii-relay-BzUf0LH3.cjs} +1 -1
- package/dist/{chii-relay-BcnVJBqm.cjs.map → chii-relay-BzUf0LH3.cjs.map} +1 -1
- package/dist/{deeplink-B-94XmWA.js → deeplink-BpO9qc-D.js} +5 -3
- package/dist/{deeplink-BLU2_hg6.cjs.map → deeplink-BpO9qc-D.js.map} +1 -1
- package/dist/{deeplink-CYqDwVYs.js → deeplink-D1HXJ2YG.js} +5 -3
- package/dist/{deeplink-CU6opogq.cjs.map → deeplink-D1HXJ2YG.js.map} +1 -1
- package/dist/{deeplink-BLU2_hg6.cjs → deeplink-DCScMYcp.cjs} +5 -3
- package/dist/deeplink-DCScMYcp.cjs.map +1 -0
- package/dist/{deeplink-CU6opogq.cjs → deeplink-DDOe0FQl.cjs} +5 -3
- package/dist/deeplink-DDOe0FQl.cjs.map +1 -0
- package/dist/{devtools-opener-Bp671YXu.cjs → devtools-opener-BDY0w3_0.cjs} +11 -5
- package/dist/devtools-opener-BDY0w3_0.cjs.map +1 -0
- package/dist/{devtools-opener-D84kZFtR.js → devtools-opener-BTl5A6Cd.js} +11 -5
- package/dist/devtools-opener-BTl5A6Cd.js.map +1 -0
- package/dist/{devtools-opener-BbUXBzgA.js → devtools-opener-XpwL3fZ9.js} +22 -6
- package/dist/devtools-opener-XpwL3fZ9.js.map +1 -0
- package/dist/{devtools-opener-h6A-UjzC.cjs → devtools-opener-mDgeg_MX.cjs} +11 -5
- package/dist/devtools-opener-mDgeg_MX.cjs.map +1 -0
- package/dist/machine-state-Chg_6SPq.js +188 -0
- package/dist/machine-state-Chg_6SPq.js.map +1 -0
- package/dist/machine-state-DOUweFsJ.cjs +216 -0
- package/dist/machine-state-DOUweFsJ.cjs.map +1 -0
- package/dist/mcp/cli.js +112 -54
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +6 -10
- package/dist/mcp/server.js.map +1 -1
- package/dist/panel/index.js +109 -8
- package/dist/panel/index.js.map +1 -1
- package/dist/{qr-http-server-DJ5K3Odk.js → qr-http-server-Buorblrx.js} +73 -23
- package/dist/qr-http-server-Buorblrx.js.map +1 -0
- package/dist/{qr-http-server-Dkx2-pKF.cjs → qr-http-server-BvAtX9Lc.cjs} +73 -23
- package/dist/qr-http-server-BvAtX9Lc.cjs.map +1 -0
- package/dist/{qr-http-server-DkOFfZsR.js → qr-http-server-BxjrJr9t.js} +73 -23
- package/dist/qr-http-server-BxjrJr9t.js.map +1 -0
- package/dist/{qr-http-server-MIUHaiYw.cjs → qr-http-server-CAUyOrCm.cjs} +73 -23
- package/dist/qr-http-server-CAUyOrCm.cjs.map +1 -0
- package/dist/{relay-secret-store-CsCOfpWt.cjs → relay-secret-store-B5WAozDv.cjs} +2 -2
- package/dist/{relay-secret-store-CsCOfpWt.cjs.map → relay-secret-store-B5WAozDv.cjs.map} +1 -1
- package/dist/{relay-secret-store-6pPzLkUO.js → relay-secret-store-BvNWdSjV.js} +2 -2
- package/dist/{relay-secret-store-6pPzLkUO.js.map → relay-secret-store-BvNWdSjV.js.map} +1 -1
- package/dist/{relay-url-store-DH8-VUFc.js → relay-url-store-1CXVqNDL.js} +2 -2
- package/dist/{relay-url-store-DH8-VUFc.js.map → relay-url-store-1CXVqNDL.js.map} +1 -1
- package/dist/{relay-url-store-BiEK9BN1.cjs → relay-url-store-D2lX9POP.cjs} +2 -2
- package/dist/{relay-url-store-BiEK9BN1.cjs.map → relay-url-store-D2lX9POP.cjs.map} +1 -1
- package/dist/{totp-DYdP9N3o.js → totp-CauHjkdE.js} +1 -1
- package/dist/{totp-DYdP9N3o.js.map → totp-CauHjkdE.js.map} +1 -1
- package/dist/{totp-CNw0w89F.cjs → totp-D9fjaVak.cjs} +1 -1
- package/dist/{totp-CNw0w89F.cjs.map → totp-D9fjaVak.cjs.map} +1 -1
- package/dist/{tunnel-C-AFdAVL.cjs → tunnel-CfT31xho.cjs} +6 -6
- package/dist/{tunnel-C-AFdAVL.cjs.map → tunnel-CfT31xho.cjs.map} +1 -1
- package/dist/{tunnel-BTlq1mmH.js → tunnel-DPwJBn1u.js} +6 -6
- package/dist/{tunnel-BTlq1mmH.js.map → tunnel-DPwJBn1u.js.map} +1 -1
- package/dist/unplugin/index.cjs +90 -5
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +90 -5
- package/dist/unplugin/index.js.map +1 -1
- package/dist/unplugin/tunnel.cjs +4 -4
- package/dist/unplugin/tunnel.js +4 -4
- package/package.json +1 -1
- package/dist/deeplink-B-94XmWA.js.map +0 -1
- package/dist/deeplink-CYqDwVYs.js.map +0 -1
- package/dist/devtools-opener-BbUXBzgA.js.map +0 -1
- package/dist/devtools-opener-Bp671YXu.cjs.map +0 -1
- package/dist/devtools-opener-D84kZFtR.js.map +0 -1
- package/dist/devtools-opener-h6A-UjzC.cjs.map +0 -1
- package/dist/qr-http-server-DJ5K3Odk.js.map +0 -1
- package/dist/qr-http-server-DkOFfZsR.js.map +0 -1
- package/dist/qr-http-server-Dkx2-pKF.cjs.map +0 -1
- package/dist/qr-http-server-MIUHaiYw.cjs.map +0 -1
package/dist/mcp/cli.js
CHANGED
|
@@ -1031,9 +1031,10 @@ const LAUNCHER_URL = "https://devtools.aitc.dev/launcher/";
|
|
|
1031
1031
|
* @param totpCode - Optional current TOTP code (6 digits). When provided, it
|
|
1032
1032
|
* is appended as `at=<totpCode>`. Must be computed at call time — it rotates
|
|
1033
1033
|
* every 30 s. Omit when TOTP is disabled.
|
|
1034
|
-
* @param opts - Optional app identity hints: `name` and `
|
|
1034
|
+
* @param opts - Optional app identity hints: `name`, `icon`, and `selfdebug`
|
|
1035
|
+
* (#498, #543).
|
|
1035
1036
|
* @returns The launcher deep-link URL with `?url=<enc>&debug=1&relay=<enc>
|
|
1036
|
-
* [&at=<code>][&name=<enc>][&icon=<enc>]` params.
|
|
1037
|
+
* [&at=<code>][&name=<enc>][&icon=<enc>][&selfdebug=1]` params.
|
|
1037
1038
|
*/
|
|
1038
1039
|
function buildLauncherAttachUrl(tunnelUrl, wssUrl, totpCode, opts) {
|
|
1039
1040
|
let url = `${LAUNCHER_URL}?url=${encodeURIComponent(tunnelUrl)}&debug=1&relay=${encodeURIComponent(wssUrl)}`;
|
|
@@ -1048,6 +1049,7 @@ function buildLauncherAttachUrl(tunnelUrl, wssUrl, totpCode, opts) {
|
|
|
1048
1049
|
}
|
|
1049
1050
|
if (iconParsed?.protocol === "https:") url += `&icon=${encodeURIComponent(opts.icon)}`;
|
|
1050
1051
|
}
|
|
1052
|
+
if (opts?.selfdebug === true) url += "&selfdebug=1";
|
|
1051
1053
|
return url;
|
|
1052
1054
|
}
|
|
1053
1055
|
/**
|
|
@@ -1233,12 +1235,18 @@ function buildChiiInspectorUrl(relayHttpBaseUrl, targetId, mintTotp, panel = "co
|
|
|
1233
1235
|
return `${relayHttpBaseUrl.replace(/\/$/, "")}/front_end/chii_app.html?${params.toString()}`;
|
|
1234
1236
|
}
|
|
1235
1237
|
/**
|
|
1236
|
-
* Returns `true` when auto-open is **disabled
|
|
1237
|
-
*
|
|
1238
|
-
* absent)
|
|
1238
|
+
* Returns `true` when auto-open is **disabled**.
|
|
1239
|
+
*
|
|
1240
|
+
* Default (env var absent or any value other than `"1"`) is **disabled** —
|
|
1241
|
+
* the developer uses the "디버그 툴 열기" button on the /attach or dashboard
|
|
1242
|
+
* page instead. Set `AIT_AUTO_DEVTOOLS=1` to restore the old automatic
|
|
1243
|
+
* browser-open behaviour on device attach.
|
|
1244
|
+
*
|
|
1245
|
+
* `AIT_AUTO_DEVTOOLS=0` retains its explicit opt-out meaning for backward
|
|
1246
|
+
* compatibility (same effect as absent).
|
|
1239
1247
|
*/
|
|
1240
1248
|
function isAutoDevtoolsDisabled() {
|
|
1241
|
-
return process.env.AIT_AUTO_DEVTOOLS
|
|
1249
|
+
return process.env.AIT_AUTO_DEVTOOLS !== "1";
|
|
1242
1250
|
}
|
|
1243
1251
|
/**
|
|
1244
1252
|
* Opens the given URL in the OS default browser using a platform-appropriate
|
|
@@ -1341,8 +1349,8 @@ var AutoDevtoolsOpener = class {
|
|
|
1341
1349
|
if (options.inspectorStableUrl) {
|
|
1342
1350
|
this._openedTargets.add(targetId);
|
|
1343
1351
|
const stableUrl = options.inspectorStableUrl;
|
|
1344
|
-
process.stderr.write(`[ait-debug] 기기가
|
|
1345
|
-
[ait-debug]
|
|
1352
|
+
process.stderr.write(`[ait-debug] 기기가 연결됐습니다.
|
|
1353
|
+
[ait-debug] QR 페이지 또는 대시보드(${stableUrl.replace("/inspector", "")})의 "디버그 툴 열기" 버튼을 눌러 DevTools를 여세요.\n[ait-debug] (AIT_AUTO_DEVTOOLS=1 로 설정하면 연결 시 자동으로 열립니다)
|
|
1346
1354
|
`);
|
|
1347
1355
|
if (!openUrlInBrowser(stableUrl)) process.stderr.write(`[ait-debug] 브라우저 자동 열기 실패 — ${stableUrl} 을 브라우저에서 직접 여세요.\n`);
|
|
1348
1356
|
return;
|
|
@@ -1354,8 +1362,8 @@ var AutoDevtoolsOpener = class {
|
|
|
1354
1362
|
process.stderr.write("[ait-debug] 기기가 연결됐습니다 — TOTP secret 미설정으로 인스펙터 URL을 생성할 수 없습니다.\n[ait-debug] relay 세션은 AIT_DEBUG_TOTP_SECRET 설정이 필요합니다.\n");
|
|
1355
1363
|
return;
|
|
1356
1364
|
}
|
|
1357
|
-
process.stderr.write(`[ait-debug] 기기가
|
|
1358
|
-
[ait-debug] DevTools URL: ${inspectorUrl}\n[ait-debug] (AIT_AUTO_DEVTOOLS=
|
|
1365
|
+
process.stderr.write(`[ait-debug] 기기가 연결됐습니다.
|
|
1366
|
+
[ait-debug] DevTools URL: ${inspectorUrl}\n[ait-debug] (AIT_AUTO_DEVTOOLS=1 로 설정하면 연결 시 자동으로 열립니다)
|
|
1359
1367
|
[ait-debug] 주의: URL의 at= 코드는 ~3분 안에서만 유효합니다.
|
|
1360
1368
|
`);
|
|
1361
1369
|
if (!openUrlInBrowser(inspectorUrl)) process.stderr.write("[ait-debug] 브라우저 자동 열기 실패 — 위 URL을 브라우저에서 직접 여세요.\n");
|
|
@@ -2134,8 +2142,8 @@ const en = {
|
|
|
2134
2142
|
"dashboard.url.copy": "Copy",
|
|
2135
2143
|
"dashboard.url.copied": "Copied",
|
|
2136
2144
|
"dashboard.inspector.section": "Inspector",
|
|
2137
|
-
"dashboard.inspector.open": "Open
|
|
2138
|
-
"dashboard.inspector.waiting": "
|
|
2145
|
+
"dashboard.inspector.open": "Open DevTools",
|
|
2146
|
+
"dashboard.inspector.waiting": "Attach a page to enable the \"Open DevTools\" button",
|
|
2139
2147
|
"inspector.error.noTarget": "No page attached. Attach a device and try again.",
|
|
2140
2148
|
"inspector.error.relayDown": "Relay is not active. Start a relay session first.",
|
|
2141
2149
|
"attach.title": "AIT Debug Session — QR Scan",
|
|
@@ -2388,8 +2396,8 @@ const tables = {
|
|
|
2388
2396
|
"dashboard.url.copy": "복사",
|
|
2389
2397
|
"dashboard.url.copied": "복사됨",
|
|
2390
2398
|
"dashboard.inspector.section": "인스펙터",
|
|
2391
|
-
"dashboard.inspector.open": "
|
|
2392
|
-
"dashboard.inspector.waiting": "
|
|
2399
|
+
"dashboard.inspector.open": "디버그 툴 열기",
|
|
2400
|
+
"dashboard.inspector.waiting": "페이지를 attach하면 \"디버그 툴 열기\" 버튼이 표시됩니다",
|
|
2393
2401
|
"inspector.error.noTarget": "연결된 페이지가 없습니다. 기기를 attach한 후 다시 시도하세요.",
|
|
2394
2402
|
"inspector.error.relayDown": "relay가 활성화되지 않았습니다. start_debug로 relay를 기동하세요.",
|
|
2395
2403
|
"attach.title": "AIT 디버그 세션 — QR 스캔",
|
|
@@ -2595,7 +2603,15 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0.5rem 0;
|
|
|
2595
2603
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2596
2604
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2597
2605
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2598
|
-
|
|
2606
|
+
.inspector-link {
|
|
2607
|
+
display: inline-block; margin-top: 0.5rem;
|
|
2608
|
+
padding: 0.45rem 1rem; border-radius: 6px;
|
|
2609
|
+
background: #1f6feb; color: #fff; font-size: 0.85rem; font-weight: 600;
|
|
2610
|
+
text-decoration: none; text-align: center;
|
|
2611
|
+
}
|
|
2612
|
+
.inspector-link:hover { background: #388bfd; }
|
|
2613
|
+
.inspector-hint { display: inline-block; margin-top: 0.5rem; font-size: 0.8rem; opacity: 0.45; }
|
|
2614
|
+
</style></head><body><h1>AIT 디버그 세션 — QR 스캔</h1>__MODE_LABEL____LANG_SWITCHER__<div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>스캔 절차</h2><ol><li>홈 화면의 launcher PWA 아이콘으로 실행하세요 (Safari 주소창이 보이면 standalone이 아닙니다).</li><li>launcher 안의 <strong>"QR 카메라로 스캔"</strong>으로 이 QR 코드를 스캔하세요.</li><li>미니앱이 풀스크린으로 열리고 디버그 세션이 자동으로 attach됩니다.</li></ol></section><hr/><section><h2>진단 체크리스트</h2><ul><li><strong>launcher가 설치돼 있지 않은 경우</strong> — <code>devtools.aitc.dev/launcher/</code>를 한 번 열어 홈 화면에 추가하세요</li><li><strong>카메라 앱으로 스캔하면 Safari 탭으로 열립니다 (하단 탭 바 노출)</strong> — launcher 아이콘으로 다시 실행해 인앱 스캔을 사용하세요</li><li><strong>QR이 만료된 경우 (TOTP — 코드 1개는 30초 창, 만료 후 ~3분(±6 step) 이내 소급 허용)</strong> — 새 QR을 다시 스캔하세요</li><li><strong>Chii 주입 실패 / 콘솔이 비어 있는 경우</strong> — 미니앱 번들에 <code>in-app</code> debug import가 있는지 확인</li></ul></section><hr/><section id="url-section"><h2>URL (fallback)</h2><div class="url-row"><p class="url-box" id="url-box">__SAFE_ATTACH_URL__</p><button class="copy-btn" id="copy-btn" type="button" aria-label="복사">복사</button></div></section><hr/><section id="inspector-section"><h2>인스펙터</h2>__INSPECTOR_SECTION__</section></body></html>`;
|
|
2599
2615
|
const attachChromeHtmlKoIntoss = `<!DOCTYPE html>
|
|
2600
2616
|
<html lang="ko"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="__QR_DATA_URL__"/><title>AIT 디버그 세션 — QR 스캔</title><style>
|
|
2601
2617
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -2645,7 +2661,15 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0.5rem 0;
|
|
|
2645
2661
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2646
2662
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2647
2663
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2648
|
-
|
|
2664
|
+
.inspector-link {
|
|
2665
|
+
display: inline-block; margin-top: 0.5rem;
|
|
2666
|
+
padding: 0.45rem 1rem; border-radius: 6px;
|
|
2667
|
+
background: #1f6feb; color: #fff; font-size: 0.85rem; font-weight: 600;
|
|
2668
|
+
text-decoration: none; text-align: center;
|
|
2669
|
+
}
|
|
2670
|
+
.inspector-link:hover { background: #388bfd; }
|
|
2671
|
+
.inspector-hint { display: inline-block; margin-top: 0.5rem; font-size: 0.8rem; opacity: 0.45; }
|
|
2672
|
+
</style></head><body><h1>AIT 디버그 세션 — QR 스캔</h1>__MODE_LABEL____LANG_SWITCHER__<p class="label">deployment: __SAFE_LABEL__</p><div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>스캔 절차</h2><ol><li>토스 앱을 실행하세요.</li><li>폰 카메라 앱으로 QR 코드를 스캔하세요.</li><li>팝업이 뜨면 <strong>"토스로 열기"</strong>를 탭하세요.</li><li>미니앱이 열리고 디버그 세션이 자동으로 attach됩니다.</li></ol></section><hr/><section><h2>진단 체크리스트</h2><ul><li><strong>토스 앱이 안 열리는 경우</strong> — 앱 버전 확인, 카메라 앱으로 스캔 (토스 앱 내 QR 리더 X)</li><li><strong>미니앱이 PREPARE 상태에서 멈추는 경우</strong> — deep-link에 <code>_deploymentId</code> 파라미터가 있는지 확인</li><li><strong>Chii 주입 실패 / 콘솔이 비어 있는 경우</strong> — 미니앱 번들에 <code>in-app</code> debug import가 있는지 확인</li><li><strong>TOTP gate Layer C가 비활성인 경우</strong> — relay 서버에 <code>AIT_DEBUG_TOTP_SECRET</code>이 설정돼 있는지 확인</li>__LIVE_FAQ__</ul></section><hr/><section id="url-section"><h2>URL (fallback)</h2><div class="url-row"><p class="url-box" id="url-box">__SAFE_ATTACH_URL__</p><button class="copy-btn" id="copy-btn" type="button" aria-label="복사">복사</button></div></section><hr/><section id="inspector-section"><h2>인스펙터</h2>__INSPECTOR_SECTION__</section></body></html>`;
|
|
2649
2673
|
const dashboardChromeHtmlEn = `<!DOCTYPE html>
|
|
2650
2674
|
<html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>AIT Debug Dashboard</title><style>
|
|
2651
2675
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -2755,7 +2779,15 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0.5rem 0;
|
|
|
2755
2779
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2756
2780
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2757
2781
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2758
|
-
|
|
2782
|
+
.inspector-link {
|
|
2783
|
+
display: inline-block; margin-top: 0.5rem;
|
|
2784
|
+
padding: 0.45rem 1rem; border-radius: 6px;
|
|
2785
|
+
background: #1f6feb; color: #fff; font-size: 0.85rem; font-weight: 600;
|
|
2786
|
+
text-decoration: none; text-align: center;
|
|
2787
|
+
}
|
|
2788
|
+
.inspector-link:hover { background: #388bfd; }
|
|
2789
|
+
.inspector-hint { display: inline-block; margin-top: 0.5rem; font-size: 0.8rem; opacity: 0.45; }
|
|
2790
|
+
</style></head><body><h1>AIT Debug Session — QR Scan</h1>__MODE_LABEL____LANG_SWITCHER__<div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>How to scan</h2><ol><li>Launch the launcher PWA icon on your home screen (if the Safari address bar is visible, it is not standalone).</li><li>Scan this QR code with <strong>"Scan QR with camera"</strong> inside the launcher.</li><li>The mini-app opens fullscreen and the debug session attaches automatically.</li></ol></section><hr/><section><h2>Troubleshooting checklist</h2><ul><li><strong>Launcher is not installed</strong> — open <code>devtools.aitc.dev/launcher/</code> once and add it to your home screen</li><li><strong>Scanning with the camera app opens a Safari tab (bottom tab bar visible)</strong> — relaunch from the launcher icon and use the in-app scanner</li><li><strong>QR expired (TOTP — 30-second step, ±6 steps (~3 min) accepted)</strong> — scan a fresh QR code</li><li><strong>Chii injection failure / console is empty</strong> — verify the mini-app bundle has an <code>in-app</code> debug import</li></ul></section><hr/><section id="url-section"><h2>URL (fallback)</h2><div class="url-row"><p class="url-box" id="url-box">__SAFE_ATTACH_URL__</p><button class="copy-btn" id="copy-btn" type="button" aria-label="Copy">Copy</button></div></section><hr/><section id="inspector-section"><h2>Inspector</h2>__INSPECTOR_SECTION__</section></body></html>`;
|
|
2759
2791
|
const attachChromeHtmlEnIntoss = `<!DOCTYPE html>
|
|
2760
2792
|
<html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="__QR_DATA_URL__"/><title>AIT Debug Session — QR Scan</title><style>
|
|
2761
2793
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -2805,7 +2837,15 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0.5rem 0;
|
|
|
2805
2837
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2806
2838
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2807
2839
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2808
|
-
|
|
2840
|
+
.inspector-link {
|
|
2841
|
+
display: inline-block; margin-top: 0.5rem;
|
|
2842
|
+
padding: 0.45rem 1rem; border-radius: 6px;
|
|
2843
|
+
background: #1f6feb; color: #fff; font-size: 0.85rem; font-weight: 600;
|
|
2844
|
+
text-decoration: none; text-align: center;
|
|
2845
|
+
}
|
|
2846
|
+
.inspector-link:hover { background: #388bfd; }
|
|
2847
|
+
.inspector-hint { display: inline-block; margin-top: 0.5rem; font-size: 0.8rem; opacity: 0.45; }
|
|
2848
|
+
</style></head><body><h1>AIT Debug Session — QR Scan</h1>__MODE_LABEL____LANG_SWITCHER__<p class="label">deployment: __SAFE_LABEL__</p><div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>How to scan</h2><ol><li>Open the Toss app.</li><li>Scan the QR code with your phone camera app.</li><li>Tap <strong>"Open in Toss"</strong> when the popup appears.</li><li>The mini-app opens and the debug session attaches automatically.</li></ol></section><hr/><section><h2>Troubleshooting checklist</h2><ul><li><strong>Toss app does not open</strong> — check app version; scan with the system camera app (not the Toss in-app QR reader)</li><li><strong>Mini-app stuck in PREPARE state</strong> — verify the deep-link has a <code>_deploymentId</code> parameter</li><li><strong>Chii injection failure / console is empty</strong> — verify the mini-app bundle has an <code>in-app</code> debug import</li><li><strong>TOTP gate Layer C is inactive</strong> — check that <code>AIT_DEBUG_TOTP_SECRET</code> is set on the relay server</li>__LIVE_FAQ__</ul></section><hr/><section id="url-section"><h2>URL (fallback)</h2><div class="url-row"><p class="url-box" id="url-box">__SAFE_ATTACH_URL__</p><button class="copy-btn" id="copy-btn" type="button" aria-label="Copy">Copy</button></div></section><hr/><section id="inspector-section"><h2>Inspector</h2>__INSPECTOR_SECTION__</section></body></html>`;
|
|
2809
2849
|
/** Map from Locale to the precompiled dashboard chrome string. */
|
|
2810
2850
|
const dashboardChromeByLocale = {
|
|
2811
2851
|
ko: dashboardChromeHtmlKo,
|
|
@@ -2905,8 +2945,9 @@ function buildDashboardHtml(state, qrDataUrl, locale, path = "/", params = new U
|
|
|
2905
2945
|
const copyLabel = escapeHtml(s("dashboard.url.copy"));
|
|
2906
2946
|
attachSection = `<img class="qr" src="${qrDataUrl}" alt="attach QR" /><div class="url-row"><p class="url-box" id="url-box">${safeAttachUrl}</p><button class="copy-btn" id="copy-btn" type="button" aria-label="${copyLabel}">${copyLabel}</button></div>`;
|
|
2907
2947
|
} else attachSection = `<p class="hint">${escapeHtml(s("dashboard.attach.hint"))}</p>`;
|
|
2948
|
+
const pagesAttached = Array.isArray(state.pages) && state.pages.length > 0;
|
|
2908
2949
|
let inspectorSection;
|
|
2909
|
-
if (state.inspectorUrl) inspectorSection = `<a class="inspector-link" id="inspector-link" href="${escapeHtml(state.inspectorUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(s("dashboard.inspector.open"))}</a>`;
|
|
2950
|
+
if (pagesAttached && state.inspectorUrl) inspectorSection = `<a class="inspector-link" id="inspector-link" href="${escapeHtml(state.inspectorUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(s("dashboard.inspector.open"))}</a>`;
|
|
2910
2951
|
else inspectorSection = `<span class="inspector-hint" id="inspector-link">${escapeHtml(s("dashboard.inspector.waiting"))}</span>`;
|
|
2911
2952
|
const pagesSection = state.pages === null ? "" : `<hr /><section id="pages-section"><h2>${escapeHtml(s("dashboard.pages.section"))}</h2><ul id="pages-list">${state.pages.length > 0 ? state.pages.map((p) => {
|
|
2912
2953
|
return `<li><span class="page-id">${escapeHtml(p.id)}</span> <span class="page-url">${escapeHtml(p.url.slice(0, 120))}</span></li>`;
|
|
@@ -3076,11 +3117,15 @@ function buildSseScript(strings) {
|
|
|
3076
3117
|
sec.innerHTML = '<p class=\\"hint\\">' + ATTACH_HINT + '</p>';`}
|
|
3077
3118
|
}
|
|
3078
3119
|
}
|
|
3079
|
-
// 인스펙터 링크 갱신 — #inspector-link (#503).
|
|
3120
|
+
// 인스펙터 링크 갱신 — #inspector-link (#503, gate 보정 #544).
|
|
3121
|
+
// 게이트: pages.length > 0 (페이지 attach 여부) — inspectorUrl 존재 여부가 아님.
|
|
3122
|
+
// #530 이후 inspectorUrl은 항상 안정 URL이므로 null 게이트는 사실상 항상 활성이었다.
|
|
3123
|
+
// pages.length > 0 으로 바꿔 미attach 시 대기 힌트를 보여주도록 수정.
|
|
3080
3124
|
// SECRET-HANDLING: inspectorUrl을 console.log 등으로 출력하지 않는다.
|
|
3081
3125
|
var insp = document.getElementById('inspector-link');
|
|
3082
3126
|
if (insp) {
|
|
3083
|
-
|
|
3127
|
+
var pagesAttachedSse = Array.isArray(s.pages) && s.pages.length > 0;
|
|
3128
|
+
if (pagesAttachedSse && s.inspectorUrl) {
|
|
3084
3129
|
var safeInspUrl = String(s.inspectorUrl).slice(0, 2000).replace(/[<>&"']/g, function (c) { return '&#' + c.charCodeAt(0) + ';'; });
|
|
3085
3130
|
insp.outerHTML = '<a class=\\"inspector-link\\" id=\\"inspector-link\\" href=\\"' + safeInspUrl + '\\" target=\\"_blank\\" rel=\\"noopener noreferrer\\">' + INSPECTOR_OPEN_LABEL + '</a>';
|
|
3086
3131
|
} else {
|
|
@@ -3102,28 +3147,33 @@ function buildSseScript(strings) {
|
|
|
3102
3147
|
* Attach 페이지 HTML — precompiled chrome에 per-request 동적 값을 채워 완성한다.
|
|
3103
3148
|
*
|
|
3104
3149
|
* 동적 파트:
|
|
3105
|
-
* - __QR_DATA_URL__
|
|
3106
|
-
* - __SAFE_LABEL__
|
|
3107
|
-
* - __SAFE_ATTACH_URL__
|
|
3108
|
-
* - __MODE_LABEL__
|
|
3109
|
-
* - __LIVE_FAQ__
|
|
3150
|
+
* - __QR_DATA_URL__ : base64 data URL (QR 이미지)
|
|
3151
|
+
* - __SAFE_LABEL__ : HTML-escaped deploymentId label (intoss family에만 존재)
|
|
3152
|
+
* - __SAFE_ATTACH_URL__ : HTML-escaped attach URL (TOTP at= 코드 포함 — 의도된 전달)
|
|
3153
|
+
* - __MODE_LABEL__ : 환경 배지 (`<p class="mode-label">…</p>` 또는 빈 문자열, #468)
|
|
3154
|
+
* - __LIVE_FAQ__ : 환경 4 LIVE read-only `<li>` 또는 빈 문자열 (intoss family에만 존재)
|
|
3155
|
+
* - __INSPECTOR_SECTION__ : "디버그 툴 열기" 버튼 또는 대기 힌트 (#544)
|
|
3110
3156
|
*
|
|
3111
3157
|
* mode-aware 분기 (#468): mode가 `relay-mobile`이면 sandbox family chrome(launcher
|
|
3112
3158
|
* PWA 절차), 그 외는 intoss family chrome(토스 앱 절차)을 선택한다. `relay-live`는
|
|
3113
3159
|
* intoss chrome에 LIVE read-only 라인을 추가한다.
|
|
3114
3160
|
*
|
|
3115
3161
|
* SSE 스크립트도 주입 — `#attach-section` hook이 있으면 `/events` push 때 QR이
|
|
3116
|
-
* `/qr.png?u=<fresh attachUrl>`로 자동 갱신된다. `#
|
|
3117
|
-
*
|
|
3162
|
+
* `/qr.png?u=<fresh attachUrl>`로 자동 갱신된다. `#inspector-link`도 SSE push로
|
|
3163
|
+
* pages.length > 0 게이트에 따라 활성/비활성 전환된다 (#544).
|
|
3118
3164
|
*
|
|
3119
3165
|
* SECRET-HANDLING: TOTP at= 코드는 attachUrl 캡슐 안에서만 노출 — 의도된 transport.
|
|
3166
|
+
* inspectorStableUrl은 /inspector 안정 URL (127.0.0.1, 시크릿 없음) — 노출 가능.
|
|
3120
3167
|
*/
|
|
3121
|
-
function buildAttachHtml(qrDataUrl, safeLabel, safeAttachUrl, locale, path = "/attach", params = new URLSearchParams(), mode) {
|
|
3168
|
+
function buildAttachHtml(qrDataUrl, safeLabel, safeAttachUrl, locale, path = "/attach", params = new URLSearchParams(), mode, pagesAttached = false, inspectorStableUrl = null) {
|
|
3122
3169
|
const s = resolveLocaleStrings(locale);
|
|
3123
3170
|
const langSwitcher = buildLangSwitcher(path, params, locale, s);
|
|
3124
3171
|
const family = attachFamilyForMode(mode);
|
|
3125
3172
|
const liveFaq = mode === "relay-live" ? `<li>${s("attach.intoss.faq.liveReadOnly")}</li>` : "";
|
|
3126
|
-
|
|
3173
|
+
let inspectorSection;
|
|
3174
|
+
if (pagesAttached && inspectorStableUrl) inspectorSection = `<a class="inspector-link" id="inspector-link" href="${escapeHtml(inspectorStableUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(s("dashboard.inspector.open"))}</a>`;
|
|
3175
|
+
else inspectorSection = `<span class="inspector-hint" id="inspector-link">${escapeHtml(s("dashboard.inspector.waiting"))}</span>`;
|
|
3176
|
+
const filled = attachChromeByLocale[locale][family].replaceAll("__LANG_SWITCHER__", langSwitcher).replaceAll("__MODE_LABEL__", buildModeLabel(mode, s)).replaceAll("__LIVE_FAQ__", liveFaq).replaceAll("__QR_DATA_URL__", qrDataUrl).replaceAll("__SAFE_LABEL__", safeLabel).replaceAll("__SAFE_ATTACH_URL__", safeAttachUrl).replaceAll("__INSPECTOR_SECTION__", inspectorSection);
|
|
3127
3177
|
const sseScript = buildSseScript({
|
|
3128
3178
|
tunnelUp: JSON.stringify(s("dashboard.tunnel.up")),
|
|
3129
3179
|
tunnelDown: JSON.stringify(s("dashboard.tunnel.down")),
|
|
@@ -3225,12 +3275,20 @@ async function startQrHttpServer(getDashboardState, options) {
|
|
|
3225
3275
|
const dpMatch = attachUrl.match(/[?&]_deploymentId=([^&]+)/);
|
|
3226
3276
|
if (dpMatch?.[1]) deploymentIdLabel = decodeURIComponent(dpMatch[1]).slice(0, 36);
|
|
3227
3277
|
} catch {}
|
|
3228
|
-
const
|
|
3278
|
+
const currentState = getDashboardState?.();
|
|
3279
|
+
const mode = currentState?.mode;
|
|
3280
|
+
const pagesAttached = Array.isArray(currentState?.pages) && (currentState?.pages.length ?? 0) > 0;
|
|
3281
|
+
const inspectorStableUrlForAttach = (() => {
|
|
3282
|
+
if (!options?.getDirectInspectorUrl) return null;
|
|
3283
|
+
const addr = server.address();
|
|
3284
|
+
if (!addr || typeof addr === "string") return null;
|
|
3285
|
+
return `http://127.0.0.1:${addr.port}/inspector`;
|
|
3286
|
+
})();
|
|
3229
3287
|
QRCode.toDataURL(attachUrl, {
|
|
3230
3288
|
type: "image/png",
|
|
3231
3289
|
errorCorrectionLevel: "M"
|
|
3232
3290
|
}).then((dataUrl) => {
|
|
3233
|
-
const html = buildAttachHtml(dataUrl, escapeHtml(deploymentIdLabel), escapeHtml(attachUrl), locale, path, params, mode);
|
|
3291
|
+
const html = buildAttachHtml(dataUrl, escapeHtml(deploymentIdLabel), escapeHtml(attachUrl), locale, path, params, mode, pagesAttached, inspectorStableUrlForAttach);
|
|
3234
3292
|
res.writeHead(200, {
|
|
3235
3293
|
"Content-Type": "text/html; charset=utf-8",
|
|
3236
3294
|
"Cache-Control": "no-store"
|
|
@@ -3703,7 +3761,7 @@ const DEBUG_TOOL_DEFINITIONS = [
|
|
|
3703
3761
|
},
|
|
3704
3762
|
{
|
|
3705
3763
|
name: "build_attach_url",
|
|
3706
|
-
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.
|
|
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.",
|
|
3707
3765
|
inputSchema: {
|
|
3708
3766
|
type: "object",
|
|
3709
3767
|
properties: {
|
|
@@ -3715,13 +3773,13 @@ const DEBUG_TOOL_DEFINITIONS = [
|
|
|
3715
3773
|
type: "boolean",
|
|
3716
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."
|
|
3717
3775
|
},
|
|
3718
|
-
open_in_browser: {
|
|
3719
|
-
type: "boolean",
|
|
3720
|
-
description: "If true (default), render the QR as a PNG and open it in the OS default browser. Only works when the MCP server is running on a local GUI machine — headless or remote container environments should set this to false to use the text QR fallback."
|
|
3721
|
-
},
|
|
3722
3776
|
projectRoot: {
|
|
3723
3777
|
type: "string",
|
|
3724
3778
|
description: "Absolute path to the mini-app project root (the directory containing its package.json and .ait_urls). When AIT_TUNNEL_BASE_URL is unset (env 2 / relay-mobile only), the daemon reads the app tunnel URL from <projectRoot>/.ait_urls written by the dev server (tunnel:{cdp:true}). Pass this because the daemon's own cwd is fixed at launch. Omit when AIT_TUNNEL_BASE_URL is set explicitly."
|
|
3779
|
+
},
|
|
3780
|
+
selfdebug: {
|
|
3781
|
+
type: "boolean",
|
|
3782
|
+
description: "Env 2 / relay-sandbox only. When true, adds &selfdebug=1 to the launcher deep-link so the launcher PWA registers its own document as the CDP target (launcher diagnostics mode). SINGLE-ATTACH MODEL: self-target attach evicts any currently-attached mini-app target. Use only when you need to inspect the launcher itself (DOM, safe-area, console). Passing selfdebug=true in env 3/4 (relay-staging/relay-live) returns an error. Default: false (omitted — output is byte-identical to previous behaviour)."
|
|
3725
3783
|
}
|
|
3726
3784
|
},
|
|
3727
3785
|
required: []
|
|
@@ -4717,7 +4775,7 @@ async function readMcpSdkVersion() {
|
|
|
4717
4775
|
* some test environments that skip the build step).
|
|
4718
4776
|
*/
|
|
4719
4777
|
function readDevtoolsVersion() {
|
|
4720
|
-
return "0.1.
|
|
4778
|
+
return "0.1.85";
|
|
4721
4779
|
}
|
|
4722
4780
|
/**
|
|
4723
4781
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -5221,7 +5279,7 @@ function createDebugServer(deps) {
|
|
|
5221
5279
|
const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
|
|
5222
5280
|
const server = new Server({
|
|
5223
5281
|
name: "ait-debug",
|
|
5224
|
-
version: "0.1.
|
|
5282
|
+
version: "0.1.85"
|
|
5225
5283
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
5226
5284
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
5227
5285
|
const conn = router.active;
|
|
@@ -5294,7 +5352,8 @@ function createDebugServer(deps) {
|
|
|
5294
5352
|
}
|
|
5295
5353
|
if (name === "build_attach_url") {
|
|
5296
5354
|
const waitForAttach = request.params.arguments?.wait_for_attach === true;
|
|
5297
|
-
const
|
|
5355
|
+
const selfdebug = request.params.arguments?.selfdebug === true;
|
|
5356
|
+
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 모드로 재시작하세요.");
|
|
5298
5357
|
if (env === "relay-mobile") {
|
|
5299
5358
|
const rawBuildProjectRoot = request.params.arguments?.projectRoot;
|
|
5300
5359
|
const buildProjectRoot = typeof rawBuildProjectRoot === "string" ? rawBuildProjectRoot : void 0;
|
|
@@ -5329,7 +5388,10 @@ function createDebugServer(deps) {
|
|
|
5329
5388
|
const rawName = typeof pkg.name === "string" ? pkg.name : "";
|
|
5330
5389
|
launcherAppName = (rawName.includes("/") ? rawName.slice(rawName.indexOf("/") + 1) : rawName).trim() || void 0;
|
|
5331
5390
|
} catch {}
|
|
5332
|
-
const attachUrl = buildLauncherAttachUrl(tunnelHttpUrl, tunnelStatus.wssUrl, totpCode, {
|
|
5391
|
+
const attachUrl = buildLauncherAttachUrl(tunnelHttpUrl, tunnelStatus.wssUrl, totpCode, {
|
|
5392
|
+
name: launcherAppName,
|
|
5393
|
+
...selfdebug ? { selfdebug: true } : {}
|
|
5394
|
+
});
|
|
5333
5395
|
onAttachUrlBuilt?.({
|
|
5334
5396
|
kind: "launcher",
|
|
5335
5397
|
tunnelHttpUrl,
|
|
@@ -5347,8 +5409,8 @@ function createDebugServer(deps) {
|
|
|
5347
5409
|
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).";
|
|
5348
5410
|
const warningPrefix = "";
|
|
5349
5411
|
const guiAvailable = canOpenBrowser();
|
|
5350
|
-
if (
|
|
5351
|
-
const headlessNote = "
|
|
5412
|
+
if (!guiAvailable) {
|
|
5413
|
+
const headlessNote = "GUI 환경이 감지되지 않았습니다 (headless/remote 환경). 텍스트 QR을 폰 카메라로 스캔하거나, 로컬 GUI 환경에서 실행하세요.\n\n";
|
|
5352
5414
|
const qrHeadless = await renderQr(attachUrl);
|
|
5353
5415
|
const headlessText = `${warningPrefix}${headlessNote}${header}\n${JSON.stringify({
|
|
5354
5416
|
attachUrl,
|
|
@@ -5378,7 +5440,7 @@ function createDebugServer(deps) {
|
|
|
5378
5440
|
text: `${headlessText}\n\n${JSON.stringify(pagesResultHl, null, 2)}`
|
|
5379
5441
|
}] };
|
|
5380
5442
|
}
|
|
5381
|
-
if (
|
|
5443
|
+
if (guiAvailable && qrHttpServer) {
|
|
5382
5444
|
const browserResult = await openQrInBrowser(qrHttpServer.buildAttachPageUrl(attachUrl), `http://127.0.0.1:${qrHttpServer.port}/qr.png?u=${encodeURIComponent(attachUrl)}`);
|
|
5383
5445
|
if (browserResult.opened) {
|
|
5384
5446
|
const retriedNote = browserResult.retried ? " (1회 retry 후 성공)" : "";
|
|
@@ -5423,7 +5485,7 @@ function createDebugServer(deps) {
|
|
|
5423
5485
|
...browserResult.stderrSummary ? { stderrSummary: browserResult.stderrSummary } : {}
|
|
5424
5486
|
};
|
|
5425
5487
|
const stderrNote = browserResult.stderrSummary ? `\nstderr: ${browserResult.stderrSummary}` : "";
|
|
5426
|
-
const fallbackNote =
|
|
5488
|
+
const fallbackNote = `브라우저 자동 열기에 실패했습니다. 다음 URL을 직접 브라우저에서 여세요:\n${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stderrNote + "\n\n";
|
|
5427
5489
|
const qr = await renderQr(attachUrl);
|
|
5428
5490
|
const baseText = `${warningPrefix}${fallbackNote}${header}\n${JSON.stringify({
|
|
5429
5491
|
attachUrl,
|
|
@@ -5518,8 +5580,8 @@ function createDebugServer(deps) {
|
|
|
5518
5580
|
const warningPrefix = authorityWarning ? `⚠️ scheme_url 경고: ${authorityWarning}\n\n` : "";
|
|
5519
5581
|
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).";
|
|
5520
5582
|
const guiAvailable = canOpenBrowser();
|
|
5521
|
-
if (
|
|
5522
|
-
const headlessNote = "
|
|
5583
|
+
if (!guiAvailable) {
|
|
5584
|
+
const headlessNote = "GUI 환경이 감지되지 않았습니다 (headless/remote 환경). 텍스트 QR을 폰 카메라로 스캔하거나, 로컬 GUI 환경에서 실행하세요.\n\n";
|
|
5523
5585
|
const qrHeadless = await renderQr(attachUrl);
|
|
5524
5586
|
const headlessText = `${warningPrefix}${headlessNote}${header}\n${JSON.stringify({
|
|
5525
5587
|
attachUrl,
|
|
@@ -5549,7 +5611,7 @@ function createDebugServer(deps) {
|
|
|
5549
5611
|
text: `${headlessText}\n\n${JSON.stringify(pagesResultHl, null, 2)}`
|
|
5550
5612
|
}] };
|
|
5551
5613
|
}
|
|
5552
|
-
if (
|
|
5614
|
+
if (guiAvailable && qrHttpServer) {
|
|
5553
5615
|
const browserResult = await openQrInBrowser(qrHttpServer.buildAttachPageUrl(attachUrl), `http://127.0.0.1:${qrHttpServer.port}/qr.png?u=${encodeURIComponent(attachUrl)}`);
|
|
5554
5616
|
if (browserResult.opened) {
|
|
5555
5617
|
const retriedNote = browserResult.retried ? " (1회 retry 후 성공)" : "";
|
|
@@ -5594,7 +5656,7 @@ function createDebugServer(deps) {
|
|
|
5594
5656
|
...browserResult.stderrSummary ? { stderrSummary: browserResult.stderrSummary } : {}
|
|
5595
5657
|
};
|
|
5596
5658
|
const stderrNote = browserResult.stderrSummary ? `\nstderr: ${browserResult.stderrSummary}` : "";
|
|
5597
|
-
const fallbackNote =
|
|
5659
|
+
const fallbackNote = `브라우저 자동 열기에 실패했습니다. 다음 URL을 직접 브라우저에서 여세요:
|
|
5598
5660
|
${browserResult.httpUrl}\n또는 PNG로 받기: ${browserResult.pngUrl}` + stderrNote + "\n\n";
|
|
5599
5661
|
const qr = await renderQr(attachUrl);
|
|
5600
5662
|
const baseText = `${warningPrefix}${fallbackNote}${header}\n${JSON.stringify({
|
|
@@ -7050,10 +7112,6 @@ const DEV_TOOL_DEFINITIONS = [
|
|
|
7050
7112
|
wait_for_attach: {
|
|
7051
7113
|
type: "boolean",
|
|
7052
7114
|
description: "If true, block until a page attaches."
|
|
7053
|
-
},
|
|
7054
|
-
open_in_browser: {
|
|
7055
|
-
type: "boolean",
|
|
7056
|
-
description: "If true (default), open the QR PNG in the OS browser."
|
|
7057
7115
|
}
|
|
7058
7116
|
},
|
|
7059
7117
|
required: ["scheme_url"]
|
|
@@ -7264,7 +7322,7 @@ function createDevServer(deps = {}) {
|
|
|
7264
7322
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
7265
7323
|
const server = new Server({
|
|
7266
7324
|
name: "ait-devtools",
|
|
7267
|
-
version: "0.1.
|
|
7325
|
+
version: "0.1.85"
|
|
7268
7326
|
}, { capabilities: { tools: {} } });
|
|
7269
7327
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
7270
7328
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|