@ait-co/devtools 0.1.141 → 0.1.142

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.
Files changed (77) hide show
  1. package/README.en.md +4 -4
  2. package/README.md +4 -4
  3. package/dist/{attach-orchestrator-BG8TGJOZ.js → attach-orchestrator-0F0m_UqQ.js} +71 -9
  4. package/dist/{attach-orchestrator-BG8TGJOZ.js.map → attach-orchestrator-0F0m_UqQ.js.map} +1 -1
  5. package/dist/{attach-orchestrator-6kz_ZdWL.js → attach-orchestrator-D65KxFy_.js} +71 -9
  6. package/dist/{attach-orchestrator-6kz_ZdWL.js.map → attach-orchestrator-D65KxFy_.js.map} +1 -1
  7. package/dist/{attach-orchestrator-BEiz3wAx.js → attach-orchestrator-DL3NQ9ca.js} +71 -9
  8. package/dist/{attach-orchestrator-BEiz3wAx.js.map → attach-orchestrator-DL3NQ9ca.js.map} +1 -1
  9. package/dist/{cell-BMlFh08P.js → cell-BaLvusOl.js} +2 -2
  10. package/dist/{cell-BMlFh08P.js.map → cell-BaLvusOl.js.map} +1 -1
  11. package/dist/{cell-Dtgtusog.js → cell-CBUS3-nT.js} +2 -2
  12. package/dist/{cell-Dtgtusog.js.map → cell-CBUS3-nT.js.map} +1 -1
  13. package/dist/{cell-KsRXFdDs.js → cell-EBKKpAAT.js} +2 -2
  14. package/dist/{cell-KsRXFdDs.js.map → cell-EBKKpAAT.js.map} +1 -1
  15. package/dist/{debug-server-DWat7d-9.js → debug-server-B3ABDrRI.js} +4 -4
  16. package/dist/{debug-server-DWat7d-9.js.map → debug-server-B3ABDrRI.js.map} +1 -1
  17. package/dist/{debug-server-6c22qSWa.js → debug-server-BWhwrVXa.js} +4 -4
  18. package/dist/{debug-server-6c22qSWa.js.map → debug-server-BWhwrVXa.js.map} +1 -1
  19. package/dist/{debug-server-BQHbaKPT.js → debug-server-CfQNxxGW.js} +6 -6
  20. package/dist/{debug-server-BQHbaKPT.js.map → debug-server-CfQNxxGW.js.map} +1 -1
  21. package/dist/in-app/auto.js +299 -3
  22. package/dist/in-app/auto.js.map +1 -1
  23. package/dist/in-app/index.d.ts +161 -14
  24. package/dist/in-app/index.d.ts.map +1 -1
  25. package/dist/in-app/index.js +300 -4
  26. package/dist/in-app/index.js.map +1 -1
  27. package/dist/mcp/cli.js +82 -16
  28. package/dist/mcp/cli.js.map +1 -1
  29. package/dist/mcp/server.js +1 -1
  30. package/dist/mock/index.d.ts +124 -89
  31. package/dist/mock/index.d.ts.map +1 -1
  32. package/dist/mock/index.js +21 -2
  33. package/dist/mock/index.js.map +1 -1
  34. package/dist/panel/index.js +7 -2
  35. package/dist/panel/index.js.map +1 -1
  36. package/dist/{qr-http-server-Cz1mA07b.cjs → qr-http-server--gl2-WKc.cjs} +9 -5
  37. package/dist/qr-http-server--gl2-WKc.cjs.map +1 -0
  38. package/dist/{qr-http-server-D4rGz8M7.js → qr-http-server-Bb7lMeqi.js} +9 -5
  39. package/dist/qr-http-server-Bb7lMeqi.js.map +1 -0
  40. package/dist/{qr-http-server-D1hpoyBF.js → qr-http-server-C_lqOrgc.js} +9 -5
  41. package/dist/qr-http-server-C_lqOrgc.js.map +1 -0
  42. package/dist/{qr-http-server-CTLdclg_.js → qr-http-server-CopuMbub.js} +9 -5
  43. package/dist/qr-http-server-CopuMbub.js.map +1 -0
  44. package/dist/{qr-http-server-D16Lq2ag.cjs → qr-http-server-D-Off6K1.cjs} +9 -5
  45. package/dist/qr-http-server-D-Off6K1.cjs.map +1 -0
  46. package/dist/{qr-http-server-DIjCV7h_.js → qr-http-server-DrbIVDjO.js} +9 -5
  47. package/dist/qr-http-server-DrbIVDjO.js.map +1 -0
  48. package/dist/qr-http-server-n1twN18z.js +1644 -0
  49. package/dist/qr-http-server-n1twN18z.js.map +1 -0
  50. package/dist/{relay-factory-C1X3G3WY.js → relay-factory-N9QobQxG.js} +6 -6
  51. package/dist/{relay-factory-C1X3G3WY.js.map → relay-factory-N9QobQxG.js.map} +1 -1
  52. package/dist/{relay-worker-BUXI0K0b.js → relay-worker-B5HKkGUY.js} +4 -4
  53. package/dist/{relay-worker-BUXI0K0b.js.map → relay-worker-B5HKkGUY.js.map} +1 -1
  54. package/dist/relay-worker-DcyboK43.d.ts.map +1 -1
  55. package/dist/test-runner/bin.js +8 -8
  56. package/dist/test-runner/bin.js.map +1 -1
  57. package/dist/test-runner/config.js +1 -1
  58. package/dist/test-runner/pool.js +1 -1
  59. package/dist/test-runner/relay-factory.js +5 -5
  60. package/dist/test-runner/relay-worker.js +1 -1
  61. package/dist/{tunnel-DjtSENJU.cjs → tunnel-BVSMXctM.cjs} +2 -2
  62. package/dist/{tunnel-DjtSENJU.cjs.map → tunnel-BVSMXctM.cjs.map} +1 -1
  63. package/dist/{tunnel-5eBncITa.js → tunnel-D7xkimBu.js} +2 -2
  64. package/dist/{tunnel-5eBncITa.js.map → tunnel-D7xkimBu.js.map} +1 -1
  65. package/dist/unplugin/index.cjs +1 -1
  66. package/dist/unplugin/index.js +1 -1
  67. package/dist/unplugin/tunnel.cjs +1 -1
  68. package/dist/unplugin/tunnel.js +1 -1
  69. package/package.json +2 -2
  70. package/dist/qr-http-server-CTLdclg_.js.map +0 -1
  71. package/dist/qr-http-server-Cz1mA07b.cjs.map +0 -1
  72. package/dist/qr-http-server-D16Lq2ag.cjs.map +0 -1
  73. package/dist/qr-http-server-D1hpoyBF.js.map +0 -1
  74. package/dist/qr-http-server-D4rGz8M7.js.map +0 -1
  75. package/dist/qr-http-server-DIjCV7h_.js.map +0 -1
  76. package/dist/qr-http-server-wy3L7Yut.js +0 -1640
  77. package/dist/qr-http-server-wy3L7Yut.js.map +0 -1
package/README.en.md CHANGED
@@ -20,7 +20,7 @@ Live demo: <https://devtools.aitc.dev/> (the `e2e/fixture/` from this repo deplo
20
20
 
21
21
  ## 15-second quickstart — pick your environment
22
22
 
23
- There are four runtime environments. Pick the card that fits your situation and follow the link to the detailed scenario doc.
23
+ There are three runtime environments. Pick the card that fits your situation and follow the link to the detailed scenario doc.
24
24
 
25
25
  ---
26
26
 
@@ -47,7 +47,7 @@ pnpm dev:phone # same as AIT_TUNNEL=1 pnpm dev
47
47
  # QR appears in the terminal → scan with your phone camera → opens in the launcher PWA
48
48
  ```
49
49
 
50
- With `tunnel: { cdp: true }`, a single QR scan opens both the screen preview and on-device CDP — inspect the real WebKit DOM, console, and exceptions from your MCP host (`call_sdk` still hits the mock on environment 2; the real SDK lives on environments 3·4).
50
+ With `tunnel: { cdp: true }`, a single QR scan opens both the screen preview and on-device CDP — inspect the real WebKit DOM, console, and exceptions from your MCP host (`call_sdk` still hits the mock on environment 2; the real SDK lives on environment 3).
51
51
 
52
52
  One-time prerequisite: add `https://devtools.aitc.dev/launcher/` to your phone's home screen. Details: [`docs/scenarios/env-2.md`](./docs/scenarios/env-2.md)
53
53
 
@@ -83,7 +83,7 @@ What this single line does:
83
83
  - **SDK bridge**: installs `window.__sdk` / `window.__sdkCall` so an agent can drive any SDK API directly over the CDP relay via `Runtime.evaluate`. Silently skipped if `@apps-in-toss/web-framework` is not available.
84
84
  - **Types**: provides `Window.__sdk` / `__sdkCall` global type declarations automatically — no separate `globals.d.ts` needed in your project.
85
85
 
86
- For environments 3 and 4 (intoss-private relay), the relay QR deep-link carries `?debug=1&relay=<wss>` query params, so this one line is all the wiring you need. Environment 2 (PWA, `tunnel: { cdp: true }`) works the same way.
86
+ For environment 3 (intoss-private relay), the relay QR deep-link carries `?debug=1&relay=<wss>` query params, so this one line is all the wiring you need. Environment 2 (PWA, `tunnel: { cdp: true }`) works the same way.
87
87
 
88
88
  > For dog-food builds with TOTP authentication, inject `__DEBUG_TOTP_SECRET__` via your build define and use `@ait-co/devtools/in-app` directly with `evaluateDebugGate({ verifyTotpCode })` + `maybeAttach()`. `in-app/auto` does not inject a TOTP verifier, so Layer C3 is disabled.
89
89
 
@@ -1020,7 +1020,7 @@ Read-only tools only. Tools are registered in two tiers based on attach state
1020
1020
 
1021
1021
  Running `devtools-mcp` as a stdio server starts a local Chii relay on an OS-assigned port and opens a cloudflared quick tunnel, printing a public `wss://*.trycloudflare.com` URL and a QR code in the terminal (secrets/auth codes are never printed). When the phone enters the dog-food entry point, the in-app attach UI connects to the relay with that URL, and the agent reads console/network/page state via `chrome-devtools-mcp`-compatible tools — diagnosing regressions without anyone watching the phone.
1022
1022
 
1023
- Environments 3 and 4 (intoss-private relay) — start `devtools-mcp` as-is, then enter via `start_debug(mode)`:
1023
+ Environment 3 (intoss-private relay) — start `devtools-mcp` as-is, then enter via `start_debug(mode)`:
1024
1024
 
1025
1025
  ```json
1026
1026
  {
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  ## 15초 quickstart — 내 상황에 맞는 환경 고르기
22
22
 
23
- 4가지 실행 환경이 있습니다. 지금 상황에 맞는 카드 하나를 고르고, 해당 상세 시나리오 문서로 이동하세요.
23
+ 3가지 실행 환경이 있습니다. 지금 상황에 맞는 카드 하나를 고르고, 해당 상세 시나리오 문서로 이동하세요.
24
24
 
25
25
  ---
26
26
 
@@ -47,7 +47,7 @@ pnpm dev:phone # AIT_TUNNEL=1 pnpm dev 와 동일
47
47
  # 터미널에 QR 출력 → 폰 카메라로 스캔 → launcher PWA에서 자동 열림
48
48
  ```
49
49
 
50
- `tunnel: { cdp: true }`를 켜면 같은 QR 한 번으로 화면 미리보기 + on-device CDP가 함께 열려 실기기 WebKit의 DOM·콘솔·예외를 MCP로 관측합니다 (`call_sdk`는 환경 2에서 mock — 실 SDK는 환경 3·4).
50
+ `tunnel: { cdp: true }`를 켜면 같은 QR 한 번으로 화면 미리보기 + on-device CDP가 함께 열려 실기기 WebKit의 DOM·콘솔·예외를 MCP로 관측합니다 (`call_sdk`는 환경 2에서 mock — 실 SDK는 환경 3).
51
51
 
52
52
  사전: 폰에 `https://devtools.aitc.dev/launcher/` 를 홈 화면에 한 번 추가. 상세: [`docs/scenarios/env-2.md`](./docs/scenarios/env-2.md)
53
53
 
@@ -83,7 +83,7 @@ import '@ait-co/devtools/in-app/auto';
83
83
  - **SDK 브리지**: `window.__sdk` / `window.__sdkCall`을 설치해 에이전트가 CDP relay의 `Runtime.evaluate`로 SDK API를 직접 구동할 수 있게 합니다. `@apps-in-toss/web-framework`가 없으면 조용히 skip합니다.
84
84
  - **타입**: `Window.__sdk` / `__sdkCall` 글로벌 타입을 자동으로 제공합니다 — 별도 `globals.d.ts` 불필요.
85
85
 
86
- 환경 3·4(intoss-private relay) 빌드는 relay QR deep-link가 `?debug=1&relay=<wss>` 파라미터를 실어 보내므로, 이 한 줄만 있으면 별도 게이트 코드가 필요 없습니다. 환경 2(PWA, `tunnel: { cdp: true }`)도 동일하게 동작합니다.
86
+ 환경 3(intoss-private relay) 빌드는 relay QR deep-link가 `?debug=1&relay=<wss>` 파라미터를 실어 보내므로, 이 한 줄만 있으면 별도 게이트 코드가 필요 없습니다. 환경 2(PWA, `tunnel: { cdp: true }`)도 동일하게 동작합니다.
87
87
 
88
88
  > TOTP 인증이 필요한 dog-food 빌드는 빌드 define으로 `__DEBUG_TOTP_SECRET__`을 주입하고 `@ait-co/devtools/in-app`을 직접 import해 `evaluateDebugGate({ verifyTotpCode })` + `maybeAttach()`를 사용하세요. `in-app/auto`는 TOTP verifier를 주입하지 않으므로 C3 레이어가 비활성화됩니다.
89
89
 
@@ -1058,7 +1058,7 @@ tunnel로 공개 `wss://*.trycloudflare.com` URL을 발급한 뒤 QR을 터미
1058
1058
  에이전트가 `chrome-devtools-mcp` 호환 tool로 console/network/page 상태를 read합니다. 사람이 폰을
1059
1059
  지켜볼 필요 없이 회귀를 단독 진단하는 것이 목표입니다.
1060
1060
 
1061
- 환경 3·4 (intoss-private relay) — `devtools-mcp`를 그대로 기동한 뒤 `start_debug(mode)`로 진입합니다:
1061
+ 환경 3 (intoss-private relay) — `devtools-mcp`를 그대로 기동한 뒤 `start_debug(mode)`로 진입합니다:
1062
1062
 
1063
1063
  ```json
1064
1064
  {
@@ -1606,7 +1606,7 @@ async function renderAndMaybeWait(deps, prep, waitForAttach, callTimeoutMs, conn
1606
1606
  let totpIssuedAt = nowMs();
1607
1607
  let reminted = 0;
1608
1608
  const relayUrl = parts.wssUrl;
1609
- 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).";
1609
+ 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).\n\n테스트가 끝날 때까지 앱을 화면 앞에 유지하세요 — 백그라운드로 전환하면 디버그 세션이 끊어집니다.";
1610
1610
  const warningPrefix = authorityWarning ? `⚠️ scheme_url 경고: ${authorityWarning}\n\n` : "";
1611
1611
  const guiAvailable = canOpenBrowserFn();
1612
1612
  /** Builds the totp object surfaced in results (fresh expiresAt + reminted). */
@@ -1732,9 +1732,35 @@ async function renderAndMaybeWait(deps, prep, waitForAttach, callTimeoutMs, conn
1732
1732
  }, null, 2)}\n\n${qr}`);
1733
1733
  }
1734
1734
  /**
1735
+ * How long the disconnected badge stays on screen before it fades and removes
1736
+ * itself (#748). Long enough to read, short enough not to linger; a reconnect
1737
+ * within this window cancels the self-dismiss (transient tunnel blips do not
1738
+ * flash-remove the badge).
1739
+ */
1740
+ const INDICATOR_SELF_DISMISS_MS = 5e3;
1741
+ /** Fade duration before the self-dismissed badge is detached from the DOM. */
1742
+ const INDICATOR_FADE_MS = 400;
1743
+ /**
1744
+ * Heartbeat / pending-call render tick (#749). 1 Hz — cheap enough to be
1745
+ * invisible (a single text write per second, no layout thrash) while making a
1746
+ * JS main-thread wedge obvious: the compositor-driven pulse dot keeps animating
1747
+ * but this JS-driven `♥<beats>` token freezes. Pending-call elapsed times are
1748
+ * recomputed on the same tick so they advance without any bridge event.
1749
+ */
1750
+ const INDICATOR_HEARTBEAT_MS = 1e3;
1751
+ /**
1752
+ * Display-side staleness guard (#749): the badge skips rendering a pending call
1753
+ * older than this. A safety net independent of the in-app observer's own prune
1754
+ * (`bridge-observer.ts` MAX_PENDING_AGE_MS) — kept local so the daemon-graph
1755
+ * expression builder never imports the in-app module (install-graph invariant).
1756
+ * Generous enough that a genuinely slow native call still shows while plausibly
1757
+ * in flight.
1758
+ */
1759
+ const INDICATOR_PENDING_STALE_MS = 12e4;
1760
+ /**
1735
1761
  * Builds a self-contained IIFE DOM expression that renders a LIVE
1736
- * "Debugger Connected" / "Debugger Disconnected" badge on the bottom-left of
1737
- * the phone screen (#730).
1762
+ * "Debugger Connected" / disconnected badge on the bottom-left of the phone
1763
+ * screen (#730), with a graceful self-dismiss on disconnect (#748).
1738
1764
  *
1739
1765
  * **Pure function** — returns a JS expression string; does NOT inject it.
1740
1766
  * Injection is performed by {@link injectDebugIndicator} in `cell.ts`.
@@ -1751,7 +1777,18 @@ async function renderAndMaybeWait(deps, prep, waitForAttach, callTimeoutMs, conn
1751
1777
  * 3. A `pointerdown` listener dismisses (hides) the badge on tap — dismiss
1752
1778
  * is NON-terminal: a later state transition un-dismisses it, so a
1753
1779
  * genuine disconnect after a dismissed tap is still visible.
1754
- * 4. Observes relay-socket lifecycle WITHOUT opening any new connection:
1780
+ * 4. **Graceful self-dismiss (#748)** the disconnected badge is NON-BLOCKING
1781
+ * (`pointer-events:none` the moment it flips to disconnected, so it can
1782
+ * never absorb a tap) and SELF-DISMISSING (it fades and detaches itself
1783
+ * after {@link INDICATOR_SELF_DISMISS_MS}). This is why a run that ends
1784
+ * with `close()` injecting `{ state: 'disconnected' }` no longer leaves a
1785
+ * permanent grey "Debugger Disconnected" element on the phone. A reconnect
1786
+ * (`setState(c, 'attached')`) cancels the pending self-dismiss and
1787
+ * re-mounts the badge if it had already detached — transient tunnel blips
1788
+ * do not flash-remove it, and a genuine re-attach restores it. The
1789
+ * controller object is retained across dismiss (never `delete`d), so a
1790
+ * later re-injection reuses it and never double-wraps `window.WebSocket`.
1791
+ * 5. Observes relay-socket lifecycle WITHOUT opening any new connection:
1755
1792
  * - Preferred path: if the in-app module (`src/in-app/attach.ts`) has
1756
1793
  * already installed its relay-WS observer (`window.__ait_relay_ws_observed`),
1757
1794
  * subscribe to the `ait:relay-ws-state` CustomEvent it broadcasts.
@@ -1759,25 +1796,50 @@ async function renderAndMaybeWait(deps, prep, waitForAttach, callTimeoutMs, conn
1759
1796
  * by PATHNAME SHAPE only (`/target/`) — never by host/wss value — so a
1760
1797
  * bare CDP-injected badge (no in-app bundle) still reacts to the
1761
1798
  * chii target socket's own open/close lifecycle.
1799
+ * 6. **Freeze/spinner triage (#749)** — renders three debug-only signals so a
1800
+ * real-device spinner is attributable at a glance (run7):
1801
+ * - **Main-thread heartbeat**: a compositor-driven pulse dot
1802
+ * (`Element.animate`, keeps running during JS jank) PLUS a JS-driven
1803
+ * `♥<beats>` token incremented by a 1 Hz `setInterval`. `CSS pulse alive
1804
+ * + ♥ frozen = JS main-thread wedge`.
1805
+ * - **Pending bridge calls**: in-flight native calls (API name + live
1806
+ * elapsed) read from `window.__ait_bridge` (published by the in-app
1807
+ * bridge observer, `src/in-app/bridge-observer.ts`). `⏳ present =
1808
+ * native/Toss-app spinner`.
1809
+ * - **Last SDK call stamp**: the most recent call's API name + wall-clock.
1810
+ * `no ⏳ + heartbeat healthy = the miniapp's own UI`.
1811
+ * The `title` tooltip states this mapping. When no in-app observer is
1812
+ * present (env 2 mock / no bridge) the pending/last lines are simply empty
1813
+ * — the heartbeat still renders. The 1 Hz interval is stored on the
1814
+ * controller (`c.hb`) and stopped by `c.stop()` (called from
1815
+ * `detachDebugSurface`) and by the self-dismiss removal, so no timer leaks
1816
+ * past detach (#748 lifecycle).
1762
1817
  *
1763
1818
  * The expression intentionally contains NO relay URLs, wss addresses, TOTP
1764
- * codes, or any other secrets. It is pure DOM UI text + enum state only.
1819
+ * codes, or any other secrets. `window.__ait_bridge` holds API NAMES + timings
1820
+ * only (never call arguments/results — see `bridge-observer.ts`
1821
+ * SECRET-HANDLING). It is pure DOM UI text + enum/name state only.
1765
1822
  *
1766
1823
  * SECRET-HANDLING: this expression contains no secrets, relay URLs, wss
1767
1824
  * addresses, or TOTP codes whatsoever — DOM label text + a structural
1768
1825
  * pathname match (`/target/`) only.
1769
1826
  *
1770
1827
  * @param opts.label - Attached-state badge text (default: `'Debugger Connected'`).
1771
- * @param opts.disconnectedLabel - Disconnected-state badge text (default: `'Debugger Disconnected'`).
1828
+ * @param opts.disconnectedLabel - Disconnected-state badge text. Default is the
1829
+ * Korean notice `'디버거 연결 끊김'` (ko-primary in-app string convention) —
1830
+ * shown briefly then self-dismissed.
1772
1831
  * @param opts.state - Initial/forced state for THIS injection call (default: `'attached'`).
1773
1832
  * @returns A JS expression string suitable for `Runtime.evaluate`.
1774
1833
  */
1775
1834
  function buildIndicatorExpression(opts) {
1776
1835
  const label = opts?.label ?? "Debugger Connected";
1777
- const disconnectedLabel = opts?.disconnectedLabel ?? "Debugger Disconnected";
1778
- 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")});})()`;
1836
+ const disconnectedLabel = opts?.disconnectedLabel ?? "디버거 연결 끊김";
1837
+ const safeLabel = JSON.stringify(label);
1838
+ const safeDisconnectedLabel = JSON.stringify(disconnectedLabel);
1839
+ const safeState = JSON.stringify(opts?.state ?? "attached");
1840
+ return `(() => {var W = window;function pad2(n) { return (n < 10 ? '0' : '') + n; }function clock(ms) { var d = new Date(ms); return pad2(d.getHours()) + ':' + pad2(d.getMinutes()) + ':' + pad2(d.getSeconds()); }function renderDetail(c) {c.beatEl.textContent = ' \\u2665' + c.beats;var b = W.__ait_bridge;var now = Date.now();var lines = [];if (b) {if (b.last) { lines.push('last: ' + b.last.method + ' ' + clock(b.last.at)); }var p = b.pending || {};var ids = Object.keys(p);var shown = 0;for (var i = 0; i < ids.length; i++) {var e = p[ids[i]];if (!e || now - e.startedAt > ${INDICATOR_PENDING_STALE_MS}) { continue; }if (shown < 3) { lines.push('\\u23f3 ' + e.method + ' ' + Math.max(0, Math.round((now - e.startedAt) / 1000)) + 's'); }shown++;}if (shown > 3) { lines.push('+' + (shown - 3) + ' more'); }}c.detailEl.textContent = lines.join('\\n');}function mount(c) { if (!c.el.isConnected && document.body) { document.body.appendChild(c.el); } }function render(c) {if (c.removed) { c.el.style.display = 'none'; return; }mount(c);if (c.dismissed) { c.el.style.display = 'none'; return; }c.el.style.display = 'block';c.el.style.opacity = '1';var up = c.state !== 'disconnected';c.el.style.background = up ? '#e5484d' : '#8a8f98';c.el.style.pointerEvents = up ? 'auto' : 'none';c.labelEl.textContent = up ? ${safeLabel} : ${safeDisconnectedLabel};c.dotEl.style.display = up ? 'inline-block' : 'none';c.beatEl.style.display = up ? 'inline' : 'none';c.detailEl.style.display = up ? 'block' : 'none';if (up) { renderDetail(c); } else { c.beatEl.textContent = ''; c.detailEl.textContent = ''; }}function clearTimers(c) { if (c.t1) { clearTimeout(c.t1); c.t1 = 0; } if (c.t2) { clearTimeout(c.t2); c.t2 = 0; } }function setState(c, next) {clearTimers(c);c.state = next; c.dismissed = false; c.removed = false;render(c);if (next === 'disconnected') {c.t1 = setTimeout(function () {try { c.el.style.transition = 'opacity ${INDICATOR_FADE_MS}ms ease'; c.el.style.opacity = '0'; } catch (_) {}c.t2 = setTimeout(function () { c.removed = true; if (c.hb) { clearInterval(c.hb); c.hb = 0; } if (c.el.parentNode) { c.el.parentNode.removeChild(c.el); } }, ${INDICATOR_FADE_MS});}, ${INDICATOR_SELF_DISMISS_MS});}}var c = W.__ait_indicator;if (!c) {var el = document.createElement('div');el.id = '__ait_debug_indicator';try { el.title = ${JSON.stringify("⏳ 네이티브 호출 대기 = 토스앱 스피너 · ♥ 정지 = JS 멈춤 · ⏳ 없고 ♥ 정상 = 앱 UI")}; } catch (_) {}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.2 system-ui,sans-serif','padding:5px 9px','border-radius:6px','max-width:72vw','pointer-events:auto','user-select:none',].join(';');var dotEl = document.createElement('span');dotEl.style.cssText = 'display:inline-block;width:6px;height:6px;border-radius:50%;background:#fff;margin-right:5px;vertical-align:middle';var labelEl = document.createElement('span');labelEl.id = '__ait_indicator_label';var beatEl = document.createElement('span');beatEl.style.cssText = 'font-weight:normal;opacity:0.85;margin-left:6px';var detailEl = document.createElement('div');detailEl.style.cssText = 'font-weight:normal;font-size:10px;line-height:1.35;margin-top:3px;white-space:pre-line;opacity:0.95';el.appendChild(dotEl); el.appendChild(labelEl); el.appendChild(beatEl); el.appendChild(detailEl);document.body.appendChild(el);c = { el: el, dotEl: dotEl, labelEl: labelEl, beatEl: beatEl, detailEl: detailEl, state: 'attached', dismissed: false, removed: false, beats: 0, t1: 0, t2: 0, hb: 0 };try { dotEl.animate([{ opacity: 1 }, { opacity: 0.25 }, { opacity: 1 }], { duration: 1400, iterations: Infinity }); } catch (_) {}c.hb = (typeof setInterval !== 'undefined') ? setInterval(function () {if (c.removed || !c.el.isConnected) { if (c.hb) { clearInterval(c.hb); c.hb = 0; } return; }c.beats = (c.beats || 0) + 1;if (c.state !== 'disconnected' && !c.dismissed) { renderDetail(c); }}, ${INDICATOR_HEARTBEAT_MS}) : 0;c.stop = function () { clearTimers(c); if (c.hb) { clearInterval(c.hb); c.hb = 0; } };el.addEventListener('pointerdown', function () { c.dismissed = true; render(c); }, { passive: true });W.addEventListener('ait:bridge-call', function () { if (c.state !== 'disconnected' && !c.dismissed && !c.removed) { renderDetail(c); } });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, ${safeState});})()`;
1779
1841
  }
1780
1842
  //#endregion
1781
1843
  export { buildIndicatorExpression as a, generateAttachToken as c, startQuickTunnel as d, startTunnelHealthProbe as f, attach_orchestrator_exports as i, makeTunnelStatus as l, logInfo as m, START_ATTACH_REMINT_THRESHOLD_MS as n, extractDeploymentId as o, logError as p, START_ATTACH_SEGMENT_MS as r, isSandboxPageFresh as s, RELAY_SANDBOX_STALE_PAGE_MS as t, printAttachBanner as u };
1782
1844
 
1783
- //# sourceMappingURL=attach-orchestrator-BG8TGJOZ.js.map
1845
+ //# sourceMappingURL=attach-orchestrator-0F0m_UqQ.js.map