@ait-co/devtools 0.1.72 → 0.1.74
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/{deeplink-CaO6hZVG.js → deeplink-B-94XmWA.js} +19 -3
- package/dist/deeplink-B-94XmWA.js.map +1 -0
- package/dist/{deeplink-BONXxWEO.cjs → deeplink-BLU2_hg6.cjs} +19 -3
- package/dist/deeplink-BLU2_hg6.cjs.map +1 -0
- package/dist/{deeplink-CCGiyoHq.cjs → deeplink-CU6opogq.cjs} +19 -3
- package/dist/deeplink-CU6opogq.cjs.map +1 -0
- package/dist/{deeplink-Cqli4qzm.js → deeplink-CYqDwVYs.js} +19 -3
- package/dist/deeplink-CYqDwVYs.js.map +1 -0
- package/dist/mcp/cli.js +162 -54
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/panel/index.js +22 -8
- package/dist/panel/index.js.map +1 -1
- package/dist/{qr-http-server-Bh9qkiRm.js → qr-http-server-Bn2ciFuC.js} +65 -12
- package/dist/qr-http-server-Bn2ciFuC.js.map +1 -0
- package/dist/{qr-http-server-DPOOrh6y.cjs → qr-http-server-BqZ8c0Bp.cjs} +65 -12
- package/dist/qr-http-server-BqZ8c0Bp.cjs.map +1 -0
- package/dist/{qr-http-server-CwkTFbMV.js → qr-http-server-Cpc4jfTA.js} +65 -12
- package/dist/qr-http-server-Cpc4jfTA.js.map +1 -0
- package/dist/{qr-http-server-CG198dpc.cjs → qr-http-server-DNGVwI0P.cjs} +65 -12
- package/dist/qr-http-server-DNGVwI0P.cjs.map +1 -0
- package/dist/{tunnel-CQdoKopR.js → tunnel-BuymAS3O.js} +22 -10
- package/dist/tunnel-BuymAS3O.js.map +1 -0
- package/dist/{tunnel-BS6Td6f4.cjs → tunnel-CAxygywQ.cjs} +22 -10
- package/dist/tunnel-CAxygywQ.cjs.map +1 -0
- package/dist/unplugin/index.cjs +13 -3
- 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 +13 -3
- package/dist/unplugin/index.js.map +1 -1
- package/dist/unplugin/tunnel.cjs +21 -9
- package/dist/unplugin/tunnel.cjs.map +1 -1
- package/dist/unplugin/tunnel.d.cts +36 -3
- package/dist/unplugin/tunnel.d.cts.map +1 -1
- package/dist/unplugin/tunnel.d.ts +36 -3
- package/dist/unplugin/tunnel.d.ts.map +1 -1
- package/dist/unplugin/tunnel.js +21 -9
- package/dist/unplugin/tunnel.js.map +1 -1
- package/package.json +1 -1
- package/dist/deeplink-BONXxWEO.cjs.map +0 -1
- package/dist/deeplink-CCGiyoHq.cjs.map +0 -1
- package/dist/deeplink-CaO6hZVG.js.map +0 -1
- package/dist/deeplink-Cqli4qzm.js.map +0 -1
- package/dist/qr-http-server-Bh9qkiRm.js.map +0 -1
- package/dist/qr-http-server-CG198dpc.cjs.map +0 -1
- package/dist/qr-http-server-CwkTFbMV.js.map +0 -1
- package/dist/qr-http-server-DPOOrh6y.cjs.map +0 -1
- package/dist/tunnel-BS6Td6f4.cjs.map +0 -1
- package/dist/tunnel-CQdoKopR.js.map +0 -1
package/dist/mcp/cli.js
CHANGED
|
@@ -1012,6 +1012,11 @@ const LAUNCHER_URL = "https://devtools.aitc.dev/launcher/";
|
|
|
1012
1012
|
* is injected. `&at=<totpCode>` is added only when a code is provided (same
|
|
1013
1013
|
* conditional as {@link buildDeepLinkAttachUrl}).
|
|
1014
1014
|
*
|
|
1015
|
+
* When `opts.name` is given (non-blank), it is added as `&name=` so the
|
|
1016
|
+
* launcher partner bar shows the app name instead of the generic default (#498).
|
|
1017
|
+
* When `opts.icon` is an absolute https:// URL, it is added as `&icon=` so the
|
|
1018
|
+
* launcher can render an icon next to the title (#498).
|
|
1019
|
+
*
|
|
1015
1020
|
* Unlike `buildDeepLinkAttachUrl` (which splices onto a non-special scheme URL
|
|
1016
1021
|
* via raw string manipulation), this function uses WHATWG `encodeURIComponent`
|
|
1017
1022
|
* because the target is a standard `https:` URL.
|
|
@@ -1026,12 +1031,23 @@ const LAUNCHER_URL = "https://devtools.aitc.dev/launcher/";
|
|
|
1026
1031
|
* @param totpCode - Optional current TOTP code (6 digits). When provided, it
|
|
1027
1032
|
* is appended as `at=<totpCode>`. Must be computed at call time — it rotates
|
|
1028
1033
|
* every 30 s. Omit when TOTP is disabled.
|
|
1034
|
+
* @param opts - Optional app identity hints: `name` and `icon` (#498).
|
|
1029
1035
|
* @returns The launcher deep-link URL with `?url=<enc>&debug=1&relay=<enc>
|
|
1030
|
-
* [&at=<code>]` params.
|
|
1036
|
+
* [&at=<code>][&name=<enc>][&icon=<enc>]` params.
|
|
1031
1037
|
*/
|
|
1032
|
-
function buildLauncherAttachUrl(tunnelUrl, wssUrl, totpCode) {
|
|
1038
|
+
function buildLauncherAttachUrl(tunnelUrl, wssUrl, totpCode, opts) {
|
|
1033
1039
|
let url = `${LAUNCHER_URL}?url=${encodeURIComponent(tunnelUrl)}&debug=1&relay=${encodeURIComponent(wssUrl)}`;
|
|
1034
1040
|
if (totpCode !== void 0 && totpCode !== "") url += `&at=${encodeURIComponent(totpCode)}`;
|
|
1041
|
+
if (opts?.name !== void 0 && opts.name.trim() !== "") url += `&name=${encodeURIComponent(opts.name.trim())}`;
|
|
1042
|
+
if (opts?.icon !== void 0) {
|
|
1043
|
+
let iconParsed;
|
|
1044
|
+
try {
|
|
1045
|
+
iconParsed = new URL(opts.icon);
|
|
1046
|
+
} catch {
|
|
1047
|
+
iconParsed = null;
|
|
1048
|
+
}
|
|
1049
|
+
if (iconParsed?.protocol === "https:") url += `&icon=${encodeURIComponent(opts.icon)}`;
|
|
1050
|
+
}
|
|
1035
1051
|
return url;
|
|
1036
1052
|
}
|
|
1037
1053
|
/**
|
|
@@ -2080,6 +2096,9 @@ const en = {
|
|
|
2080
2096
|
"dashboard.pages.empty": "No attached pages",
|
|
2081
2097
|
"dashboard.url.copy": "Copy",
|
|
2082
2098
|
"dashboard.url.copied": "Copied",
|
|
2099
|
+
"dashboard.inspector.section": "Inspector",
|
|
2100
|
+
"dashboard.inspector.open": "Open inspector",
|
|
2101
|
+
"dashboard.inspector.waiting": "Inspector URL pending — appears after a page attaches",
|
|
2083
2102
|
"attach.title": "AIT Debug Session — QR Scan",
|
|
2084
2103
|
"attach.deployment": "deployment: {label}",
|
|
2085
2104
|
"attach.steps.section": "How to scan",
|
|
@@ -2110,7 +2129,6 @@ const en = {
|
|
|
2110
2129
|
"launcher.urlPlaceholder": "https://example.trycloudflare.com",
|
|
2111
2130
|
"launcher.openBtn": "Open",
|
|
2112
2131
|
"launcher.scanBtn": "Scan QR with camera",
|
|
2113
|
-
"launcher.rescanBtn": "Rescan",
|
|
2114
2132
|
"launcher.noCamera": "No camera available — paste the URL instead.",
|
|
2115
2133
|
"launcher.cameraError": "Could not access the camera — paste the URL instead.",
|
|
2116
2134
|
"launcher.invalidUrlHttps": "Enter a valid https:// URL (the tunnel URL from your terminal).",
|
|
@@ -2119,11 +2137,16 @@ const en = {
|
|
|
2119
2137
|
"launcher.debugAuthFailedHint": "The QR code may have expired. Scan a fresh QR code.",
|
|
2120
2138
|
"launcher.debugAuthExpiredHint": "The debug session has expired. Scan a fresh QR from the attach page on your Mac.",
|
|
2121
2139
|
"launcher.debugAuthRescanCta": "Scan a new QR",
|
|
2122
|
-
"launcher.diagFab": "Diag",
|
|
2123
2140
|
"launcher.diagTitle": "Viewport diagnostics",
|
|
2124
2141
|
"launcher.diagYes": "yes",
|
|
2125
2142
|
"launcher.diagNo": "no",
|
|
2126
|
-
"launcher.letterboxDetected": "Display area is {pt}pt short — likely an iOS standalone letterbox. Removing and re-adding the launcher to the home screen may fix it."
|
|
2143
|
+
"launcher.letterboxDetected": "Display area is {pt}pt short — likely an iOS standalone letterbox. Removing and re-adding the launcher to the home screen may fix it.",
|
|
2144
|
+
"launcher.navbar.defaultTitle": "Mini App",
|
|
2145
|
+
"launcher.navbar.menu": "Menu",
|
|
2146
|
+
"launcher.navbar.close": "Close",
|
|
2147
|
+
"launcher.navbar.menuRescan": "Rescan",
|
|
2148
|
+
"launcher.navbar.menuDiag": "Viewport diagnostics",
|
|
2149
|
+
"launcher.navbar.menuLanguage": "Language"
|
|
2127
2150
|
};
|
|
2128
2151
|
//#endregion
|
|
2129
2152
|
//#region src/i18n/index.ts
|
|
@@ -2324,6 +2347,9 @@ const tables = {
|
|
|
2324
2347
|
"dashboard.pages.empty": "attach된 페이지 없음",
|
|
2325
2348
|
"dashboard.url.copy": "복사",
|
|
2326
2349
|
"dashboard.url.copied": "복사됨",
|
|
2350
|
+
"dashboard.inspector.section": "인스펙터",
|
|
2351
|
+
"dashboard.inspector.open": "인스펙터 열기",
|
|
2352
|
+
"dashboard.inspector.waiting": "인스펙터 URL 대기 중 (페이지 attach 후 표시됩니다)",
|
|
2327
2353
|
"attach.title": "AIT 디버그 세션 — QR 스캔",
|
|
2328
2354
|
"attach.deployment": "deployment: {label}",
|
|
2329
2355
|
"attach.steps.section": "스캔 절차",
|
|
@@ -2354,7 +2380,6 @@ const tables = {
|
|
|
2354
2380
|
"launcher.urlPlaceholder": "https://example.trycloudflare.com",
|
|
2355
2381
|
"launcher.openBtn": "Open",
|
|
2356
2382
|
"launcher.scanBtn": "QR 카메라로 스캔",
|
|
2357
|
-
"launcher.rescanBtn": "Rescan",
|
|
2358
2383
|
"launcher.noCamera": "카메라를 사용할 수 없습니다 — URL을 직접 붙여넣으세요.",
|
|
2359
2384
|
"launcher.cameraError": "카메라에 접근할 수 없습니다 — URL을 직접 붙여넣으세요.",
|
|
2360
2385
|
"launcher.invalidUrlHttps": "올바른 https:// URL을 입력하세요 (터미널의 터널 URL).",
|
|
@@ -2363,11 +2388,16 @@ const tables = {
|
|
|
2363
2388
|
"launcher.debugAuthFailedHint": "QR 코드가 만료되었을 수 있어요. 새 QR을 다시 스캔하세요.",
|
|
2364
2389
|
"launcher.debugAuthExpiredHint": "디버그 세션이 만료됐어요. Mac의 attach 페이지에서 새 QR을 스캔하세요.",
|
|
2365
2390
|
"launcher.debugAuthRescanCta": "새 QR 스캔하기",
|
|
2366
|
-
"launcher.diagFab": "진단",
|
|
2367
2391
|
"launcher.diagTitle": "뷰포트 진단",
|
|
2368
2392
|
"launcher.diagYes": "예",
|
|
2369
2393
|
"launcher.diagNo": "아니요",
|
|
2370
|
-
"launcher.letterboxDetected": "표시 영역이 {pt}pt 부족합니다 — iOS standalone letterbox로 보입니다. 런처를 홈 화면에서 제거 후 다시 설치하면 해소될 수 있어요."
|
|
2394
|
+
"launcher.letterboxDetected": "표시 영역이 {pt}pt 부족합니다 — iOS standalone letterbox로 보입니다. 런처를 홈 화면에서 제거 후 다시 설치하면 해소될 수 있어요.",
|
|
2395
|
+
"launcher.navbar.defaultTitle": "미니앱",
|
|
2396
|
+
"launcher.navbar.menu": "메뉴",
|
|
2397
|
+
"launcher.navbar.close": "닫기",
|
|
2398
|
+
"launcher.navbar.menuRescan": "다시 스캔",
|
|
2399
|
+
"launcher.navbar.menuDiag": "뷰포트 진단",
|
|
2400
|
+
"launcher.navbar.menuLanguage": "언어"
|
|
2371
2401
|
},
|
|
2372
2402
|
en
|
|
2373
2403
|
};
|
|
@@ -2455,6 +2485,14 @@ img.qr {
|
|
|
2455
2485
|
}
|
|
2456
2486
|
.copy-btn:hover { background: #30363d; }
|
|
2457
2487
|
.hint { font-size: 0.85rem; opacity: 0.5; margin: 0.25rem 0 0; }
|
|
2488
|
+
.inspector-link {
|
|
2489
|
+
display: inline-block; margin-top: 0.5rem;
|
|
2490
|
+
padding: 0.45rem 1rem; border-radius: 6px;
|
|
2491
|
+
background: #1f6feb; color: #fff; font-size: 0.85rem; font-weight: 600;
|
|
2492
|
+
text-decoration: none; text-align: center;
|
|
2493
|
+
}
|
|
2494
|
+
.inspector-link:hover { background: #388bfd; }
|
|
2495
|
+
.inspector-hint { display: inline-block; margin-top: 0.5rem; font-size: 0.8rem; opacity: 0.45; }
|
|
2458
2496
|
ul { margin: 0; padding-left: 1.25rem; }
|
|
2459
2497
|
li { margin-bottom: 0.35rem; font-size: 0.85rem; line-height: 1.5; }
|
|
2460
2498
|
li.empty { opacity: 0.4; list-style: none; padding-left: 0; }
|
|
@@ -2464,7 +2502,7 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0; }
|
|
|
2464
2502
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2465
2503
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2466
2504
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2467
|
-
</style></head><body><h1>AIT 디버그 Dashboard</h1>__LANG_SWITCHER__<p class="updated" id="updated">마지막 갱신: __NOW__</p><section><h2>터널 상태</h2><span class="status __TUNNEL_CLASS__" id="tunnel-status">__TUNNEL_STATUS__</span></section><hr/><section><h2>Attach QR</h2><div id="attach-section">__ATTACH_SECTION__</div></section>__PAGES_SECTION__</body></html>`;
|
|
2505
|
+
</style></head><body><h1>AIT 디버그 Dashboard</h1>__LANG_SWITCHER__<p class="updated" id="updated">마지막 갱신: __NOW__</p><section><h2>터널 상태</h2><span class="status __TUNNEL_CLASS__" id="tunnel-status">__TUNNEL_STATUS__</span></section><hr/><section><h2>Attach QR</h2><div id="attach-section">__ATTACH_SECTION__</div></section><hr/><section id="inspector-section"><h2>인스펙터</h2>__INSPECTOR_SECTION__</section>__PAGES_SECTION__</body></html>`;
|
|
2468
2506
|
const attachChromeHtmlKoSandbox = `<!DOCTYPE html>
|
|
2469
2507
|
<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>
|
|
2470
2508
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -2607,6 +2645,14 @@ img.qr {
|
|
|
2607
2645
|
}
|
|
2608
2646
|
.copy-btn:hover { background: #30363d; }
|
|
2609
2647
|
.hint { font-size: 0.85rem; opacity: 0.5; margin: 0.25rem 0 0; }
|
|
2648
|
+
.inspector-link {
|
|
2649
|
+
display: inline-block; margin-top: 0.5rem;
|
|
2650
|
+
padding: 0.45rem 1rem; border-radius: 6px;
|
|
2651
|
+
background: #1f6feb; color: #fff; font-size: 0.85rem; font-weight: 600;
|
|
2652
|
+
text-decoration: none; text-align: center;
|
|
2653
|
+
}
|
|
2654
|
+
.inspector-link:hover { background: #388bfd; }
|
|
2655
|
+
.inspector-hint { display: inline-block; margin-top: 0.5rem; font-size: 0.8rem; opacity: 0.45; }
|
|
2610
2656
|
ul { margin: 0; padding-left: 1.25rem; }
|
|
2611
2657
|
li { margin-bottom: 0.35rem; font-size: 0.85rem; line-height: 1.5; }
|
|
2612
2658
|
li.empty { opacity: 0.4; list-style: none; padding-left: 0; }
|
|
@@ -2616,7 +2662,7 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0; }
|
|
|
2616
2662
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2617
2663
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2618
2664
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2619
|
-
</style></head><body><h1>AIT Debug Dashboard</h1>__LANG_SWITCHER__<p class="updated" id="updated">Last updated: __NOW__</p><section><h2>Tunnel status</h2><span class="status __TUNNEL_CLASS__" id="tunnel-status">__TUNNEL_STATUS__</span></section><hr/><section><h2>Attach QR</h2><div id="attach-section">__ATTACH_SECTION__</div></section>__PAGES_SECTION__</body></html>`;
|
|
2665
|
+
</style></head><body><h1>AIT Debug Dashboard</h1>__LANG_SWITCHER__<p class="updated" id="updated">Last updated: __NOW__</p><section><h2>Tunnel status</h2><span class="status __TUNNEL_CLASS__" id="tunnel-status">__TUNNEL_STATUS__</span></section><hr/><section><h2>Attach QR</h2><div id="attach-section">__ATTACH_SECTION__</div></section><hr/><section id="inspector-section"><h2>Inspector</h2>__INSPECTOR_SECTION__</section>__PAGES_SECTION__</body></html>`;
|
|
2620
2666
|
const attachChromeHtmlEnSandbox = `<!DOCTYPE html>
|
|
2621
2667
|
<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>
|
|
2622
2668
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -2793,13 +2839,15 @@ function buildLangSwitcher(path, existingParams, locale, s) {
|
|
|
2793
2839
|
*
|
|
2794
2840
|
* 동적 파트 분류:
|
|
2795
2841
|
* - "token-fill": 단일 값 교체 (__NOW__, __TUNNEL_CLASS__, __TUNNEL_STATUS__,
|
|
2796
|
-
* __ATTACH_SECTION__)
|
|
2842
|
+
* __ATTACH_SECTION__, __INSPECTOR_SECTION__)
|
|
2797
2843
|
* - "runtime builder": 가변 길이 구조 (__PAGES_SECTION__ — 조건부 렌더 + 가변 rows)
|
|
2798
2844
|
* - "suffix": inline SSE <script> (빌드 파이프라인 없는 클라이언트 스크립트, locale
|
|
2799
2845
|
* aware 문자열 포함)
|
|
2800
2846
|
*
|
|
2801
2847
|
* SECRET-HANDLING:
|
|
2802
2848
|
* - attachUrl은 url-box 안에서만 노출 (TOTP at= 코드 캡슐 그대로).
|
|
2849
|
+
* - inspectorUrl은 anchor href 안에서만 노출 (TOTP at= 코드 캡슐 그대로).
|
|
2850
|
+
* relay host + TOTP 코드가 담길 수 있으나 대시보드 HTML은 의도된 transport.
|
|
2803
2851
|
* - tunnel wssUrl은 "터널 연결됨" 상태 표시에서 UP/DOWN만 노출.
|
|
2804
2852
|
* wssUrl 값 자체는 dashboard HTML에 넣지 않는다.
|
|
2805
2853
|
*/
|
|
@@ -2814,6 +2862,9 @@ function buildDashboardHtml(state, qrDataUrl, locale, path = "/", params = new U
|
|
|
2814
2862
|
const copyLabel = escapeHtml(s("dashboard.url.copy"));
|
|
2815
2863
|
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>`;
|
|
2816
2864
|
} else attachSection = `<p class="hint">${escapeHtml(s("dashboard.attach.hint"))}</p>`;
|
|
2865
|
+
let inspectorSection;
|
|
2866
|
+
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>`;
|
|
2867
|
+
else inspectorSection = `<span class="inspector-hint" id="inspector-link">${escapeHtml(s("dashboard.inspector.waiting"))}</span>`;
|
|
2817
2868
|
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) => {
|
|
2818
2869
|
return `<li><span class="page-id">${escapeHtml(p.id)}</span> <span class="page-url">${escapeHtml(p.url.slice(0, 120))}</span></li>`;
|
|
2819
2870
|
}).join("\n") : `<li class="empty">${escapeHtml(s("dashboard.pages.empty"))}</li>`}</ul></section>`;
|
|
@@ -2824,10 +2875,12 @@ function buildDashboardHtml(state, qrDataUrl, locale, path = "/", params = new U
|
|
|
2824
2875
|
attachHint: JSON.stringify(s("dashboard.attach.hint")),
|
|
2825
2876
|
copyLabel: JSON.stringify(s("dashboard.url.copy")),
|
|
2826
2877
|
copiedLabel: JSON.stringify(s("dashboard.url.copied")),
|
|
2878
|
+
inspectorOpenLabel: JSON.stringify(s("dashboard.inspector.open")),
|
|
2879
|
+
inspectorWaitingLabel: JSON.stringify(s("dashboard.inspector.waiting")),
|
|
2827
2880
|
dashboardSurface: true
|
|
2828
2881
|
};
|
|
2829
2882
|
const langSwitcher = buildLangSwitcher(path, params, locale, s);
|
|
2830
|
-
const filled = dashboardChromeByLocale[locale].replaceAll("__LANG_SWITCHER__", langSwitcher).replaceAll("__NOW__", escapeHtml(now)).replaceAll("__TUNNEL_CLASS__", tunnelClass).replaceAll("__TUNNEL_STATUS__", escapeHtml(tunnelStatus)).replaceAll("__ATTACH_SECTION__", attachSection).replaceAll("__PAGES_SECTION__", pagesSection);
|
|
2883
|
+
const filled = dashboardChromeByLocale[locale].replaceAll("__LANG_SWITCHER__", langSwitcher).replaceAll("__NOW__", escapeHtml(now)).replaceAll("__TUNNEL_CLASS__", tunnelClass).replaceAll("__TUNNEL_STATUS__", escapeHtml(tunnelStatus)).replaceAll("__ATTACH_SECTION__", attachSection).replaceAll("__INSPECTOR_SECTION__", inspectorSection).replaceAll("__PAGES_SECTION__", pagesSection);
|
|
2831
2884
|
const sseScript = buildSseScript(sseStrings);
|
|
2832
2885
|
return filled.replace("</body>", `${sseScript}\n</body>`);
|
|
2833
2886
|
}
|
|
@@ -2865,6 +2918,8 @@ function buildSseScript(strings) {
|
|
|
2865
2918
|
var ATTACH_HINT = ${strings.attachHint};
|
|
2866
2919
|
var COPY_LABEL = ${strings.copyLabel};
|
|
2867
2920
|
var COPIED_LABEL = ${strings.copiedLabel};
|
|
2921
|
+
var INSPECTOR_OPEN_LABEL = ${strings.inspectorOpenLabel};
|
|
2922
|
+
var INSPECTOR_WAITING_LABEL = ${strings.inspectorWaitingLabel};
|
|
2868
2923
|
|
|
2869
2924
|
// ── 클립보드 복사 헬퍼 ────────────────────────────────────────────────
|
|
2870
2925
|
function copyText(text) {
|
|
@@ -2978,6 +3033,17 @@ function buildSseScript(strings) {
|
|
|
2978
3033
|
sec.innerHTML = '<p class=\\"hint\\">' + ATTACH_HINT + '</p>';`}
|
|
2979
3034
|
}
|
|
2980
3035
|
}
|
|
3036
|
+
// 인스펙터 링크 갱신 — #inspector-link (#503).
|
|
3037
|
+
// SECRET-HANDLING: inspectorUrl을 console.log 등으로 출력하지 않는다.
|
|
3038
|
+
var insp = document.getElementById('inspector-link');
|
|
3039
|
+
if (insp) {
|
|
3040
|
+
if (s.inspectorUrl) {
|
|
3041
|
+
var safeInspUrl = String(s.inspectorUrl).slice(0, 2000).replace(/[<>&"']/g, function (c) { return '&#' + c.charCodeAt(0) + ';'; });
|
|
3042
|
+
insp.outerHTML = '<a class=\\"inspector-link\\" id=\\"inspector-link\\" href=\\"' + safeInspUrl + '\\" target=\\"_blank\\" rel=\\"noopener noreferrer\\">' + INSPECTOR_OPEN_LABEL + '</a>';
|
|
3043
|
+
} else {
|
|
3044
|
+
insp.outerHTML = '<span class=\\"inspector-hint\\" id=\\"inspector-link\\">' + INSPECTOR_WAITING_LABEL + '</span>';
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
2981
3047
|
// 갱신 시각 (dashboard만 #updated 요소 있음)
|
|
2982
3048
|
var upd = document.getElementById('updated');
|
|
2983
3049
|
if (upd) upd.textContent = upd.textContent.replace(/[^ ]+$/, new Date().toISOString());
|
|
@@ -3022,6 +3088,8 @@ function buildAttachHtml(qrDataUrl, safeLabel, safeAttachUrl, locale, path = "/a
|
|
|
3022
3088
|
attachHint: JSON.stringify(s("dashboard.attach.hint")),
|
|
3023
3089
|
copyLabel: JSON.stringify(s("dashboard.url.copy")),
|
|
3024
3090
|
copiedLabel: JSON.stringify(s("dashboard.url.copied")),
|
|
3091
|
+
inspectorOpenLabel: JSON.stringify(s("dashboard.inspector.open")),
|
|
3092
|
+
inspectorWaitingLabel: JSON.stringify(s("dashboard.inspector.waiting")),
|
|
3025
3093
|
dashboardSurface: false
|
|
3026
3094
|
});
|
|
3027
3095
|
return filled.replace("</body>", `${sseScript}\n</body>`);
|
|
@@ -3045,7 +3113,8 @@ async function startQrHttpServer(getDashboardState) {
|
|
|
3045
3113
|
wssUrl: state.tunnel.wssUrl
|
|
3046
3114
|
},
|
|
3047
3115
|
pages: state.pages,
|
|
3048
|
-
attachUrl: state.attachUrl
|
|
3116
|
+
attachUrl: state.attachUrl,
|
|
3117
|
+
inspectorUrl: state.inspectorUrl ?? null
|
|
3049
3118
|
});
|
|
3050
3119
|
res.write(`data: ${payload}\n\n`);
|
|
3051
3120
|
}
|
|
@@ -3552,7 +3621,7 @@ const DEBUG_TOOL_DEFINITIONS = [
|
|
|
3552
3621
|
},
|
|
3553
3622
|
{
|
|
3554
3623
|
name: "build_attach_url",
|
|
3555
|
-
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=…). 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. When open_in_browser=true (default), saves the QR as a PNG and opens it in the OS default browser — only works when the MCP server runs on a local GUI machine (not headless/remote containers). \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.",
|
|
3624
|
+
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. When open_in_browser=true (default), saves the QR as a PNG and opens it in the OS default browser — only works when the MCP server runs on a local GUI machine (not headless/remote containers). \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.",
|
|
3556
3625
|
inputSchema: {
|
|
3557
3626
|
type: "object",
|
|
3558
3627
|
properties: {
|
|
@@ -4566,7 +4635,7 @@ async function readMcpSdkVersion() {
|
|
|
4566
4635
|
* some test environments that skip the build step).
|
|
4567
4636
|
*/
|
|
4568
4637
|
function readDevtoolsVersion() {
|
|
4569
|
-
return "0.1.
|
|
4638
|
+
return "0.1.74";
|
|
4570
4639
|
}
|
|
4571
4640
|
/**
|
|
4572
4641
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -5070,7 +5139,7 @@ function createDebugServer(deps) {
|
|
|
5070
5139
|
const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
|
|
5071
5140
|
const server = new Server({
|
|
5072
5141
|
name: "ait-debug",
|
|
5073
|
-
version: "0.1.
|
|
5142
|
+
version: "0.1.74"
|
|
5074
5143
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
5075
5144
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
5076
5145
|
const conn = router.active;
|
|
@@ -5170,11 +5239,20 @@ function createDebugServer(deps) {
|
|
|
5170
5239
|
expiresAt: new Date(expiresAtMs).toISOString()
|
|
5171
5240
|
};
|
|
5172
5241
|
}
|
|
5173
|
-
|
|
5242
|
+
let launcherAppName;
|
|
5243
|
+
if (buildProjectRoot !== void 0) try {
|
|
5244
|
+
const { readFileSync } = await import("node:fs");
|
|
5245
|
+
const pkgRaw = readFileSync(`${buildProjectRoot}/package.json`, "utf8");
|
|
5246
|
+
const pkg = JSON.parse(pkgRaw);
|
|
5247
|
+
const rawName = typeof pkg.name === "string" ? pkg.name : "";
|
|
5248
|
+
launcherAppName = (rawName.includes("/") ? rawName.slice(rawName.indexOf("/") + 1) : rawName).trim() || void 0;
|
|
5249
|
+
} catch {}
|
|
5250
|
+
const attachUrl = buildLauncherAttachUrl(tunnelHttpUrl, tunnelStatus.wssUrl, totpCode, { name: launcherAppName });
|
|
5174
5251
|
onAttachUrlBuilt?.({
|
|
5175
5252
|
kind: "launcher",
|
|
5176
5253
|
tunnelHttpUrl,
|
|
5177
|
-
wssUrl: tunnelStatus.wssUrl
|
|
5254
|
+
wssUrl: tunnelStatus.wssUrl,
|
|
5255
|
+
appName: launcherAppName
|
|
5178
5256
|
});
|
|
5179
5257
|
const relayUrl = tunnelStatus.wssUrl;
|
|
5180
5258
|
const totp = totpMeta;
|
|
@@ -5610,7 +5688,7 @@ function makeSingleConnectionRouter(connection) {
|
|
|
5610
5688
|
function rebuildAttachUrl(parts) {
|
|
5611
5689
|
const secret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
5612
5690
|
const code = secret ? generateTotp(secret) : void 0;
|
|
5613
|
-
return parts.kind === "launcher" ? buildLauncherAttachUrl(parts.tunnelHttpUrl, parts.wssUrl, code) : buildDeepLinkAttachUrl(parts.schemeUrl, parts.wssUrl, code);
|
|
5691
|
+
return parts.kind === "launcher" ? buildLauncherAttachUrl(parts.tunnelHttpUrl, parts.wssUrl, code, { name: parts.appName }) : buildDeepLinkAttachUrl(parts.schemeUrl, parts.wssUrl, code);
|
|
5614
5692
|
}
|
|
5615
5693
|
function jsonResult$1(value) {
|
|
5616
5694
|
return { content: [{
|
|
@@ -5987,6 +6065,15 @@ var DualConnectionRouter = class {
|
|
|
5987
6065
|
get activeRelayOrigin() {
|
|
5988
6066
|
return this.activeFamily?.relayOrigin;
|
|
5989
6067
|
}
|
|
6068
|
+
/**
|
|
6069
|
+
* Local HTTP base URL of the Chii relay for the currently-active family (#503).
|
|
6070
|
+
* Used by `getDashboardState` to build the inspector URL via `buildChiiInspectorUrl`.
|
|
6071
|
+
* Returns `undefined` when no relay family is active (local/mock mode).
|
|
6072
|
+
* SECRET-HANDLING: not logged — callers must not write this to stdout/logs.
|
|
6073
|
+
*/
|
|
6074
|
+
get activeRelayHttpUrl() {
|
|
6075
|
+
return this.activeFamily?.relayHttpUrl;
|
|
6076
|
+
}
|
|
5990
6077
|
/** Every booted family (for unified shutdown). All families are lazy (#396). */
|
|
5991
6078
|
bootedFamilies() {
|
|
5992
6079
|
return [...this.lazyFamilies.values()];
|
|
@@ -6114,18 +6201,25 @@ async function runDebugServer(options = {}) {
|
|
|
6114
6201
|
return router.active;
|
|
6115
6202
|
});
|
|
6116
6203
|
let lastAttachParts = null;
|
|
6117
|
-
const getDashboardState = () =>
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6204
|
+
const getDashboardState = () => {
|
|
6205
|
+
const targets = router.active.listTargets();
|
|
6206
|
+
const relayHttpUrl = router.activeRelayHttpUrl;
|
|
6207
|
+
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6208
|
+
const inspectorUrl = relayHttpUrl && targets.length > 0 ? buildChiiInspectorUrl(relayHttpUrl, targets[0].id, totpSecret ? () => generateTotp(totpSecret, Date.now()) : void 0) : null;
|
|
6209
|
+
return {
|
|
6210
|
+
tunnel: {
|
|
6211
|
+
up: router.relayTunnelStatus().up,
|
|
6212
|
+
wssUrl: router.relayTunnelStatus().wssUrl
|
|
6213
|
+
},
|
|
6214
|
+
pages: targets.map((t) => ({
|
|
6215
|
+
id: t.id,
|
|
6216
|
+
url: t.url
|
|
6217
|
+
})),
|
|
6218
|
+
attachUrl: lastAttachParts ? rebuildAttachUrl(lastAttachParts) : null,
|
|
6219
|
+
inspectorUrl,
|
|
6220
|
+
mode: deriveEnvironment(router.active.kind, getLiveIntent(), router.activeRelayOrigin)
|
|
6221
|
+
};
|
|
6222
|
+
};
|
|
6129
6223
|
let qrServer;
|
|
6130
6224
|
try {
|
|
6131
6225
|
qrServer = await startQrHttpServer(getDashboardState);
|
|
@@ -6280,17 +6374,24 @@ async function runLocalDebugServer(options = {}) {
|
|
|
6280
6374
|
return router.active;
|
|
6281
6375
|
});
|
|
6282
6376
|
let lastAttachParts = null;
|
|
6283
|
-
const getDashboardState = () =>
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6377
|
+
const getDashboardState = () => {
|
|
6378
|
+
const targets = router.active.listTargets();
|
|
6379
|
+
const relayHttpUrl = router.activeRelayHttpUrl;
|
|
6380
|
+
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6381
|
+
const inspectorUrl = relayHttpUrl && targets.length > 0 ? buildChiiInspectorUrl(relayHttpUrl, targets[0].id, totpSecret ? () => generateTotp(totpSecret, Date.now()) : void 0) : null;
|
|
6382
|
+
return {
|
|
6383
|
+
tunnel: {
|
|
6384
|
+
up: router.relayTunnelStatus().up,
|
|
6385
|
+
wssUrl: router.relayTunnelStatus().wssUrl
|
|
6386
|
+
},
|
|
6387
|
+
pages: targets.map((t) => ({
|
|
6388
|
+
id: t.id,
|
|
6389
|
+
url: t.url
|
|
6390
|
+
})),
|
|
6391
|
+
attachUrl: lastAttachParts ? rebuildAttachUrl(lastAttachParts) : null,
|
|
6392
|
+
inspectorUrl
|
|
6393
|
+
};
|
|
6394
|
+
};
|
|
6294
6395
|
let qrServer;
|
|
6295
6396
|
try {
|
|
6296
6397
|
qrServer = await startQrHttpServer(getDashboardState);
|
|
@@ -6429,17 +6530,24 @@ async function runMobileDebugServer(options = {}) {
|
|
|
6429
6530
|
return router.active;
|
|
6430
6531
|
});
|
|
6431
6532
|
let lastAttachParts = null;
|
|
6432
|
-
const getDashboardState = () =>
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6533
|
+
const getDashboardState = () => {
|
|
6534
|
+
const targets = router.active.listTargets();
|
|
6535
|
+
const relayHttpUrl = router.activeRelayHttpUrl;
|
|
6536
|
+
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6537
|
+
const inspectorUrl = relayHttpUrl && targets.length > 0 ? buildChiiInspectorUrl(relayHttpUrl, targets[0].id, totpSecret ? () => generateTotp(totpSecret, Date.now()) : void 0) : null;
|
|
6538
|
+
return {
|
|
6539
|
+
tunnel: {
|
|
6540
|
+
up: router.relayTunnelStatus().up,
|
|
6541
|
+
wssUrl: router.relayTunnelStatus().wssUrl
|
|
6542
|
+
},
|
|
6543
|
+
pages: targets.map((t) => ({
|
|
6544
|
+
id: t.id,
|
|
6545
|
+
url: t.url
|
|
6546
|
+
})),
|
|
6547
|
+
attachUrl: lastAttachParts ? rebuildAttachUrl(lastAttachParts) : null,
|
|
6548
|
+
inspectorUrl
|
|
6549
|
+
};
|
|
6550
|
+
};
|
|
6443
6551
|
let qrServer;
|
|
6444
6552
|
try {
|
|
6445
6553
|
qrServer = await startQrHttpServer(getDashboardState);
|
|
@@ -6957,7 +7065,7 @@ function createDevServer(deps = {}) {
|
|
|
6957
7065
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
6958
7066
|
const server = new Server({
|
|
6959
7067
|
name: "ait-devtools",
|
|
6960
|
-
version: "0.1.
|
|
7068
|
+
version: "0.1.74"
|
|
6961
7069
|
}, { capabilities: { tools: {} } });
|
|
6962
7070
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
6963
7071
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|