@ait-co/devtools 0.1.129 → 0.1.131
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/{attach-orchestrator-DvrgUIx7.js → attach-orchestrator-BiaYiNOr.js} +31 -13
- package/dist/{attach-orchestrator-DvrgUIx7.js.map → attach-orchestrator-BiaYiNOr.js.map} +1 -1
- package/dist/{attach-orchestrator-RhOdO3DS.js → attach-orchestrator-Cjdhir2U.js} +31 -13
- package/dist/{attach-orchestrator-RhOdO3DS.js.map → attach-orchestrator-Cjdhir2U.js.map} +1 -1
- package/dist/{attach-orchestrator-Bdt5Zdo9.js → attach-orchestrator-ca34r46y.js} +31 -13
- package/dist/{attach-orchestrator-Bdt5Zdo9.js.map → attach-orchestrator-ca34r46y.js.map} +1 -1
- package/dist/{cell-Ba9lCI3B.js → cell-Bi8MAPA8.js} +11 -7
- package/dist/cell-Bi8MAPA8.js.map +1 -0
- package/dist/{cell-DA_lcpDI.js → cell-Cw4fVOl2.js} +11 -7
- package/dist/cell-Cw4fVOl2.js.map +1 -0
- package/dist/{cell-DLRv4iG6.js → cell-rr6gL9AT.js} +11 -7
- package/dist/cell-rr6gL9AT.js.map +1 -0
- package/dist/{debug-server-Bu3pCG9c.js → debug-server-BSbfnFR5.js} +9 -9
- package/dist/debug-server-BSbfnFR5.js.map +1 -0
- package/dist/{debug-server-e1qPe0ZB.js → debug-server-CD4zYBq4.js} +4 -4
- package/dist/{debug-server-e1qPe0ZB.js.map → debug-server-CD4zYBq4.js.map} +1 -1
- package/dist/{debug-server--Y0QZVJS.js → debug-server-qRSRG7YZ.js} +15 -11
- package/dist/debug-server-qRSRG7YZ.js.map +1 -0
- package/dist/in-app/auto.js +15 -0
- package/dist/in-app/auto.js.map +1 -1
- package/dist/in-app/index.d.ts +27 -0
- package/dist/in-app/index.d.ts.map +1 -1
- package/dist/in-app/index.js +15 -0
- package/dist/in-app/index.js.map +1 -1
- package/dist/mcp/cli.js +143 -35
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/panel/index.js +11 -1
- package/dist/panel/index.js.map +1 -1
- package/dist/{pool-C0pc7T2p.d.ts → pool-BaSCZALv.d.ts} +9 -1
- package/dist/{pool-C0pc7T2p.d.ts.map → pool-BaSCZALv.d.ts.map} +1 -1
- package/dist/{qr-http-server-HANhOFwI.js → qr-http-server-BdNGPRoL.js} +76 -6
- package/dist/qr-http-server-BdNGPRoL.js.map +1 -0
- package/dist/{qr-http-server-CrGohOwC.js → qr-http-server-Bq9zR7-z.js} +76 -6
- package/dist/qr-http-server-Bq9zR7-z.js.map +1 -0
- package/dist/{qr-http-server-DM2R25jP.cjs → qr-http-server-BxoRp2w7.cjs} +76 -6
- package/dist/qr-http-server-BxoRp2w7.cjs.map +1 -0
- package/dist/{qr-http-server-Bt_RAxFn.cjs → qr-http-server-CV6TA9ZN.cjs} +76 -6
- package/dist/qr-http-server-CV6TA9ZN.cjs.map +1 -0
- package/dist/{qr-http-server-s1iW_5VQ.js → qr-http-server-DCbjORFa.js} +76 -6
- package/dist/qr-http-server-DCbjORFa.js.map +1 -0
- package/dist/{qr-http-server-BH0UFeUn.js → qr-http-server-cKbciY5k.js} +76 -6
- package/dist/qr-http-server-cKbciY5k.js.map +1 -0
- package/dist/{relay-factory-Ck3vBonr.js → relay-factory-Cloog1jb.js} +33 -13
- package/dist/relay-factory-Cloog1jb.js.map +1 -0
- package/dist/{relay-worker-Bt0QYKQH.js → relay-worker-CCAHLC4a.js} +9 -5
- package/dist/relay-worker-CCAHLC4a.js.map +1 -0
- package/dist/test-runner/bin.js +45 -19
- package/dist/test-runner/bin.js.map +1 -1
- package/dist/test-runner/config.d.ts +15 -9
- package/dist/test-runner/config.d.ts.map +1 -1
- package/dist/test-runner/config.js +1 -1
- package/dist/test-runner/pool.d.ts +1 -1
- package/dist/test-runner/pool.js +1 -1
- package/dist/test-runner/pool.js.map +1 -1
- package/dist/test-runner/relay-factory.d.ts +22 -8
- package/dist/test-runner/relay-factory.d.ts.map +1 -1
- package/dist/test-runner/relay-factory.js +32 -12
- package/dist/test-runner/relay-factory.js.map +1 -1
- package/dist/test-runner/relay-worker.js +1 -1
- package/dist/{tunnel-DyEV6WwL.cjs → tunnel-B2UCKAc7.cjs} +4 -3
- package/dist/{tunnel-DyEV6WwL.cjs.map → tunnel-B2UCKAc7.cjs.map} +1 -1
- package/dist/{tunnel-BFwOSwT4.js → tunnel-C-rQ34dM.js} +4 -3
- package/dist/{tunnel-BFwOSwT4.js.map → tunnel-C-rQ34dM.js.map} +1 -1
- package/dist/unplugin/index.cjs +1 -1
- package/dist/unplugin/index.js +1 -1
- package/dist/unplugin/tunnel.cjs +3 -2
- package/dist/unplugin/tunnel.cjs.map +1 -1
- package/dist/unplugin/tunnel.d.cts.map +1 -1
- package/dist/unplugin/tunnel.d.ts.map +1 -1
- package/dist/unplugin/tunnel.js +3 -2
- package/dist/unplugin/tunnel.js.map +1 -1
- package/package.json +1 -1
- package/dist/cell-Ba9lCI3B.js.map +0 -1
- package/dist/cell-DA_lcpDI.js.map +0 -1
- package/dist/cell-DLRv4iG6.js.map +0 -1
- package/dist/debug-server--Y0QZVJS.js.map +0 -1
- package/dist/debug-server-Bu3pCG9c.js.map +0 -1
- package/dist/qr-http-server-BH0UFeUn.js.map +0 -1
- package/dist/qr-http-server-Bt_RAxFn.cjs.map +0 -1
- package/dist/qr-http-server-CrGohOwC.js.map +0 -1
- package/dist/qr-http-server-DM2R25jP.cjs.map +0 -1
- package/dist/qr-http-server-HANhOFwI.js.map +0 -1
- package/dist/qr-http-server-s1iW_5VQ.js.map +0 -1
- package/dist/relay-factory-Ck3vBonr.js.map +0 -1
- package/dist/relay-worker-Bt0QYKQH.js.map +0 -1
package/dist/mcp/cli.js
CHANGED
|
@@ -2024,7 +2024,7 @@ async function readMcpSdkVersion() {
|
|
|
2024
2024
|
* some test environments that skip the build step).
|
|
2025
2025
|
*/
|
|
2026
2026
|
function readDevtoolsVersion() {
|
|
2027
|
-
return "0.1.
|
|
2027
|
+
return "0.1.131";
|
|
2028
2028
|
}
|
|
2029
2029
|
/**
|
|
2030
2030
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -2864,32 +2864,50 @@ async function renderAndMaybeWait(deps, prep, waitForAttach, callTimeoutMs, conn
|
|
|
2864
2864
|
}, null, 2)}\n\n${qr}`);
|
|
2865
2865
|
}
|
|
2866
2866
|
/**
|
|
2867
|
-
* Builds a self-contained IIFE DOM expression that renders a
|
|
2868
|
-
* "Debugger Connected" badge on the bottom-left of
|
|
2867
|
+
* Builds a self-contained IIFE DOM expression that renders a LIVE
|
|
2868
|
+
* "Debugger Connected" / "Debugger Disconnected" badge on the bottom-left of
|
|
2869
|
+
* the phone screen (#730).
|
|
2869
2870
|
*
|
|
2870
2871
|
* **Pure function** — returns a JS expression string; does NOT inject it.
|
|
2871
2872
|
* Injection is performed by {@link injectDebugIndicator} in `cell.ts`.
|
|
2872
2873
|
*
|
|
2873
2874
|
* The expression, when evaluated on the page, does the following:
|
|
2874
|
-
* 1.
|
|
2875
|
-
*
|
|
2876
|
-
*
|
|
2877
|
-
*
|
|
2878
|
-
*
|
|
2879
|
-
*
|
|
2875
|
+
* 1. Idempotent controller — a single `window.__ait_indicator` controller
|
|
2876
|
+
* object is created once (keyed on its presence, not on the DOM node).
|
|
2877
|
+
* Re-injection (e.g. explicit disconnect notice from `close()`) UPDATES
|
|
2878
|
+
* the existing badge's state/label in place — it never duplicates the
|
|
2879
|
+
* `<div>` or stacks a second observer.
|
|
2880
|
+
* 2. Renders a fixed-position `<div id="__ait_debug_indicator">` at the
|
|
2881
|
+
* bottom-left, accounting for safe-area insets. Background/text flip
|
|
2882
|
+
* between the attached (red) and disconnected (grey) visuals.
|
|
2883
|
+
* 3. A `pointerdown` listener dismisses (hides) the badge on tap — dismiss
|
|
2884
|
+
* is NON-terminal: a later state transition un-dismisses it, so a
|
|
2885
|
+
* genuine disconnect after a dismissed tap is still visible.
|
|
2886
|
+
* 4. Observes relay-socket lifecycle WITHOUT opening any new connection:
|
|
2887
|
+
* - Preferred path: if the in-app module (`src/in-app/attach.ts`) has
|
|
2888
|
+
* already installed its relay-WS observer (`window.__ait_relay_ws_observed`),
|
|
2889
|
+
* subscribe to the `ait:relay-ws-state` CustomEvent it broadcasts.
|
|
2890
|
+
* - Fallback path: wrap `window.WebSocket` in a `Proxy` and match dials
|
|
2891
|
+
* by PATHNAME SHAPE only (`/target/`) — never by host/wss value — so a
|
|
2892
|
+
* bare CDP-injected badge (no in-app bundle) still reacts to the
|
|
2893
|
+
* chii target socket's own open/close lifecycle.
|
|
2880
2894
|
*
|
|
2881
2895
|
* The expression intentionally contains NO relay URLs, wss addresses, TOTP
|
|
2882
|
-
* codes, or any other secrets. It is pure DOM UI text only.
|
|
2896
|
+
* codes, or any other secrets. It is pure DOM UI text + enum state only.
|
|
2883
2897
|
*
|
|
2884
2898
|
* SECRET-HANDLING: this expression contains no secrets, relay URLs, wss
|
|
2885
|
-
* addresses, or TOTP codes whatsoever — DOM label text
|
|
2899
|
+
* addresses, or TOTP codes whatsoever — DOM label text + a structural
|
|
2900
|
+
* pathname match (`/target/`) only.
|
|
2886
2901
|
*
|
|
2887
|
-
* @param opts.label -
|
|
2902
|
+
* @param opts.label - Attached-state badge text (default: `'Debugger Connected'`).
|
|
2903
|
+
* @param opts.disconnectedLabel - Disconnected-state badge text (default: `'Debugger Disconnected'`).
|
|
2904
|
+
* @param opts.state - Initial/forced state for THIS injection call (default: `'attached'`).
|
|
2888
2905
|
* @returns A JS expression string suitable for `Runtime.evaluate`.
|
|
2889
2906
|
*/
|
|
2890
2907
|
function buildIndicatorExpression(opts) {
|
|
2891
2908
|
const label = opts?.label ?? "Debugger Connected";
|
|
2892
|
-
|
|
2909
|
+
const disconnectedLabel = opts?.disconnectedLabel ?? "Debugger Disconnected";
|
|
2910
|
+
return `(() => {var W = window;function render(c) {if (c.dismissed) { c.el.style.display = 'none'; return; }c.el.style.display = 'block';var up = c.state !== 'disconnected';c.el.style.background = up ? '#e5484d' : '#8a8f98';c.el.textContent = up ? ${JSON.stringify(label)} : ${JSON.stringify(disconnectedLabel)};}function setState(c, next) { c.state = next; c.dismissed = false; render(c); }var c = W.__ait_indicator;if (!c) {var el = document.createElement('div');el.id = '__ait_debug_indicator';el.style.cssText = ['position:fixed','left:max(12px,calc(env(safe-area-inset-left,0px) + 8px))','bottom:max(12px,calc(env(safe-area-inset-bottom,0px) + 8px))','z-index:2147483647','color:#fff','font:bold 11px/1 system-ui,sans-serif','padding:5px 9px','border-radius:6px','pointer-events:auto','user-select:none',].join(';');document.body.appendChild(el);c = { el: el, state: 'attached', dismissed: false };el.addEventListener('pointerdown', function () { c.dismissed = true; render(c); }, { passive: true });W.__ait_indicator = c;if (W.__ait_relay_ws_observed) {W.addEventListener('ait:relay-ws-state', function (e) {setState(c, e.detail && e.detail.state === 'open' ? 'attached' : 'disconnected');});} else {try {var Native = W.WebSocket;W.WebSocket = new Proxy(Native, {construct: function (t, a) {var ws = Reflect.construct(t, a);try {var u = new URL(String(a[0]), location.href);if (/\\/target\\//.test(u.pathname)) {ws.addEventListener('open', function () { setState(c, 'attached'); });ws.addEventListener('close', function () { setState(c, 'disconnected'); });ws.addEventListener('error', function () { setState(c, 'disconnected'); });}} catch (_) {}return ws;},});} catch (_) {}}}setState(c, ${JSON.stringify(opts?.state ?? "attached")});})()`;
|
|
2893
2911
|
}
|
|
2894
2912
|
//#endregion
|
|
2895
2913
|
//#region src/test-runner/cell.ts
|
|
@@ -2935,12 +2953,16 @@ async function injectGlobals(conn, globals) {
|
|
|
2935
2953
|
}
|
|
2936
2954
|
}
|
|
2937
2955
|
/**
|
|
2938
|
-
* Injects the "Debugger Connected"
|
|
2956
|
+
* Injects (or updates) the "Debugger Connected"/"Debugger Disconnected"
|
|
2957
|
+
* on-phone indicator via `Runtime.evaluate` (#730).
|
|
2939
2958
|
*
|
|
2940
2959
|
* Uses the same CDP mechanism as {@link injectGlobals} — a single
|
|
2941
2960
|
* `Runtime.evaluate` round-trip with the expression built by
|
|
2942
|
-
* {@link buildIndicatorExpression}. The indicator is a
|
|
2943
|
-
*
|
|
2961
|
+
* {@link buildIndicatorExpression}. The indicator is a LIVE badge rendered at
|
|
2962
|
+
* the bottom-left of the page (position:fixed, safe-area-aware): the badge's
|
|
2963
|
+
* controller is idempotent, so calling this again (e.g. right before
|
|
2964
|
+
* `close()` with `{ state: 'disconnected' }`) UPDATES the existing badge in
|
|
2965
|
+
* place instead of injecting a duplicate.
|
|
2944
2966
|
*
|
|
2945
2967
|
* **Isolation**: unlike {@link injectGlobals}, this function NEVER throws to
|
|
2946
2968
|
* its caller. Injection failure (e.g. page detached during inject, CSS not
|
|
@@ -2950,12 +2972,12 @@ async function injectGlobals(conn, globals) {
|
|
|
2950
2972
|
* `src/in-app/attach.ts`.
|
|
2951
2973
|
*
|
|
2952
2974
|
* Call this ONLY on the manual debug paths (start_attach MCP, devtools-test
|
|
2953
|
-
* CLI). Do NOT call on the `run_tests` auto-attach path — the
|
|
2975
|
+
* CLI). Do NOT call on the `run_tests` auto-attach path — the badge
|
|
2954
2976
|
* would contaminate screenshots, measure_safe_area probes, and DOM snapshots
|
|
2955
2977
|
* taken during automated measurement runs.
|
|
2956
2978
|
*
|
|
2957
2979
|
* SECRET-HANDLING: the injected expression contains no secrets, relay URLs,
|
|
2958
|
-
* wss addresses, or TOTP codes — it is pure DOM UI text built by
|
|
2980
|
+
* wss addresses, or TOTP codes — it is pure DOM UI text + enum state built by
|
|
2959
2981
|
* {@link buildIndicatorExpression}.
|
|
2960
2982
|
*
|
|
2961
2983
|
* @param conn - The live CDP connection (relay-attached page).
|
|
@@ -3235,7 +3257,11 @@ var ChiiCdpConnection = class {
|
|
|
3235
3257
|
* EventEmitter is missed (defensive belt-and-suspenders).
|
|
3236
3258
|
*
|
|
3237
3259
|
* @param filterFn - Predicate that the returned targets must satisfy.
|
|
3238
|
-
* @param timeoutMs - Reject after this many ms (default 90 000).
|
|
3260
|
+
* @param timeoutMs - Reject after this many ms (default 90 000). Pass a
|
|
3261
|
+
* non-finite value (`Infinity`) to disable the rejection timer entirely —
|
|
3262
|
+
* used by the test-runner's unbounded QR-attach wait (devtools#735). Node
|
|
3263
|
+
* clamps `setTimeout(fn, Infinity)` to ~1ms, so a non-finite value MUST
|
|
3264
|
+
* skip arming the timer rather than pass it through.
|
|
3239
3265
|
* @param pollIntervalMs - Fallback poll interval (default 500ms).
|
|
3240
3266
|
*/
|
|
3241
3267
|
waitForFirstTarget(filterFn, timeoutMs = 9e4, pollIntervalMs = 500) {
|
|
@@ -3247,7 +3273,7 @@ var ChiiCdpConnection = class {
|
|
|
3247
3273
|
const settle = (targets) => {
|
|
3248
3274
|
if (settled) return;
|
|
3249
3275
|
settled = true;
|
|
3250
|
-
clearTimeout(timeoutHandle);
|
|
3276
|
+
if (timeoutHandle !== null) clearTimeout(timeoutHandle);
|
|
3251
3277
|
if (pollHandle !== null) {
|
|
3252
3278
|
clearInterval(pollHandle);
|
|
3253
3279
|
pollHandle = null;
|
|
@@ -3258,7 +3284,7 @@ var ChiiCdpConnection = class {
|
|
|
3258
3284
|
const onAttach = (targets) => {
|
|
3259
3285
|
if (filterFn(targets)) settle(targets);
|
|
3260
3286
|
};
|
|
3261
|
-
const timeoutHandle = setTimeout(() => {
|
|
3287
|
+
const timeoutHandle = Number.isFinite(timeoutMs) ? setTimeout(() => {
|
|
3262
3288
|
if (settled) return;
|
|
3263
3289
|
settled = true;
|
|
3264
3290
|
if (pollHandle !== null) {
|
|
@@ -3267,7 +3293,7 @@ var ChiiCdpConnection = class {
|
|
|
3267
3293
|
}
|
|
3268
3294
|
this.emitter.off("target:attached", onAttach);
|
|
3269
3295
|
reject(/* @__PURE__ */ new Error(`waitForFirstTarget: 타임아웃 (${timeoutMs}ms) — 폰이 relay에 attach되지 않았습니다.`));
|
|
3270
|
-
}, timeoutMs);
|
|
3296
|
+
}, timeoutMs) : null;
|
|
3271
3297
|
this.emitter.on("target:attached", onAttach);
|
|
3272
3298
|
pollHandle = setInterval(() => {
|
|
3273
3299
|
this.refreshTargets().then((targets) => {
|
|
@@ -4252,9 +4278,9 @@ OPTIONS
|
|
|
4252
4278
|
the CLI waits for a human to scan the QR code — use
|
|
4253
4279
|
--attach-timeout for that.
|
|
4254
4280
|
--attach-timeout <ms> How long to wait for a human to scan the QR code with
|
|
4255
|
-
their phone (default
|
|
4256
|
-
|
|
4257
|
-
|
|
4281
|
+
their phone. Omit (default) to wait indefinitely — the
|
|
4282
|
+
runner stays up until you stop it (Ctrl-C/SIGTERM).
|
|
4283
|
+
Pass a value to bound the wait for CI/headless runs.
|
|
4258
4284
|
--cell-sdk-line <line> SDK line to inject as __AIT_CELL__.sdkLine (2.x|3.x)
|
|
4259
4285
|
--cell-platform <plat> Platform to inject as __AIT_CELL__.platform
|
|
4260
4286
|
(mock|ios|android, default: AIT_CELL_PLATFORM env)
|
|
@@ -5397,6 +5423,11 @@ const en = {
|
|
|
5397
5423
|
"dashboard.watchdog.title": "Server has shut down",
|
|
5398
5424
|
"dashboard.watchdog.body": "The MCP server has stopped. This session is no longer active. You may close this tab.",
|
|
5399
5425
|
"dashboard.watchdog.close": "Close tab",
|
|
5426
|
+
"dashboard.conn.lost": "Connection lost — reconnecting…",
|
|
5427
|
+
"dashboard.session.completeTitle": "Test run complete",
|
|
5428
|
+
"dashboard.session.completeBody": "The runner exited cleanly. See your terminal for results.",
|
|
5429
|
+
"dashboard.session.shutdownTitle": "Debug server stopped",
|
|
5430
|
+
"dashboard.session.shutdownBody": "The MCP server has shut down; this session is no longer live. You can close this tab.",
|
|
5400
5431
|
"attach.title": "AIT Debug Session — QR Scan",
|
|
5401
5432
|
"attach.deployment": "deployment: {label}",
|
|
5402
5433
|
"attach.steps.section": "How to scan",
|
|
@@ -5635,6 +5666,11 @@ const tables = {
|
|
|
5635
5666
|
"dashboard.watchdog.title": "서버가 종료되었습니다",
|
|
5636
5667
|
"dashboard.watchdog.body": "MCP server가 종료되어 이 세션이 더 이상 유효하지 않습니다. 이 탭을 닫아도 됩니다.",
|
|
5637
5668
|
"dashboard.watchdog.close": "탭 닫기",
|
|
5669
|
+
"dashboard.conn.lost": "연결 끊김 — 재연결 시도 중…",
|
|
5670
|
+
"dashboard.session.completeTitle": "테스트 실행 완료",
|
|
5671
|
+
"dashboard.session.completeBody": "러너가 정상 종료되었습니다. 결과는 터미널에서 확인하세요.",
|
|
5672
|
+
"dashboard.session.shutdownTitle": "디버그 서버가 종료되었습니다",
|
|
5673
|
+
"dashboard.session.shutdownBody": "MCP server가 종료되어 이 세션이 더 이상 유효하지 않습니다. 이 탭을 닫아도 됩니다.",
|
|
5638
5674
|
"attach.title": "AIT 디버그 세션 — QR 스캔",
|
|
5639
5675
|
"attach.deployment": "deployment: {label}",
|
|
5640
5676
|
"attach.steps.section": "스캔 절차",
|
|
@@ -6216,7 +6252,12 @@ function buildDashboardHtml(state, qrDataUrl, locale, path = "/", params = new U
|
|
|
6216
6252
|
watchdogTimeoutMs,
|
|
6217
6253
|
watchdogTitle: JSON.stringify(s("dashboard.watchdog.title")),
|
|
6218
6254
|
watchdogBody: JSON.stringify(s("dashboard.watchdog.body")),
|
|
6219
|
-
watchdogCloseLabel: JSON.stringify(s("dashboard.watchdog.close"))
|
|
6255
|
+
watchdogCloseLabel: JSON.stringify(s("dashboard.watchdog.close")),
|
|
6256
|
+
connectionLost: JSON.stringify(s("dashboard.conn.lost")),
|
|
6257
|
+
completeTitle: JSON.stringify(s("dashboard.session.completeTitle")),
|
|
6258
|
+
completeBody: JSON.stringify(s("dashboard.session.completeBody")),
|
|
6259
|
+
shutdownTitle: JSON.stringify(s("dashboard.session.shutdownTitle")),
|
|
6260
|
+
shutdownBody: JSON.stringify(s("dashboard.session.shutdownBody"))
|
|
6220
6261
|
};
|
|
6221
6262
|
const langSwitcher = buildLangSwitcher(path, params, locale, s);
|
|
6222
6263
|
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);
|
|
@@ -6273,6 +6314,14 @@ function buildSseScript(strings) {
|
|
|
6273
6314
|
var WATCHDOG_BODY = ${strings.watchdogBody};
|
|
6274
6315
|
var WATCHDOG_CLOSE_LABEL = ${strings.watchdogCloseLabel};
|
|
6275
6316
|
|
|
6317
|
+
// ── 실시간 상태 표면 상수 (#730) ───────────────────────────────────────
|
|
6318
|
+
var CONN_LOST = ${strings.connectionLost};
|
|
6319
|
+
var COMPLETE_TITLE = ${strings.completeTitle};
|
|
6320
|
+
var COMPLETE_BODY = ${strings.completeBody};
|
|
6321
|
+
var SHUTDOWN_TITLE = ${strings.shutdownTitle};
|
|
6322
|
+
var SHUTDOWN_BODY = ${strings.shutdownBody};
|
|
6323
|
+
var disconnectedShown = false;
|
|
6324
|
+
|
|
6276
6325
|
// ── 클립보드 복사 헬퍼 ────────────────────────────────────────────────
|
|
6277
6326
|
function copyText(text) {
|
|
6278
6327
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
@@ -6350,6 +6399,36 @@ function buildSseScript(strings) {
|
|
|
6350
6399
|
}
|
|
6351
6400
|
}
|
|
6352
6401
|
|
|
6402
|
+
// ── Terminal 상태 배너 (#730) ─────────────────────────────────────────
|
|
6403
|
+
// "완료"/"종료"는 watchdog(최대 210s 지연)을 기다리지 않고 close() 직전에
|
|
6404
|
+
// 서버가 직접 push하는 마지막 SSE 프레임으로 즉시 렌더한다. watchdogFired를
|
|
6405
|
+
// 함께 세워 뒤이은 onerror의 showConnLost가 이 배너를 덮어쓰지 않게 한다.
|
|
6406
|
+
function showTerminalBanner(title, body) {
|
|
6407
|
+
if (watchdogTimer) { clearInterval(watchdogTimer); watchdogTimer = null; }
|
|
6408
|
+
watchdogFired = true; // 이후 watchdog/onerror 발화를 억제
|
|
6409
|
+
if (document.body) {
|
|
6410
|
+
document.body.innerHTML =
|
|
6411
|
+
'<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;' +
|
|
6412
|
+
'height:100vh;font-family:sans-serif;text-align:center;padding:2rem;box-sizing:border-box;">' +
|
|
6413
|
+
'<p style="font-size:1.4rem;font-weight:bold;margin-bottom:1rem;">' + title + '</p>' +
|
|
6414
|
+
'<p style="color:#555;">' + body + '</p></div>';
|
|
6415
|
+
}
|
|
6416
|
+
}
|
|
6417
|
+
|
|
6418
|
+
// ── SSE onerror 즉시 신호 (#730) ──────────────────────────────────────
|
|
6419
|
+
// EventSource는 "재시도 중"과 "서버 종료"를 구분하지 못하므로, onerror에서
|
|
6420
|
+
// 즉시 구분되는 '연결 끊김' 상태를 tunnel-status 배지에 표시한다. 이미
|
|
6421
|
+
// terminal 배너가 뜬 뒤(watchdogFired)라면 덮어쓰지 않는다.
|
|
6422
|
+
function showConnLost() {
|
|
6423
|
+
if (disconnectedShown || watchdogFired) return;
|
|
6424
|
+
disconnectedShown = true;
|
|
6425
|
+
var el = document.getElementById('tunnel-status');
|
|
6426
|
+
if (el) { el.textContent = CONN_LOST; el.className = 'status status-down'; }
|
|
6427
|
+
}
|
|
6428
|
+
function clearConnLost() {
|
|
6429
|
+
disconnectedShown = false;
|
|
6430
|
+
}
|
|
6431
|
+
|
|
6353
6432
|
function fireWatchdog() {
|
|
6354
6433
|
if (watchdogFired) return;
|
|
6355
6434
|
watchdogFired = true;
|
|
@@ -6380,7 +6459,7 @@ function buildSseScript(strings) {
|
|
|
6380
6459
|
src.onopen = function () {
|
|
6381
6460
|
// 연결 복구 시 lastSeen 갱신 — 일시적 끊김이 watchdog을 발화시키지 않게.
|
|
6382
6461
|
// watchdog이 이미 발화했으면 갱신하지 않는다(폴백 UI를 그대로 유지).
|
|
6383
|
-
if (!watchdogFired) { lastSeen = Date.now(); }
|
|
6462
|
+
if (!watchdogFired) { lastSeen = Date.now(); clearConnLost(); }
|
|
6384
6463
|
};
|
|
6385
6464
|
src.onmessage = function (e) {
|
|
6386
6465
|
// SSE 메시지 수신 — lastSeen 갱신으로 watchdog 리셋.
|
|
@@ -6388,6 +6467,13 @@ function buildSseScript(strings) {
|
|
|
6388
6467
|
if (!watchdogFired) { lastSeen = Date.now(); }
|
|
6389
6468
|
try {
|
|
6390
6469
|
var s = JSON.parse(e.data);
|
|
6470
|
+
// 어떤 프레임이든 성공적으로 수신됐다는 건 서버가 살아있다는 뜻 —
|
|
6471
|
+
// onerror가 세운 '연결 끊김' 표시를 해제한다 (#730).
|
|
6472
|
+
clearConnLost();
|
|
6473
|
+
// phase gate (#730) — 'complete'/'shutdown'은 terminal 상태이므로
|
|
6474
|
+
// 나머지 필드 렌더보다 먼저 처리하고 즉시 반환한다.
|
|
6475
|
+
if (s.phase === 'complete') { showTerminalBanner(COMPLETE_TITLE, COMPLETE_BODY); return; }
|
|
6476
|
+
if (s.phase === 'shutdown') { showTerminalBanner(SHUTDOWN_TITLE, SHUTDOWN_BODY); return; }
|
|
6391
6477
|
// 터널 상태 갱신
|
|
6392
6478
|
var el = document.getElementById('tunnel-status');
|
|
6393
6479
|
if (el) {
|
|
@@ -6466,7 +6552,11 @@ function buildSseScript(strings) {
|
|
|
6466
6552
|
} catch (_) { /* 파싱 오류 무시 */ }
|
|
6467
6553
|
};
|
|
6468
6554
|
src.onerror = function () {
|
|
6469
|
-
//
|
|
6555
|
+
// EventSource는 "재시도 중"과 "서버 종료"를 구분하지 못하므로 즉시
|
|
6556
|
+
// 구분되는 "연결 끊김"을 표시한다(#730). 네이티브 auto-reconnect는 그대로
|
|
6557
|
+
// 진행되며 onopen/onmessage가 성공 즉시 이 상태를 해제한다. watchdog은
|
|
6558
|
+
// 여전히 2차 fallback으로 남는다(최종 폴백 UI 전환).
|
|
6559
|
+
showConnLost();
|
|
6470
6560
|
};
|
|
6471
6561
|
})();
|
|
6472
6562
|
<\/script>`;
|
|
@@ -6515,7 +6605,12 @@ function buildAttachHtml(qrDataUrl, safeLabel, safeAttachUrl, locale, path = "/a
|
|
|
6515
6605
|
watchdogTimeoutMs,
|
|
6516
6606
|
watchdogTitle: JSON.stringify(s("dashboard.watchdog.title")),
|
|
6517
6607
|
watchdogBody: JSON.stringify(s("dashboard.watchdog.body")),
|
|
6518
|
-
watchdogCloseLabel: JSON.stringify(s("dashboard.watchdog.close"))
|
|
6608
|
+
watchdogCloseLabel: JSON.stringify(s("dashboard.watchdog.close")),
|
|
6609
|
+
connectionLost: JSON.stringify(s("dashboard.conn.lost")),
|
|
6610
|
+
completeTitle: JSON.stringify(s("dashboard.session.completeTitle")),
|
|
6611
|
+
completeBody: JSON.stringify(s("dashboard.session.completeBody")),
|
|
6612
|
+
shutdownTitle: JSON.stringify(s("dashboard.session.shutdownTitle")),
|
|
6613
|
+
shutdownBody: JSON.stringify(s("dashboard.session.shutdownBody"))
|
|
6519
6614
|
});
|
|
6520
6615
|
return filled.replace("</body>", `${sseScript}\n</body>`);
|
|
6521
6616
|
}
|
|
@@ -6542,7 +6637,8 @@ async function startQrHttpServer(getDashboardState, options) {
|
|
|
6542
6637
|
},
|
|
6543
6638
|
pages: state.pages,
|
|
6544
6639
|
attachUrl: state.attachUrl,
|
|
6545
|
-
inspectorUrl: state.inspectorUrl ?? null
|
|
6640
|
+
inspectorUrl: state.inspectorUrl ?? null,
|
|
6641
|
+
phase: state.phase ?? "active"
|
|
6546
6642
|
});
|
|
6547
6643
|
res.write(`data: ${payload}\n\n`);
|
|
6548
6644
|
}
|
|
@@ -6851,7 +6947,7 @@ function createDebugServer(deps) {
|
|
|
6851
6947
|
};
|
|
6852
6948
|
const server = new Server({
|
|
6853
6949
|
name: "ait-debug",
|
|
6854
|
-
version: "0.1.
|
|
6950
|
+
version: "0.1.131"
|
|
6855
6951
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
6856
6952
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
6857
6953
|
const conn = router.active;
|
|
@@ -7813,6 +7909,7 @@ async function runDebugServer(options = {}) {
|
|
|
7813
7909
|
return router.active;
|
|
7814
7910
|
});
|
|
7815
7911
|
let lastAttachParts = null;
|
|
7912
|
+
let currentPhase = "active";
|
|
7816
7913
|
const getDashboardState = () => {
|
|
7817
7914
|
const targets = router.active.listTargets();
|
|
7818
7915
|
const inspectorUrl = qrServer?.inspectorStableUrl ?? null;
|
|
@@ -7827,7 +7924,8 @@ async function runDebugServer(options = {}) {
|
|
|
7827
7924
|
})),
|
|
7828
7925
|
attachUrl: lastAttachParts ? rebuildAttachUrl(lastAttachParts) : null,
|
|
7829
7926
|
inspectorUrl,
|
|
7830
|
-
mode: deriveEnvironment(router.active.kind, router.activeRelayOrigin)
|
|
7927
|
+
mode: deriveEnvironment(router.active.kind, router.activeRelayOrigin),
|
|
7928
|
+
phase: currentPhase
|
|
7831
7929
|
};
|
|
7832
7930
|
};
|
|
7833
7931
|
const getDirectInspectorUrl = () => {
|
|
@@ -7891,6 +7989,8 @@ async function runDebugServer(options = {}) {
|
|
|
7891
7989
|
const shutdown = () => {
|
|
7892
7990
|
if (closed) return;
|
|
7893
7991
|
closed = true;
|
|
7992
|
+
currentPhase = "shutdown";
|
|
7993
|
+
qrServer?.notifyStateChange();
|
|
7894
7994
|
parentWatcher?.stop();
|
|
7895
7995
|
maxAgeWatchdog?.stop();
|
|
7896
7996
|
if (totpRefreshHandle) clearInterval(totpRefreshHandle);
|
|
@@ -8028,6 +8128,7 @@ async function runLocalDebugServer(options = {}) {
|
|
|
8028
8128
|
return router.active;
|
|
8029
8129
|
});
|
|
8030
8130
|
let lastAttachParts = null;
|
|
8131
|
+
let currentPhase = "active";
|
|
8031
8132
|
const getDashboardState = () => {
|
|
8032
8133
|
const targets = router.active.listTargets();
|
|
8033
8134
|
const inspectorUrl = qrServer?.inspectorStableUrl ?? null;
|
|
@@ -8041,7 +8142,8 @@ async function runLocalDebugServer(options = {}) {
|
|
|
8041
8142
|
url: t.url
|
|
8042
8143
|
})),
|
|
8043
8144
|
attachUrl: lastAttachParts ? rebuildAttachUrl(lastAttachParts) : null,
|
|
8044
|
-
inspectorUrl
|
|
8145
|
+
inspectorUrl,
|
|
8146
|
+
phase: currentPhase
|
|
8045
8147
|
};
|
|
8046
8148
|
};
|
|
8047
8149
|
const getDirectInspectorUrl = () => {
|
|
@@ -8105,6 +8207,8 @@ async function runLocalDebugServer(options = {}) {
|
|
|
8105
8207
|
const shutdown = () => {
|
|
8106
8208
|
if (closed) return;
|
|
8107
8209
|
closed = true;
|
|
8210
|
+
currentPhase = "shutdown";
|
|
8211
|
+
qrServer?.notifyStateChange();
|
|
8108
8212
|
parentWatcher?.stop();
|
|
8109
8213
|
maxAgeWatchdog?.stop();
|
|
8110
8214
|
if (totpRefreshHandle) clearInterval(totpRefreshHandle);
|
|
@@ -8225,6 +8329,7 @@ async function runMobileDebugServer(options = {}) {
|
|
|
8225
8329
|
return router.active;
|
|
8226
8330
|
});
|
|
8227
8331
|
let lastAttachParts = null;
|
|
8332
|
+
let currentPhase = "active";
|
|
8228
8333
|
const getDashboardState = () => {
|
|
8229
8334
|
const targets = router.active.listTargets();
|
|
8230
8335
|
const inspectorUrl = qrServer?.inspectorStableUrl ?? null;
|
|
@@ -8238,7 +8343,8 @@ async function runMobileDebugServer(options = {}) {
|
|
|
8238
8343
|
url: t.url
|
|
8239
8344
|
})),
|
|
8240
8345
|
attachUrl: lastAttachParts ? rebuildAttachUrl(lastAttachParts) : null,
|
|
8241
|
-
inspectorUrl
|
|
8346
|
+
inspectorUrl,
|
|
8347
|
+
phase: currentPhase
|
|
8242
8348
|
};
|
|
8243
8349
|
};
|
|
8244
8350
|
const getDirectInspectorUrl = () => {
|
|
@@ -8302,6 +8408,8 @@ async function runMobileDebugServer(options = {}) {
|
|
|
8302
8408
|
const shutdown = () => {
|
|
8303
8409
|
if (closed) return;
|
|
8304
8410
|
closed = true;
|
|
8411
|
+
currentPhase = "shutdown";
|
|
8412
|
+
qrServer?.notifyStateChange();
|
|
8305
8413
|
parentWatcher?.stop();
|
|
8306
8414
|
maxAgeWatchdog?.stop();
|
|
8307
8415
|
if (totpRefreshHandle) clearInterval(totpRefreshHandle);
|
|
@@ -8823,7 +8931,7 @@ function createDevServer(deps = {}) {
|
|
|
8823
8931
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
8824
8932
|
const server = new Server({
|
|
8825
8933
|
name: "ait-devtools",
|
|
8826
|
-
version: "0.1.
|
|
8934
|
+
version: "0.1.131"
|
|
8827
8935
|
}, { capabilities: { tools: {} } });
|
|
8828
8936
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
8829
8937
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|