@ait-co/devtools 0.1.144 → 0.2.1
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 +47 -218
- package/README.md +36 -246
- package/dist/in-app/auto.d.ts +1 -138
- package/dist/in-app/auto.js +28 -1102
- package/dist/in-app/auto.js.map +1 -1
- package/dist/in-app/index.d.ts +38 -547
- package/dist/in-app/index.d.ts.map +1 -1
- package/dist/in-app/index.js +62 -939
- package/dist/in-app/index.js.map +1 -1
- package/dist/mcp/cli.d.ts +1 -54
- package/dist/mcp/cli.js +33 -9720
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -88
- package/dist/mcp/server.js +36 -1076
- package/dist/mcp/server.js.map +1 -1
- package/dist/mock/index.d.ts +35 -21
- package/dist/mock/index.d.ts.map +1 -1
- package/dist/mock/index.js +81 -2
- package/dist/mock/index.js.map +1 -1
- package/dist/panel/index.js +80 -104
- package/dist/panel/index.js.map +1 -1
- package/dist/relay-url-store-CkVSQZMq.cjs +110 -0
- package/dist/relay-url-store-CkVSQZMq.cjs.map +1 -0
- package/dist/relay-url-store-dkII-DHD.js +109 -0
- package/dist/relay-url-store-dkII-DHD.js.map +1 -0
- package/dist/stubs/bin-devtools-mcp.js +58 -0
- package/dist/stubs/bin-devtools-mcp.js.map +1 -0
- package/dist/stubs/bin-devtools-test.d.ts +2 -0
- package/dist/stubs/bin-devtools-test.js +55 -0
- package/dist/stubs/bin-devtools-test.js.map +1 -0
- package/dist/test-runner/config.d.ts +1 -231
- package/dist/test-runner/config.js +41 -45
- package/dist/test-runner/config.js.map +1 -1
- package/dist/{tunnel-BGT9Curk.cjs → tunnel-BKZkOyQp.cjs} +1 -1
- package/dist/{tunnel-BGT9Curk.cjs.map → tunnel-BKZkOyQp.cjs.map} +1 -1
- package/dist/{tunnel-BOKmLzBO.js → tunnel-CqSCIrdU.js} +1 -1
- package/dist/{tunnel-BOKmLzBO.js.map → tunnel-CqSCIrdU.js.map} +1 -1
- package/dist/unplugin/index.cjs +9 -18
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts +26 -5
- package/dist/unplugin/index.d.cts.map +1 -1
- package/dist/unplugin/index.d.ts +27 -6
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +10 -19
- package/dist/unplugin/index.js.map +1 -1
- package/package.json +10 -25
- package/dist/attach-orchestrator-0F0m_UqQ.js +0 -1845
- package/dist/attach-orchestrator-0F0m_UqQ.js.map +0 -1
- package/dist/attach-orchestrator-D65KxFy_.js +0 -1831
- package/dist/attach-orchestrator-D65KxFy_.js.map +0 -1
- package/dist/attach-orchestrator-DL3NQ9ca.js +0 -1846
- package/dist/attach-orchestrator-DL3NQ9ca.js.map +0 -1
- package/dist/bundle-C796JIwG.d.ts +0 -159
- package/dist/bundle-C796JIwG.d.ts.map +0 -1
- package/dist/capture-DsP525OZ.d.ts +0 -58
- package/dist/capture-DsP525OZ.d.ts.map +0 -1
- package/dist/cdp-connection-rP1WdnH5.d.ts +0 -287
- package/dist/cdp-connection-rP1WdnH5.d.ts.map +0 -1
- package/dist/cell-BaLvusOl.js +0 -68
- package/dist/cell-BaLvusOl.js.map +0 -1
- package/dist/cell-CBUS3-nT.js +0 -274
- package/dist/cell-CBUS3-nT.js.map +0 -1
- package/dist/cell-EBKKpAAT.js +0 -307
- package/dist/cell-EBKKpAAT.js.map +0 -1
- package/dist/chii-relay-B3ZhjGMi.js +0 -304
- package/dist/chii-relay-B3ZhjGMi.js.map +0 -1
- package/dist/chii-relay-CGMlePMd.cjs +0 -304
- package/dist/chii-relay-CGMlePMd.cjs.map +0 -1
- package/dist/debug-server-B3ABDrRI.js +0 -456
- package/dist/debug-server-B3ABDrRI.js.map +0 -1
- package/dist/debug-server-BWhwrVXa.js +0 -1158
- package/dist/debug-server-BWhwrVXa.js.map +0 -1
- package/dist/debug-server-CfQNxxGW.js +0 -600
- package/dist/debug-server-CfQNxxGW.js.map +0 -1
- package/dist/devtools-opener-3Drge_RJ.js +0 -75
- package/dist/devtools-opener-3Drge_RJ.js.map +0 -1
- package/dist/devtools-opener-CJpEsXXQ.js +0 -76
- package/dist/devtools-opener-CJpEsXXQ.js.map +0 -1
- package/dist/devtools-opener-CxtryS8c.js +0 -75
- package/dist/devtools-opener-CxtryS8c.js.map +0 -1
- package/dist/in-app/auto.d.ts.map +0 -1
- package/dist/mcp/cli.d.ts.map +0 -1
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/pool-DcaaOwUq.d.ts +0 -14761
- package/dist/pool-DcaaOwUq.d.ts.map +0 -1
- package/dist/qr-http-server-C_lqOrgc.js +0 -1644
- package/dist/qr-http-server-C_lqOrgc.js.map +0 -1
- package/dist/qr-http-server-CopuMbub.js +0 -1644
- package/dist/qr-http-server-CopuMbub.js.map +0 -1
- package/dist/qr-http-server-DrbIVDjO.js +0 -1645
- package/dist/qr-http-server-DrbIVDjO.js.map +0 -1
- package/dist/relay-factory-N9QobQxG.js +0 -206
- package/dist/relay-factory-N9QobQxG.js.map +0 -1
- package/dist/relay-secret-store-BR0YIkNv.cjs +0 -241
- package/dist/relay-secret-store-BR0YIkNv.cjs.map +0 -1
- package/dist/relay-secret-store-Bmyleu0A.js +0 -154
- package/dist/relay-secret-store-Bmyleu0A.js.map +0 -1
- package/dist/relay-secret-store-CQenfcSL.js +0 -154
- package/dist/relay-secret-store-CQenfcSL.js.map +0 -1
- package/dist/relay-secret-store-CYM8CBIF.js +0 -240
- package/dist/relay-secret-store-CYM8CBIF.js.map +0 -1
- package/dist/relay-secret-store-DKxs7zwq.js +0 -153
- package/dist/relay-secret-store-DKxs7zwq.js.map +0 -1
- package/dist/relay-secret-store-WJ8EGkIl.js +0 -153
- package/dist/relay-secret-store-WJ8EGkIl.js.map +0 -1
- package/dist/relay-url-store-BR2XodiO.js +0 -123
- package/dist/relay-url-store-BR2XodiO.js.map +0 -1
- package/dist/relay-url-store-C1as_m5G.cjs +0 -115
- package/dist/relay-url-store-C1as_m5G.cjs.map +0 -1
- package/dist/relay-url-store-CH63fVCm.js +0 -122
- package/dist/relay-url-store-CH63fVCm.js.map +0 -1
- package/dist/relay-url-store-CzFo_84F.js +0 -114
- package/dist/relay-url-store-CzFo_84F.js.map +0 -1
- package/dist/relay-url-store-DaY1QPes.js +0 -123
- package/dist/relay-url-store-DaY1QPes.js.map +0 -1
- package/dist/relay-url-store-xmUuTjXA.js +0 -122
- package/dist/relay-url-store-xmUuTjXA.js.map +0 -1
- package/dist/relay-worker-B5HKkGUY.js +0 -832
- package/dist/relay-worker-B5HKkGUY.js.map +0 -1
- package/dist/relay-worker-YdlpZQl9.d.ts +0 -214
- package/dist/relay-worker-YdlpZQl9.d.ts.map +0 -1
- package/dist/rolldown-runtime-DGkTqVfb.js +0 -15
- package/dist/rolldown-runtime-DUslC3ob.js +0 -14
- package/dist/runtime-kn9DxOeg.d.ts +0 -249
- package/dist/runtime-kn9DxOeg.d.ts.map +0 -1
- package/dist/test-runner/bin.js +0 -2584
- package/dist/test-runner/bin.js.map +0 -1
- package/dist/test-runner/bridge-stub.d.ts +0 -125
- package/dist/test-runner/bridge-stub.d.ts.map +0 -1
- package/dist/test-runner/bridge-stub.js +0 -92
- package/dist/test-runner/bridge-stub.js.map +0 -1
- package/dist/test-runner/bundle.d.ts +0 -2
- package/dist/test-runner/bundle.js +0 -439
- package/dist/test-runner/bundle.js.map +0 -1
- package/dist/test-runner/capture.d.ts +0 -2
- package/dist/test-runner/capture.js +0 -44
- package/dist/test-runner/capture.js.map +0 -1
- package/dist/test-runner/config.d.ts.map +0 -1
- package/dist/test-runner/method-pace.d.ts +0 -82
- package/dist/test-runner/method-pace.d.ts.map +0 -1
- package/dist/test-runner/method-pace.js +0 -120
- package/dist/test-runner/method-pace.js.map +0 -1
- package/dist/test-runner/pool.d.ts +0 -2
- package/dist/test-runner/pool.js +0 -136
- package/dist/test-runner/pool.js.map +0 -1
- package/dist/test-runner/relay-factory.d.ts +0 -11245
- package/dist/test-runner/relay-factory.d.ts.map +0 -1
- package/dist/test-runner/relay-factory.js +0 -206
- package/dist/test-runner/relay-factory.js.map +0 -1
- package/dist/test-runner/relay-worker.d.ts +0 -2
- package/dist/test-runner/relay-worker.js +0 -2
- package/dist/test-runner/report.d.ts +0 -163
- package/dist/test-runner/report.d.ts.map +0 -1
- package/dist/test-runner/report.js +0 -198
- package/dist/test-runner/report.js.map +0 -1
- package/dist/test-runner/rpc.d.ts +0 -56
- package/dist/test-runner/rpc.d.ts.map +0 -1
- package/dist/test-runner/rpc.js +0 -98
- package/dist/test-runner/rpc.js.map +0 -1
- package/dist/test-runner/runtime.d.ts +0 -2
- package/dist/test-runner/runtime.js +0 -659
- package/dist/test-runner/runtime.js.map +0 -1
- package/dist/test-runner/task-graph.d.ts +0 -38
- package/dist/test-runner/task-graph.d.ts.map +0 -1
- package/dist/test-runner/task-graph.js +0 -182
- package/dist/test-runner/task-graph.js.map +0 -1
- package/dist/throttle-DKKzX1qC.js +0 -59
- package/dist/throttle-DKKzX1qC.js.map +0 -1
- package/dist/totp-BqmCLSNA.js +0 -189
- package/dist/totp-BqmCLSNA.js.map +0 -1
- package/dist/totp-CMHR5lsW.cjs +0 -191
- package/dist/totp-CMHR5lsW.cjs.map +0 -1
- package/dist/totp-CZLLKfOC.js +0 -200
- package/dist/totp-CZLLKfOC.js.map +0 -1
- package/dist/totp-DAxys-r0.js +0 -199
- package/dist/totp-DAxys-r0.js.map +0 -1
- package/dist/totp-DfekTBk3.js +0 -211
- package/dist/totp-DfekTBk3.js.map +0 -1
- package/dist/totp-Dwft0Kz7.js +0 -3
- package/dist/totp-WY6l0ysP.js +0 -190
- package/dist/totp-WY6l0ysP.js.map +0 -1
- /package/dist/{test-runner/bin.d.ts → stubs/bin-devtools-mcp.d.ts} +0 -0
|
@@ -1,600 +0,0 @@
|
|
|
1
|
-
import { a as ChiiCdpConnection, o as RELAY_AUTH_REJECT_CLOSE_CODE, s as RELAY_AUTH_REJECT_REASON } from "./relay-worker-B5HKkGUY.js";
|
|
2
|
-
import { c as generateAttachToken, d as startQuickTunnel, f as startTunnelHealthProbe, l as makeTunnelStatus, m as logInfo, p as logError, u as printAttachBanner } from "./attach-orchestrator-0F0m_UqQ.js";
|
|
3
|
-
import { n as buildRelayVerifyAuth, t as assertRelayAuthConfigured } from "./totp-DAxys-r0.js";
|
|
4
|
-
import "./cell-CBUS3-nT.js";
|
|
5
|
-
import "./relay-factory-N9QobQxG.js";
|
|
6
|
-
import "./qr-http-server-CopuMbub.js";
|
|
7
|
-
import "./relay-secret-store-WJ8EGkIl.js";
|
|
8
|
-
import { createRequire } from "node:module";
|
|
9
|
-
import "node:events";
|
|
10
|
-
import { WebSocketServer } from "ws";
|
|
11
|
-
import "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
-
import "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
-
import "@modelcontextprotocol/sdk/types.js";
|
|
14
|
-
import { createServer } from "node:http";
|
|
15
|
-
`
|
|
16
|
-
devtools-test — run mini-app tests on a real device WebView over the CDP relay
|
|
17
|
-
|
|
18
|
-
USAGE
|
|
19
|
-
devtools-test <glob> [<glob> ...] [options]
|
|
20
|
-
|
|
21
|
-
OPTIONS
|
|
22
|
-
--scheme-url <url> intoss-private:// URL from \`ait deploy --scheme-only\`
|
|
23
|
-
(required for standalone relay attach / env3). Unused
|
|
24
|
-
and ignored when --attach-launcher is set.
|
|
25
|
-
--attach-launcher Attach over the env-2 AITC Sandbox PWA launcher
|
|
26
|
-
(real-device WebKit) instead of the env-3 intoss
|
|
27
|
-
scheme deep-link. The relay/QR/dashboard/attach-wait
|
|
28
|
-
are identical; only the QR is a launcher deep-link.
|
|
29
|
-
Requires --app-url. The SDK still hits the mock in
|
|
30
|
-
env 2, so this measures the mock + standard Web API
|
|
31
|
-
layer's engine-attributable behavior (env1↔env2
|
|
32
|
-
equivalence), NOT native-bridge fidelity (that is env3).
|
|
33
|
-
--app-url <url> The consumer dev server's HTTP tunnel URL (e.g. the
|
|
34
|
-
*.trycloudflare.com URL from \`pnpm dev:phone:cdp\`)
|
|
35
|
-
that the launcher PWA frames. REQUIRED with
|
|
36
|
-
--attach-launcher; ignored otherwise. SECRET: a tunnel
|
|
37
|
-
host — never printed; it rides only inside the QR.
|
|
38
|
-
--timeout <ms> Per-file evaluate timeout in ms (default: 60000).
|
|
39
|
-
Controls how long a single test file is allowed to run
|
|
40
|
-
before it is considered hung. Does NOT affect how long
|
|
41
|
-
the CLI waits for a human to scan the QR code — use
|
|
42
|
-
--attach-timeout for that.
|
|
43
|
-
--attach-timeout <ms> How long to wait for a human to scan the QR code with
|
|
44
|
-
their phone. Omit (default) to wait indefinitely — the
|
|
45
|
-
runner stays up until you stop it (Ctrl-C/SIGTERM).
|
|
46
|
-
Pass a value to bound the wait for CI/headless runs.
|
|
47
|
-
--cell-sdk-line <line> SDK line to inject as __AIT_CELL__.sdkLine (2.x|3.x)
|
|
48
|
-
--cell-platform <plat> Platform to inject as __AIT_CELL__.platform
|
|
49
|
-
(mock|ios|android|ios-pwa, default: AIT_CELL_PLATFORM
|
|
50
|
-
env). Use ios-pwa for env-2 (--attach-launcher) runs
|
|
51
|
-
so env1(mock@desktop) and env2(mock@WebKit) captures
|
|
52
|
-
stay distinguishable. An unknown value is rejected.
|
|
53
|
-
--report-dir <dir> Persist a runner-agnostic report + captures to <dir>
|
|
54
|
-
(report: <sdkLine>.<platform>.json; captures:
|
|
55
|
-
<dir>/.ait-capture/<category>.<sdkLine>.<platform>.json).
|
|
56
|
-
Omitted = nothing saved. Enables console capture.
|
|
57
|
-
--dashboard-port <port> Base port for the QR dashboard HTTP server. On
|
|
58
|
-
EADDRINUSE it increments (+1, up to 20 tries) before
|
|
59
|
-
falling back to an ephemeral port. Omit to use
|
|
60
|
-
AIT_DEBUG_HTTP_PORT env or the built-in default
|
|
61
|
-
(8317) — pass 0 to force a random ephemeral port.
|
|
62
|
-
--no-qr-stdout Suppress the QR/attach block on stdout (auto-on for
|
|
63
|
-
non-interactive stdout / CI / AIT_NO_QR_STDOUT)
|
|
64
|
-
--headless Disable browser auto-open (text QR only)
|
|
65
|
-
--project-root <dir> Project root for .ait_relay secret lookup
|
|
66
|
-
(default: current working directory)
|
|
67
|
-
--pace <ms> Minimum delay in ms between test-to-test AND
|
|
68
|
-
file-to-file bridge calls (default: 0, i.e. no
|
|
69
|
-
added delay — today's behavior byte-for-byte).
|
|
70
|
-
Falls back to the AIT_PACE env var when omitted
|
|
71
|
-
(--pace takes precedence over the env var when both
|
|
72
|
-
are given). Use on a 2.x cell scan when the native
|
|
73
|
-
per-method bridge rate limit (APP_BRIDGE_THROTTLED,
|
|
74
|
-
devtools#767) is rejecting rapid same-method calls —
|
|
75
|
-
3.x cells are unaffected by that limiter and do not
|
|
76
|
-
need this flag.
|
|
77
|
-
--pace-method <ms> Minimum delay in ms BETWEEN calls to the SAME named
|
|
78
|
-
SDK function — paces a same-method burst WITHIN a
|
|
79
|
-
single test body (e.g. a clipboard happy-path loop
|
|
80
|
-
calling setClipboardText/getClipboardText 8 times
|
|
81
|
-
back to back), which --pace's test/file spacing
|
|
82
|
-
cannot reach (devtools#769). Falls back to the
|
|
83
|
-
AIT_PACE_METHOD env var when omitted (--pace-method
|
|
84
|
-
takes precedence over the env var when both are
|
|
85
|
-
given). Default: 250ms when --cell-sdk-line is 2.x
|
|
86
|
-
(unset --cell-sdk-line also defaults to 2.x — see
|
|
87
|
-
--cell-sdk-line), 0 (no added delay) otherwise. Pass
|
|
88
|
-
--pace-method 0 to opt out even on a 2.x cell.
|
|
89
|
-
--manual-blocking Run manual-tagged test files (*.manual.ait.test.ts)
|
|
90
|
-
LAST, after all regular files, with a human present.
|
|
91
|
-
Before each manual file, the QR dashboard is pushed
|
|
92
|
-
a step-by-step Korean prompt naming the file + its
|
|
93
|
-
progress (k/n), and the same line is printed to
|
|
94
|
-
stdout. Manual files get a 5-minute per-file evaluate
|
|
95
|
-
timeout (vs. --timeout for everything else) since a
|
|
96
|
-
human is expected to tap through a native sheet
|
|
97
|
-
(photo picker, permission dialog, fullscreen ad).
|
|
98
|
-
Without this flag (default off), *.manual.ait.test.ts
|
|
99
|
-
files are EXCLUDED from the glob expansion entirely —
|
|
100
|
-
existing unattended runs are byte-for-byte unaffected.
|
|
101
|
-
With --report-dir, a run that included manual files
|
|
102
|
-
ALSO writes <sdkLine>.<platform>.manual.json
|
|
103
|
-
alongside (never replacing) the standard report, and
|
|
104
|
-
each manual file's report entry is stamped
|
|
105
|
-
mode: 'manual' — never diff a manual run against an
|
|
106
|
-
unattended baseline as if they were equivalent.
|
|
107
|
-
--stub-blocking Run manual-tagged test files (*.manual.ait.test.ts)
|
|
108
|
-
UNATTENDED (devtools#740, DT-2) by intercepting a
|
|
109
|
-
fixed allowlist of blocking-UI SDK calls (ads
|
|
110
|
-
show*, openPermissionDialog/requestPermission,
|
|
111
|
-
saveBase64Data) in the page and answering them from
|
|
112
|
-
fixtures captured by a real --manual-blocking run,
|
|
113
|
-
instead of forwarding them to native UI. Implies
|
|
114
|
-
--manual-blocking (manual files are included in the
|
|
115
|
-
run); no human presence or QR-dashboard prompt is
|
|
116
|
-
needed for them. HYBRID cell, not pure env3 — every
|
|
117
|
-
other SDK call in the same run still hits the real
|
|
118
|
-
native bridge. With --report-dir, files that ran
|
|
119
|
-
under the stub are written to a SEPARATE
|
|
120
|
-
<sdkLine>.<platform>.stubbed.json artifact (never
|
|
121
|
-
merged into the standard or .manual.json report) and
|
|
122
|
-
the report body is stamped cell.bridgeStub: true —
|
|
123
|
-
never diff a stubbed run against a real device
|
|
124
|
-
baseline (manual or unattended) as if equivalent.
|
|
125
|
-
--help, -h Show this help message
|
|
126
|
-
|
|
127
|
-
DESCRIPTION
|
|
128
|
-
Boots a Chii relay + cloudflared tunnel, renders a QR code, waits for a real
|
|
129
|
-
device to scan and attach, injects the cell globals (__AIT_CELL__), bundles
|
|
130
|
-
each matched test file with esbuild (SDK imports redirected to window.__sdk),
|
|
131
|
-
injects the bundle into the attached WebView via Runtime.evaluate, and prints
|
|
132
|
-
a summary.
|
|
133
|
-
|
|
134
|
-
With --report-dir, also harvests __AIT_CAPTURE__ console lines and writes a
|
|
135
|
-
runner-agnostic report + per-category capture files so 2.x↔3.0 runs can be
|
|
136
|
-
compared offline.
|
|
137
|
-
|
|
138
|
-
The test files run against the live relay connection started by this process;
|
|
139
|
-
no separate MCP daemon is required.
|
|
140
|
-
|
|
141
|
-
EXAMPLE (env 3 — intoss-private scheme)
|
|
142
|
-
devtools-test 'src/**/*.ait.test.ts' \\
|
|
143
|
-
--scheme-url "intoss-private://..." \\
|
|
144
|
-
--cell-sdk-line 3.x \\
|
|
145
|
-
--cell-platform ios \\
|
|
146
|
-
--report-dir .ait-report \\
|
|
147
|
-
--timeout 60000
|
|
148
|
-
|
|
149
|
-
EXAMPLE (env 2 — AITC Sandbox PWA launcher)
|
|
150
|
-
devtools-test 'src/**/*.ait.test.ts' \\
|
|
151
|
-
--attach-launcher \\
|
|
152
|
-
--app-url "https://<subdomain>.trycloudflare.com" \\
|
|
153
|
-
--cell-sdk-line 3.x \\
|
|
154
|
-
--cell-platform ios-pwa \\
|
|
155
|
-
--report-dir .ait-report
|
|
156
|
-
|
|
157
|
-
`.trimStart();
|
|
158
|
-
//#endregion
|
|
159
|
-
//#region src/mcp/chii-relay.ts
|
|
160
|
-
/**
|
|
161
|
-
* Boots the local Chii relay server.
|
|
162
|
-
*
|
|
163
|
-
* Chii (liriliri/chii) is a chobitsu-based CDP relay that lets non-Chrome
|
|
164
|
-
* WebViews (iOS WKWebView / Android WebView — i.e. the Toss app) expose CDP.
|
|
165
|
-
* The relay accepts a `target` websocket from the phone's injected `target.js`
|
|
166
|
-
* and `client` websockets from CDP frontends (our MCP connection).
|
|
167
|
-
*
|
|
168
|
-
* Node-only: `chii` pulls in Koa + ws. Never bundled into the browser/in-app
|
|
169
|
-
* entries.
|
|
170
|
-
*
|
|
171
|
-
* TOTP auth (relay-side, authoritative gate):
|
|
172
|
-
* When `verifyAuth` is provided, this module gates both inbound surfaces:
|
|
173
|
-
*
|
|
174
|
-
* - HTTP 'request': a listener registered BEFORE `chii.start({server})`.
|
|
175
|
-
* Node's `http.Server` calls listeners in registration order; the first
|
|
176
|
-
* to call `res.end()` wins. Invalid auth → 401 + CORS header + a tiny
|
|
177
|
-
* JSON body (`{"error":"totp-rejected"}`) so a cross-origin script
|
|
178
|
-
* `fetch()` probe can READ the status (issue #478). Valid auth → return
|
|
179
|
-
* without side-effect (chii's Koa handler serves it).
|
|
180
|
-
*
|
|
181
|
-
* - WS 'upgrade': after `chii.start()` has registered chii's own upgrade
|
|
182
|
-
* listener, we take over the upgrade chain (remove chii's listeners,
|
|
183
|
-
* re-dispatch manually). Invalid auth → accept-then-close: complete the
|
|
184
|
-
* handshake via a `noServer` WebSocketServer, then immediately close
|
|
185
|
-
* with code 4401 reason 'totp-rejected' (issue #478). A raw 401 +
|
|
186
|
-
* `socket.destroy()` only ever surfaced as close code 1006 in the
|
|
187
|
-
* browser — indistinguishable from a tunnel failure, which left the
|
|
188
|
-
* env-2 phone UI silent. The explicit dispatch (not listener ordering)
|
|
189
|
-
* is what keeps chii away from rejected sockets: accept-then-close
|
|
190
|
-
* leaves the socket alive, so an order-based early-return would let
|
|
191
|
-
* chii's later listener complete a SECOND handshake on the same socket
|
|
192
|
-
* — an auth bypass. Valid auth → forward to chii's captured listeners.
|
|
193
|
-
*
|
|
194
|
-
* TOTP code transports (issue #466) — two equivalent ways to carry the code:
|
|
195
|
-
* 1. Query param `at=<code>` — used by the daemon-side `/client` connection
|
|
196
|
-
* (`chii-connection.ts` appends it; it holds the secret).
|
|
197
|
-
* 2. Path prefix `/at/<code>/…` — used by the phone-side target. Chii's
|
|
198
|
-
* stock `target.js` derives its WS endpoint from the script `src`
|
|
199
|
-
* (`scriptEl.src.replace('target.js','')`), so the only way for the
|
|
200
|
-
* phone to carry a code is to embed it in the script URL path. The
|
|
201
|
-
* in-app attach injects `https://<host>/at/<code>/target.js`; both the
|
|
202
|
-
* script fetch and the derived `wss://<host>/at/<code>/target/<id>` WS
|
|
203
|
-
* dial then carry the prefix. The listeners below rewrite the prefix
|
|
204
|
-
* into the query form (`rewriteAtPathPrefix`) and MUTATE `req.url`
|
|
205
|
-
* before chii's own handlers (registered later) parse it — chii only
|
|
206
|
-
* ever sees the stripped URL.
|
|
207
|
-
*
|
|
208
|
-
* Threat model: "URL leak" — someone obtains the tunnel URL (Slack paste, QR
|
|
209
|
-
* screenshot, shoulder-surfing) but does not have the shared TOTP secret.
|
|
210
|
-
* Rotating 6-digit code makes the URL stale after 30 s.
|
|
211
|
-
* A determined attacker who extracts the secret from the dogfood bundle can
|
|
212
|
-
* still compute valid codes; that is out of scope (see umbrella CLAUDE.md §4).
|
|
213
|
-
*
|
|
214
|
-
* SECRET-HANDLING: The secret value and computed TOTP codes MUST NOT appear
|
|
215
|
-
* in any log, error message, or process output. `verifyAuth` is a black-box
|
|
216
|
-
* predicate from the caller's perspective; this module only forwards pass/fail.
|
|
217
|
-
*/
|
|
218
|
-
const require = createRequire(import.meta.url);
|
|
219
|
-
/**
|
|
220
|
-
* WS keepalive ping interval (ms).
|
|
221
|
-
*
|
|
222
|
-
* Cloudflare proxied connections are dropped after ~100 s of no traffic.
|
|
223
|
-
* 45 s comfortably fits inside that window and lets both the phone-target leg
|
|
224
|
-
* and the daemon-client leg survive idle CDP sessions.
|
|
225
|
-
*/
|
|
226
|
-
const DEFAULT_KEEPALIVE_INTERVAL_MS = 45e3;
|
|
227
|
-
/**
|
|
228
|
-
* Loads chii's internal WebSocketServer class and returns it together with a
|
|
229
|
-
* flag indicating whether the real class was found.
|
|
230
|
-
*
|
|
231
|
-
* Returns `null` if the internal path is not resolvable (future chii release
|
|
232
|
-
* changes the layout) — callers skip keepalive gracefully.
|
|
233
|
-
*/
|
|
234
|
-
function tryLoadChiiWssClass() {
|
|
235
|
-
try {
|
|
236
|
-
const mod = require("chii/server/lib/WebSocketServer");
|
|
237
|
-
if (typeof mod === "function") return mod;
|
|
238
|
-
} catch {}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Calls `chii.start()` and returns the chii `WebSocketServer` instance that
|
|
243
|
-
* was constructed during the call.
|
|
244
|
-
*
|
|
245
|
-
* How: `chii/server/index.js`'s `start()` creates `new WebSocketServer()`
|
|
246
|
-
* where `WebSocketServer` is captured from `require('./lib/WebSocketServer')`
|
|
247
|
-
* at module load time. The class reference is stable, so we can temporarily
|
|
248
|
-
* patch `ChiiWssClass.prototype.start` — which runs *on the instance* —
|
|
249
|
-
* to record `this` before the original `start` runs.
|
|
250
|
-
*
|
|
251
|
-
* The patch is installed before `chii.start()` and removed (via `finally`)
|
|
252
|
-
* immediately after, so concurrent `startChiiRelay` calls nest correctly: each
|
|
253
|
-
* call's patch overrides the previous in the prototype chain for the duration
|
|
254
|
-
* of its own `chii.start()` call, restoring the prior descriptor on exit.
|
|
255
|
-
*
|
|
256
|
-
* If `ChiiWssClass` is null (internal path changed in a future chii release),
|
|
257
|
-
* `chii.start()` runs unpatched and the function returns null — callers skip
|
|
258
|
-
* keepalive gracefully without affecting relay correctness.
|
|
259
|
-
*/
|
|
260
|
-
async function startChiiWithCapture(chii, startOptions, ChiiWssClass) {
|
|
261
|
-
if (ChiiWssClass === null) {
|
|
262
|
-
await chii.start(startOptions);
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
let captured = null;
|
|
266
|
-
const proto = ChiiWssClass.prototype;
|
|
267
|
-
const originalStart = proto.start;
|
|
268
|
-
proto.start = function(server) {
|
|
269
|
-
captured = this;
|
|
270
|
-
return originalStart.call(this, server);
|
|
271
|
-
};
|
|
272
|
-
try {
|
|
273
|
-
await chii.start(startOptions);
|
|
274
|
-
} finally {
|
|
275
|
-
proto.start = originalStart;
|
|
276
|
-
}
|
|
277
|
-
return captured;
|
|
278
|
-
}
|
|
279
|
-
function loadChiiServer() {
|
|
280
|
-
const mod = require("chii");
|
|
281
|
-
if (typeof mod === "object" && mod !== null && "start" in mod && typeof mod.start === "function") return mod;
|
|
282
|
-
throw new Error("chii server module did not expose start()");
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Rewrites a `/at/<code>/…` path-prefixed request URL into the equivalent
|
|
286
|
-
* query-based form, e.g.:
|
|
287
|
-
*
|
|
288
|
-
* `/at/123456/target.js` → `/target.js?at=123456`
|
|
289
|
-
* `/at/123456/target/x?url=u` → `/target/x?url=u&at=123456`
|
|
290
|
-
* `/at/123456/` → `/?at=123456`
|
|
291
|
-
*
|
|
292
|
-
* Returns `null` when the URL does not carry the prefix (including an empty
|
|
293
|
-
* code segment) — callers fall back to the unmodified URL and the existing
|
|
294
|
-
* query-based auth path.
|
|
295
|
-
*
|
|
296
|
-
* Pure string surgery — this function knows nothing about secrets or code
|
|
297
|
-
* validity; verification stays inside the caller-provided `verifyAuth`
|
|
298
|
-
* predicate (which parses the query). The raw path segment is appended
|
|
299
|
-
* verbatim to the query: both path segments and query values are
|
|
300
|
-
* percent-decoded exactly once by their consumers, so no re-encoding is
|
|
301
|
-
* needed (TOTP codes are 6 digits and never percent-encoded in practice).
|
|
302
|
-
*/
|
|
303
|
-
function rewriteAtPathPrefix(rawUrl) {
|
|
304
|
-
const match = /^\/at\/([^/?]+)(\/[^?]*)?(\?.*)?$/.exec(rawUrl);
|
|
305
|
-
if (match === null) return null;
|
|
306
|
-
const code = match[1];
|
|
307
|
-
const path = match[2] === void 0 || match[2] === "" ? "/" : match[2];
|
|
308
|
-
const query = match[3] ?? "";
|
|
309
|
-
return `${path}${query}${query === "" ? "?" : "&"}at=${code}`;
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Starts the Chii relay and resolves once listening.
|
|
313
|
-
*
|
|
314
|
-
* Default port is 0 (OS-assigned). With port 0 the OS picks a free ephemeral
|
|
315
|
-
* port on every start, so a stale cloudflared orphan holding any particular
|
|
316
|
-
* port cannot cause EADDRINUSE. The resolved `ChiiRelay.port` and `baseUrl`
|
|
317
|
-
* always reflect the actual bound port.
|
|
318
|
-
*
|
|
319
|
-
* chii.start() is called with `server` (our pre-created httpServer) BEFORE
|
|
320
|
-
* httpServer.listen(). This is intentional: chii attaches its Koa handler and
|
|
321
|
-
* WS upgrade listener to the server object, but the actual TCP bind is
|
|
322
|
-
* performed by our httpServer.listen() call below. The `port`/`domain` values
|
|
323
|
-
* passed to chii.start() are used for display/banner purposes inside chii and
|
|
324
|
-
* do not affect which port the server binds. The connection path (clients
|
|
325
|
-
* connecting to `relay.baseUrl`) always uses the post-listen confirmed port.
|
|
326
|
-
*/
|
|
327
|
-
async function startChiiRelay(options = {}) {
|
|
328
|
-
const requestedPort = options.port ?? 0;
|
|
329
|
-
const host = options.host ?? "127.0.0.1";
|
|
330
|
-
const { verifyAuth, onAuthReject } = options;
|
|
331
|
-
const keepaliveIntervalMs = options.keepaliveIntervalMs !== void 0 ? options.keepaliveIntervalMs : DEFAULT_KEEPALIVE_INTERVAL_MS;
|
|
332
|
-
const httpServer = createServer();
|
|
333
|
-
const notifyAuthReject = (kind) => {
|
|
334
|
-
if (onAuthReject === void 0) return;
|
|
335
|
-
try {
|
|
336
|
-
onAuthReject({ kind });
|
|
337
|
-
} catch {}
|
|
338
|
-
};
|
|
339
|
-
if (verifyAuth) httpServer.on("request", (req, res) => {
|
|
340
|
-
const rewritten = rewriteAtPathPrefix(req.url ?? "");
|
|
341
|
-
if (rewritten !== null) {
|
|
342
|
-
req.url = rewritten;
|
|
343
|
-
if (!verifyAuth(req)) {
|
|
344
|
-
res.statusCode = 401;
|
|
345
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
346
|
-
res.setHeader("Content-Type", "application/json");
|
|
347
|
-
res.end(JSON.stringify({ error: RELAY_AUTH_REJECT_REASON }));
|
|
348
|
-
notifyAuthReject("http-request");
|
|
349
|
-
}
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
const pathname = (req.url ?? "").split("?")[0];
|
|
353
|
-
if (pathname === "/targets" || pathname === "/targets/") {
|
|
354
|
-
if (!verifyAuth(req)) {
|
|
355
|
-
res.statusCode = 401;
|
|
356
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
357
|
-
res.setHeader("Content-Type", "application/json");
|
|
358
|
-
res.end(JSON.stringify({ error: RELAY_AUTH_REJECT_REASON }));
|
|
359
|
-
notifyAuthReject("http-request");
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
const chiiWssClass = keepaliveIntervalMs > 0 ? tryLoadChiiWssClass() : null;
|
|
366
|
-
const capturedChiiWss = await startChiiWithCapture(loadChiiServer(), {
|
|
367
|
-
server: httpServer,
|
|
368
|
-
domain: `${host}:${requestedPort}`,
|
|
369
|
-
port: requestedPort
|
|
370
|
-
}, chiiWssClass);
|
|
371
|
-
if (verifyAuth) {
|
|
372
|
-
const chiiUpgradeListeners = httpServer.listeners("upgrade");
|
|
373
|
-
httpServer.removeAllListeners("upgrade");
|
|
374
|
-
const rejectWss = new WebSocketServer({ noServer: true });
|
|
375
|
-
httpServer.on("upgrade", (req, socket, head) => {
|
|
376
|
-
const rewritten = rewriteAtPathPrefix(req.url ?? "");
|
|
377
|
-
if (rewritten !== null) req.url = rewritten;
|
|
378
|
-
if (!verifyAuth(req)) {
|
|
379
|
-
rejectWss.handleUpgrade(req, socket, head, (ws) => {
|
|
380
|
-
ws.close(RELAY_AUTH_REJECT_CLOSE_CODE, RELAY_AUTH_REJECT_REASON);
|
|
381
|
-
});
|
|
382
|
-
notifyAuthReject("ws-upgrade");
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
for (const listener of chiiUpgradeListeners) listener(req, socket, head);
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
const actualPort = await new Promise((resolve, reject) => {
|
|
389
|
-
httpServer.once("error", reject);
|
|
390
|
-
httpServer.listen(requestedPort, host, () => {
|
|
391
|
-
httpServer.off("error", reject);
|
|
392
|
-
resolve(httpServer.address().port);
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
let keepaliveHandle = null;
|
|
396
|
-
if (keepaliveIntervalMs > 0 && capturedChiiWss !== null) {
|
|
397
|
-
const chiiWss = capturedChiiWss;
|
|
398
|
-
keepaliveHandle = setInterval(() => {
|
|
399
|
-
for (const client of chiiWss._wss.clients) if (client.readyState === 1) client.ping();
|
|
400
|
-
}, keepaliveIntervalMs);
|
|
401
|
-
}
|
|
402
|
-
return {
|
|
403
|
-
port: actualPort,
|
|
404
|
-
baseUrl: `http://${host}:${actualPort}`,
|
|
405
|
-
close: () => new Promise((resolve) => {
|
|
406
|
-
if (keepaliveHandle !== null) {
|
|
407
|
-
clearInterval(keepaliveHandle);
|
|
408
|
-
keepaliveHandle = null;
|
|
409
|
-
}
|
|
410
|
-
if (capturedChiiWss !== null) for (const client of capturedChiiWss._wss.clients) client.terminate();
|
|
411
|
-
httpServer.close(() => resolve());
|
|
412
|
-
})
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
//#endregion
|
|
416
|
-
//#region src/mcp/debug-server.ts
|
|
417
|
-
/**
|
|
418
|
-
* Starts a polling watcher that detects target-set changes on
|
|
419
|
-
* `connection.listTargets()` and sends a `notifications/tools/list_changed`
|
|
420
|
-
* notification on the given server.
|
|
421
|
-
*
|
|
422
|
-
* The watcher polls every `intervalMs` (default 1 000 ms). On each tick it
|
|
423
|
-
* calls `connection.refreshTargets?.()` first (fix #705-B) so that silent
|
|
424
|
-
* disconnects (no CDP event, phone backgrounded / tunnel quiet) are picked up
|
|
425
|
-
* before the signature is read. If `refreshTargets` throws — e.g. a transient
|
|
426
|
-
* relay error — the tick is skipped entirely to avoid a spurious detach signal.
|
|
427
|
-
*
|
|
428
|
-
* After the refresh, it fires `server.sendToolListChanged()` + `onAttach()`
|
|
429
|
-
* whenever the sorted target-id signature changes AND the new target set is
|
|
430
|
-
* non-empty. This covers:
|
|
431
|
-
* - 0→N first attach
|
|
432
|
-
* - 1→1 target replacement (same count, different id — e.g. rescan)
|
|
433
|
-
* - N→M any change where the result is still non-empty
|
|
434
|
-
*
|
|
435
|
-
* Full detach (→ empty) fires `onDetach()` (fix #705-A) on the exact
|
|
436
|
-
* non-empty→empty edge — i.e. only when the previous signature was non-empty.
|
|
437
|
-
* This lets callers push an immediate "disconnected" SSE update to the
|
|
438
|
-
* dashboard without waiting for the next periodic interval.
|
|
439
|
-
*
|
|
440
|
-
* The interval is **never cleared automatically** — it keeps running until
|
|
441
|
-
* `stop()` is called during shutdown. This ensures that a target replacement
|
|
442
|
-
* after the first attach is always detected.
|
|
443
|
-
*
|
|
444
|
-
* `onAttach` is called on every non-empty signature change (or immediately when
|
|
445
|
-
* already attached). Use this to trigger side-effects such as pushing a fresh
|
|
446
|
-
* SSE state to open dashboard tabs (issue #509). Both callbacks are optional;
|
|
447
|
-
* omitting them preserves the previous behaviour exactly.
|
|
448
|
-
*
|
|
449
|
-
* SECRET-HANDLING: target `id`/`title`/`url` are not written to any log here.
|
|
450
|
-
* Only an attach-detected stderr line is emitted (no target details).
|
|
451
|
-
*
|
|
452
|
-
* `server` is optional (devtools#772): the standalone test-runner path
|
|
453
|
-
* (`test-runner/relay-factory.ts`) has no MCP `Server` instance to notify —
|
|
454
|
-
* it only needs the `onAttach`/`onDetach` dashboard-push side effects. When
|
|
455
|
-
* omitted, `sendToolListChanged()` is simply skipped; the signature-diff and
|
|
456
|
-
* callback logic is byte-for-byte the same. Existing MCP daemon call sites
|
|
457
|
-
* always pass a real `Server`, so their behavior is unchanged.
|
|
458
|
-
*
|
|
459
|
-
* @returns `stop` — call this during shutdown to clear the interval.
|
|
460
|
-
*/
|
|
461
|
-
function startAttachWatcher(connection, server, intervalMs = 1e3, onAttach, onDetach) {
|
|
462
|
-
/** Sorted, comma-joined target-id string — '' means no targets attached. */
|
|
463
|
-
function signature() {
|
|
464
|
-
return connection.listTargets().map((t) => t.id).sort().join(",");
|
|
465
|
-
}
|
|
466
|
-
let lastSignature = signature();
|
|
467
|
-
if (lastSignature !== "") {
|
|
468
|
-
server?.sendToolListChanged();
|
|
469
|
-
onAttach?.();
|
|
470
|
-
}
|
|
471
|
-
/** Compare current vs last signature and fire the appropriate callback. */
|
|
472
|
-
function tick() {
|
|
473
|
-
const current = signature();
|
|
474
|
-
if (current !== lastSignature) {
|
|
475
|
-
const wasNonEmpty = lastSignature !== "";
|
|
476
|
-
lastSignature = current;
|
|
477
|
-
if (current !== "") {
|
|
478
|
-
server?.sendToolListChanged();
|
|
479
|
-
onAttach?.();
|
|
480
|
-
} else if (wasNonEmpty) onDetach?.();
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
const handle = setInterval(() => {
|
|
484
|
-
if (connection.refreshTargets) connection.refreshTargets().then(() => {
|
|
485
|
-
tick();
|
|
486
|
-
}, (_err) => {});
|
|
487
|
-
else tick();
|
|
488
|
-
}, intervalMs);
|
|
489
|
-
return { stop() {
|
|
490
|
-
clearInterval(handle);
|
|
491
|
-
} };
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Factory that constructs a `ChiiCdpConnection` for the given relay base URL.
|
|
495
|
-
*
|
|
496
|
-
* Introduced as a named seam so PR-2 (dual-connection, #348) can defer
|
|
497
|
-
* construction to first-activation time by moving or replacing this call. Since
|
|
498
|
-
* #396 every family (relay included) is constructed lazily on its first
|
|
499
|
-
* `start_debug`, so this is always called from the lazy boot path.
|
|
500
|
-
*
|
|
501
|
-
* The relay base URL is only available after `startChiiRelay()` resolves, so
|
|
502
|
-
* the factory is called right after that point (same as before this refactor).
|
|
503
|
-
*/
|
|
504
|
-
function createRelayConnection(relayBaseUrl) {
|
|
505
|
-
return new ChiiCdpConnection({
|
|
506
|
-
relayBaseUrl,
|
|
507
|
-
totpSecret: process.env.AIT_DEBUG_TOTP_SECRET
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* Boots the relay family (issues #348, #356): starts the Chii relay on an
|
|
512
|
-
* OS-assigned port (with optional TOTP gate), opens a cloudflared quick tunnel
|
|
513
|
-
* to the relay's confirmed port in the background, prints the attach banner,
|
|
514
|
-
* and arms the tunnel health probe. Returns a {@link BootedFamily} whose
|
|
515
|
-
* `getTunnelStatus()` reflects the live tunnel (it flips up once the background
|
|
516
|
-
* tunnel resolves and follows reissues).
|
|
517
|
-
*
|
|
518
|
-
* Booted lazily via the dual router's `bootLazyFor('relay-intoss')` callback
|
|
519
|
-
* (symmetry with {@link bootLocalFamily}), at most once on the first
|
|
520
|
-
* `start_debug({ mode: 'relay-staging' })` (all-lazy, #396 — every relay boot now
|
|
521
|
-
* flows through `switchMode` after the project-local secret load). `relay-live`
|
|
522
|
-
* removed (#665).
|
|
523
|
-
*
|
|
524
|
-
* The relay base URL is only known after `startChiiRelay()` resolves, so the
|
|
525
|
-
* `ChiiCdpConnection` (via {@link createRelayConnection}) is constructed inside
|
|
526
|
-
* this function, after the relay port is confirmed.
|
|
527
|
-
*
|
|
528
|
-
* SECRET-HANDLING: the TOTP secret rides only inside `verifyAuth`; the wssUrl
|
|
529
|
-
* (relay host) is never logged here directly.
|
|
530
|
-
*/
|
|
531
|
-
async function bootRelayFamily(options = {}) {
|
|
532
|
-
assertRelayAuthConfigured();
|
|
533
|
-
const relayPort = options.relayPort ?? 0;
|
|
534
|
-
const totpEnabled = options.verifyAuth !== void 0;
|
|
535
|
-
const relay = await startChiiRelay({
|
|
536
|
-
port: relayPort,
|
|
537
|
-
verifyAuth: options.verifyAuth,
|
|
538
|
-
onAuthReject: options.onAuthReject
|
|
539
|
-
});
|
|
540
|
-
logInfo("server.start", {
|
|
541
|
-
port: relay.port,
|
|
542
|
-
totpEnabled
|
|
543
|
-
});
|
|
544
|
-
let tunnel = null;
|
|
545
|
-
let tunnelStatus = makeTunnelStatus(false, null);
|
|
546
|
-
let tunnelProbe = null;
|
|
547
|
-
generateAttachToken();
|
|
548
|
-
startQuickTunnel(relay.port).then((t) => {
|
|
549
|
-
tunnel = t;
|
|
550
|
-
tunnelStatus = makeTunnelStatus(true, t.wssUrl);
|
|
551
|
-
options.onWssUrl?.(t.wssUrl);
|
|
552
|
-
if (t.childPid !== void 0) options.onTunnelChildPid?.(t.childPid);
|
|
553
|
-
logInfo("tunnel.up", { totpEnabled });
|
|
554
|
-
tunnelProbe = startTunnelHealthProbe(t, relay.port, {
|
|
555
|
-
onReissue: (newTunnel) => {
|
|
556
|
-
tunnel = newTunnel;
|
|
557
|
-
tunnelStatus = makeTunnelStatus(true, newTunnel.wssUrl, null, 0);
|
|
558
|
-
options.onWssUrl?.(newTunnel.wssUrl);
|
|
559
|
-
if (newTunnel.childPid !== void 0) options.onTunnelChildPid?.(newTunnel.childPid);
|
|
560
|
-
printAttachBanner({
|
|
561
|
-
wssUrl: newTunnel.wssUrl,
|
|
562
|
-
totpEnabled
|
|
563
|
-
}).then(() => {
|
|
564
|
-
logInfo("tunnel.up", {
|
|
565
|
-
totpEnabled,
|
|
566
|
-
reissued: true
|
|
567
|
-
});
|
|
568
|
-
});
|
|
569
|
-
},
|
|
570
|
-
onPermanentDrop: (droppedAt) => {
|
|
571
|
-
tunnelStatus = makeTunnelStatus(false, null, droppedAt, 3);
|
|
572
|
-
logError("tunnel.down", { msg: `tunnel permanently dropped (${droppedAt}). Restart: npx @ait-co/devtools devtools-mcp` });
|
|
573
|
-
options.onTunnelDown?.();
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
|
-
return printAttachBanner({
|
|
577
|
-
wssUrl: t.wssUrl,
|
|
578
|
-
totpEnabled
|
|
579
|
-
});
|
|
580
|
-
}, (err) => {
|
|
581
|
-
logError("tunnel.down", { msg: `Failed to open cloudflared quick tunnel: ${err instanceof Error ? err.message : String(err)}. The relay is up locally; attach over the public URL is unavailable until the tunnel starts.` });
|
|
582
|
-
});
|
|
583
|
-
const connection = createRelayConnection(relay.baseUrl);
|
|
584
|
-
return {
|
|
585
|
-
connection,
|
|
586
|
-
relayOrigin: "intoss-webview",
|
|
587
|
-
relayHttpUrl: relay.baseUrl,
|
|
588
|
-
getTunnelStatus: () => tunnelStatus,
|
|
589
|
-
stop() {
|
|
590
|
-
tunnelProbe?.stop();
|
|
591
|
-
tunnel?.stop();
|
|
592
|
-
connection.close();
|
|
593
|
-
relay.close();
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
//#endregion
|
|
598
|
-
export { bootRelayFamily, buildRelayVerifyAuth, startAttachWatcher };
|
|
599
|
-
|
|
600
|
-
//# sourceMappingURL=debug-server-CfQNxxGW.js.map
|