@comecaramelos/dsh-chrome-mcp 0.0.1 → 1.0.0

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 (171) hide show
  1. package/README.md +22 -78
  2. package/assets/dsh-chrome-mcp-social.png +0 -0
  3. package/assets/icon.svg +71 -0
  4. package/assets/prelaunch-windows-chrome.html +277 -0
  5. package/lib/client.js +1433 -568
  6. package/lib/client.js.map +7 -0
  7. package/lib/host/apply.d.ts +3 -0
  8. package/lib/host/apply.d.ts.map +1 -0
  9. package/lib/host/apply.js +31 -0
  10. package/lib/host/apply.js.map +1 -0
  11. package/lib/host/bridge.d.ts +65 -0
  12. package/lib/host/bridge.d.ts.map +1 -0
  13. package/lib/host/bridge.js +176 -0
  14. package/lib/host/bridge.js.map +1 -0
  15. package/lib/host/catalog.d.ts +31 -0
  16. package/lib/host/catalog.d.ts.map +1 -0
  17. package/lib/host/catalog.js +92 -0
  18. package/lib/host/catalog.js.map +1 -0
  19. package/lib/host/constants.d.ts +229 -0
  20. package/lib/host/constants.d.ts.map +1 -0
  21. package/lib/host/constants.js +242 -0
  22. package/lib/host/constants.js.map +1 -0
  23. package/lib/host/controller/bridge-run.d.ts +38 -0
  24. package/lib/host/controller/bridge-run.d.ts.map +1 -0
  25. package/lib/host/controller/bridge-run.js +69 -0
  26. package/lib/host/controller/bridge-run.js.map +1 -0
  27. package/lib/host/controller/captures.d.ts +22 -0
  28. package/lib/host/controller/captures.d.ts.map +1 -0
  29. package/lib/host/controller/captures.js +60 -0
  30. package/lib/host/controller/captures.js.map +1 -0
  31. package/lib/host/controller/commits.d.ts +4 -0
  32. package/lib/host/controller/commits.d.ts.map +1 -0
  33. package/lib/host/controller/commits.js +75 -0
  34. package/lib/host/controller/commits.js.map +1 -0
  35. package/lib/host/controller/controller.d.ts +40 -0
  36. package/lib/host/controller/controller.d.ts.map +1 -0
  37. package/lib/host/controller/controller.js +167 -0
  38. package/lib/host/controller/controller.js.map +1 -0
  39. package/lib/host/controller/index.d.ts +26 -0
  40. package/lib/host/controller/index.d.ts.map +1 -0
  41. package/lib/host/controller/index.js +25 -0
  42. package/lib/host/controller/index.js.map +1 -0
  43. package/lib/host/controller/notes.d.ts +26 -0
  44. package/lib/host/controller/notes.d.ts.map +1 -0
  45. package/lib/host/controller/notes.js +49 -0
  46. package/lib/host/controller/notes.js.map +1 -0
  47. package/lib/host/controller/persist.d.ts +9 -0
  48. package/lib/host/controller/persist.d.ts.map +1 -0
  49. package/lib/host/controller/persist.js +51 -0
  50. package/lib/host/controller/persist.js.map +1 -0
  51. package/lib/host/controller/runs.d.ts +56 -0
  52. package/lib/host/controller/runs.d.ts.map +1 -0
  53. package/lib/host/controller/runs.js +211 -0
  54. package/lib/host/controller/runs.js.map +1 -0
  55. package/lib/host/controller/state.d.ts +70 -0
  56. package/lib/host/controller/state.d.ts.map +1 -0
  57. package/lib/host/controller/state.js +2 -0
  58. package/lib/host/controller/state.js.map +1 -0
  59. package/lib/host/controller/windows-run.d.ts +9 -0
  60. package/lib/host/controller/windows-run.d.ts.map +1 -0
  61. package/lib/host/controller/windows-run.js +78 -0
  62. package/lib/host/controller/windows-run.js.map +1 -0
  63. package/lib/host/discovery/candidates.d.ts +37 -0
  64. package/lib/host/discovery/candidates.d.ts.map +1 -0
  65. package/lib/host/discovery/candidates.js +56 -0
  66. package/lib/host/discovery/candidates.js.map +1 -0
  67. package/lib/host/discovery/index.d.ts +18 -0
  68. package/lib/host/discovery/index.d.ts.map +1 -0
  69. package/lib/host/discovery/index.js +18 -0
  70. package/lib/host/discovery/index.js.map +1 -0
  71. package/lib/host/discovery/probe.d.ts +18 -0
  72. package/lib/host/discovery/probe.d.ts.map +1 -0
  73. package/lib/host/discovery/probe.js +97 -0
  74. package/lib/host/discovery/probe.js.map +1 -0
  75. package/lib/host/discovery/scan.d.ts +67 -0
  76. package/lib/host/discovery/scan.d.ts.map +1 -0
  77. package/lib/host/discovery/scan.js +169 -0
  78. package/lib/host/discovery/scan.js.map +1 -0
  79. package/lib/host/flags.d.ts +42 -0
  80. package/lib/host/flags.d.ts.map +1 -0
  81. package/lib/host/flags.js +118 -0
  82. package/lib/host/flags.js.map +1 -0
  83. package/lib/host/platform.d.ts +38 -0
  84. package/lib/host/platform.d.ts.map +1 -0
  85. package/lib/host/platform.js +64 -0
  86. package/lib/host/platform.js.map +1 -0
  87. package/lib/host/plugin-meta.d.ts +12 -0
  88. package/lib/host/plugin-meta.d.ts.map +1 -0
  89. package/lib/host/plugin-meta.js +12 -0
  90. package/lib/host/plugin-meta.js.map +1 -0
  91. package/lib/host/schema.d.ts +307 -0
  92. package/lib/host/schema.d.ts.map +1 -0
  93. package/lib/host/schema.js +171 -0
  94. package/lib/host/schema.js.map +1 -0
  95. package/lib/host/settings.d.ts +9 -0
  96. package/lib/host/settings.d.ts.map +1 -0
  97. package/lib/host/settings.js +70 -0
  98. package/lib/host/settings.js.map +1 -0
  99. package/lib/host/status.d.ts +28 -0
  100. package/lib/host/status.d.ts.map +1 -0
  101. package/lib/host/status.js +60 -0
  102. package/lib/host/status.js.map +1 -0
  103. package/lib/host/tool.d.ts +59 -0
  104. package/lib/host/tool.d.ts.map +1 -0
  105. package/lib/host/tool.js +51 -0
  106. package/lib/host/tool.js.map +1 -0
  107. package/lib/host/types/config.d.ts +47 -0
  108. package/lib/host/types/config.d.ts.map +1 -0
  109. package/lib/host/types/config.js +2 -0
  110. package/lib/host/types/config.js.map +1 -0
  111. package/lib/host/types/executable.d.ts +30 -0
  112. package/lib/host/types/executable.d.ts.map +1 -0
  113. package/lib/host/types/executable.js +9 -0
  114. package/lib/host/types/executable.js.map +1 -0
  115. package/lib/host/types/index.d.ts +23 -0
  116. package/lib/host/types/index.d.ts.map +1 -0
  117. package/lib/host/types/index.js +2 -0
  118. package/lib/host/types/index.js.map +1 -0
  119. package/lib/host/types/plugin.d.ts +38 -0
  120. package/lib/host/types/plugin.d.ts.map +1 -0
  121. package/lib/host/types/plugin.js +10 -0
  122. package/lib/host/types/plugin.js.map +1 -0
  123. package/lib/host/types/settings.d.ts +127 -0
  124. package/lib/host/types/settings.d.ts.map +1 -0
  125. package/lib/host/types/settings.js +2 -0
  126. package/lib/host/types/settings.js.map +1 -0
  127. package/lib/host/types/stderr.d.ts +53 -0
  128. package/lib/host/types/stderr.d.ts.map +1 -0
  129. package/lib/host/types/stderr.js +9 -0
  130. package/lib/host/types/stderr.js.map +1 -0
  131. package/lib/host/types/windows.d.ts +39 -0
  132. package/lib/host/types/windows.d.ts.map +1 -0
  133. package/lib/host/types/windows.js +9 -0
  134. package/lib/host/types/windows.js.map +1 -0
  135. package/lib/host/values.d.ts +89 -0
  136. package/lib/host/values.d.ts.map +1 -0
  137. package/lib/host/values.js +212 -0
  138. package/lib/host/values.js.map +1 -0
  139. package/lib/host/windows/connect-flags.d.ts +16 -0
  140. package/lib/host/windows/connect-flags.d.ts.map +1 -0
  141. package/lib/host/windows/connect-flags.js +67 -0
  142. package/lib/host/windows/connect-flags.js.map +1 -0
  143. package/lib/host/windows/index.d.ts +31 -0
  144. package/lib/host/windows/index.d.ts.map +1 -0
  145. package/lib/host/windows/index.js +30 -0
  146. package/lib/host/windows/index.js.map +1 -0
  147. package/lib/host/windows/landing.d.ts +47 -0
  148. package/lib/host/windows/landing.d.ts.map +1 -0
  149. package/lib/host/windows/landing.js +174 -0
  150. package/lib/host/windows/landing.js.map +1 -0
  151. package/lib/host/windows/link.d.ts +11 -0
  152. package/lib/host/windows/link.d.ts.map +1 -0
  153. package/lib/host/windows/link.js +52 -0
  154. package/lib/host/windows/link.js.map +1 -0
  155. package/lib/host/windows/paths.d.ts +43 -0
  156. package/lib/host/windows/paths.d.ts.map +1 -0
  157. package/lib/host/windows/paths.js +121 -0
  158. package/lib/host/windows/paths.js.map +1 -0
  159. package/lib/host/windows/run.d.ts +74 -0
  160. package/lib/host/windows/run.d.ts.map +1 -0
  161. package/lib/host/windows/run.js +210 -0
  162. package/lib/host/windows/run.js.map +1 -0
  163. package/lib/host/windows/spawn.d.ts +22 -0
  164. package/lib/host/windows/spawn.d.ts.map +1 -0
  165. package/lib/host/windows/spawn.js +73 -0
  166. package/lib/host/windows/spawn.js.map +1 -0
  167. package/lib/index.d.ts +86 -0
  168. package/lib/index.d.ts.map +1 -0
  169. package/lib/index.js +72 -625
  170. package/lib/index.js.map +1 -0
  171. package/package.json +26 -4
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Host-half fixed identifiers, budgets and validation patterns.
3
+ *
4
+ * The strings are a public contract — the settings namespace is both the
5
+ * persistence key and the Web card's slot key — and the budgets are what the
6
+ * README and the tests quote, so they live here once and every module imports
7
+ * rather than repeats them.
8
+ */
9
+ /** Fixed settings namespace; also the client card's slot key. */
10
+ export const SETTINGS_NAMESPACE = "chrome-mcp";
11
+ /** Environment variable that seeds the launch flags for one dsh run. */
12
+ export const FLAGS_ENV_VAR = "DSH_CHROME_MCP_FLAGS";
13
+ /**
14
+ * Flag entries are appended verbatim to the bridge argv and reach Chrome via
15
+ * a direct execve (no shell), so shell metacharacters cannot execute here.
16
+ * What must never reach argv is control characters (newlines, NULs) — they
17
+ * corrupt the argument framing, so entries may not contain any.
18
+ */
19
+ export const CONTROL_PATTERN = /[\x00-\x1f\x7f]/u;
20
+ /** Flags every launch carries unless the user overrides them. */
21
+ export const DEFAULT_EXTRA_FLAGS = ["--no-usage-statistics", "--no-performance-crux"];
22
+ /**
23
+ * The flags the card's **Flags WSL** control appends for a WSL ↔ Windows
24
+ * debugging setup — a fixed recommendation, not a setting and never persisted.
25
+ * The shape is what makes it actually reach the browser:
26
+ *
27
+ * - the debug port travels as `--chromeArg=--remote-debugging-port=9222`:
28
+ * the bridge's strict parser knows only its own option names, so a bare
29
+ * `--remote-debugging-port` is reported as `Unknown arguments` and dropped
30
+ * before any browser is launched; as a `chromeArg` it lands in Chrome's own
31
+ * argv and the debugging port opens (see the "Browser switches" hard
32
+ * constraint).
33
+ *
34
+ * A `--user-data-dir` entry is deliberately **not** recommended alongside it.
35
+ * The idea of carrying the profile as a project-rooted `.chrome` was measured
36
+ * to be impossible for the Windows executable: a Windows `chrome.exe` profile
37
+ * directory has to live on a Windows-local path — neither the WSL checkout
38
+ * nor the `\\wsl.localhost` mount can carry its locks (9P) — so the
39
+ * **Prelaunch Windows Chrome** run launches with its own Windows-native
40
+ * profile under `%LOCALAPPDATA%`, and the one entry this list recommends is
41
+ * the debug port the run answers on.
42
+ */
43
+ export const WSL_EXTRA_FLAGS = ["--chromeArg=--remote-debugging-port=9222"];
44
+ /** Upstream configuration docs surfaced by the client card link. */
45
+ export const CONFIGURATION_DOCS_URL = "https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md";
46
+ /** Timeout for one check subprocess (chrome executable probe). */
47
+ export const CHECK_TIMEOUT_MS = 15000;
48
+ /**
49
+ * Why a Windows `chrome.exe` on a WSL host is **never probed**. The exec answers
50
+ * successfully — which is what makes the selection look healthy — but answering
51
+ * means *attaching to the Windows browser session*, and that brings a browser
52
+ * instance up (measured on WSL2 + Google Chrome 153: `chrome.exe --version`
53
+ * spawns the browser process plus its crashpad handler). No tool call could
54
+ * ever drive what it started (the debug pipes do not cross the interop
55
+ * boundary), so the probe buys nothing and costs a running browser: the check
56
+ * answers nothing, and the scan keeps the entry visible as `unavailable` with
57
+ * this reason.
58
+ */
59
+ export const WSL_WINDOWS_LAUNCH_ERROR = "Windows chrome.exe cannot be launched from WSL — use connect mode (--browserUrl / --wsEndpoint) against a Chrome started with --remote-debugging-port";
60
+ /** Max length of a reported error line. */
61
+ export const ERROR_CAP = 240;
62
+ /**
63
+ * Log lines the bridge emits after a lost connection recovered — clear
64
+ * the captured connection error when one shows up.
65
+ */
66
+ export const BRIDGE_RECONNECTED = /reconnected and re-synced tools/u;
67
+ /** One bridge log level: the levels the captured wrapper shadows. */
68
+ export const LOG_LEVELS = ["trace", "debug", "info", "warn", "error", "fatal"];
69
+ /**
70
+ * Bridge console-output routing modes (mirrors the reference plugin's gateway
71
+ * stderr modes). "log" redirects the spawned child's stderr into a log file;
72
+ * "console" inherits it onto the dsh console.
73
+ */
74
+ export const BRIDGE_STDERR_MODES = ["log", "console"];
75
+ /** Modes accepted by the `stderrMode` user layer: also the empty string
76
+ * ("" → follow the row config `bridgeStderr`). */
77
+ export const STDERR_MODE_FIELD_PATTERN = /^(?:log|console)?$/u;
78
+ /** Row-config `bridgeStderrLog` accepts "" (the per-spawn default) or a
79
+ * POSIX absolute path; nothing else. */
80
+ export const OPTIONAL_PATH_PATTERN = /^(?:\/[^\x00-\x1f\x7f]*|)$/u;
81
+ /**
82
+ * Candidate POSIX shells used only to redirect the bridge's stderr (see
83
+ * ./bridge.ts `buildBridgeSpawn`). None found on a bare Windows host → the
84
+ * redirect is impossible and the spawn stays unwrapped with `fallback:
85
+ * "platform"`/`"shell"`.
86
+ */
87
+ export const SH_PATH_CANDIDATES = ["/bin/sh", "/usr/bin/sh"];
88
+ /**
89
+ * `chromePath` user layer: a filesystem path or a PATH-resolved name. Nothing
90
+ * travels through a shell here either, so the only hard constraint is that no
91
+ * control character reaches it; the length cap keeps one absurd value out of
92
+ * argv and out of the settings document.
93
+ */
94
+ export const CHROME_PATH_PATTERN = /^[^\x00-\x1f\x7f]{0,4096}$/u;
95
+ /** Upper bound of the **legacy** UI-authored custom-executable MRU (oldest
96
+ * drops off). The MRU is the older shape of the saved-executables list: a host
97
+ * whose settings still carry it folds its paths into the `executables` rows the
98
+ * first time the picker scans (see ./catalog.ts `mergeEntries`), after which the
99
+ * rows are the only source of saved executables. */
100
+ export const CUSTOM_PATHS_CAP = 10;
101
+ /** Longest display name a saved-executable row may carry — the `name` half is
102
+ * UI-only (what the pill and the dropdown render), the `id` half is what goes on
103
+ * the wire. */
104
+ export const CATALOG_NAME_MAX_LENGTH = 200;
105
+ /** Upper bound of the saved-executable rows, merged-list side: the tail drops
106
+ * off, so a scan that keeps discovering new ids cannot grow the persisted
107
+ * catalog without limit. */
108
+ export const CATALOG_MAX_LENGTH = 50;
109
+ /**
110
+ * The Windows-side Chrome/Edge executables the card's **Prelaunch Windows Chrome**
111
+ * button locates through the WSL interop mount. They are NOT local picker
112
+ * candidates — the picker must never offer what cannot be launched from
113
+ * Linux (hard constraint) — the button exists precisely because Windows
114
+ * Chrome is only usable in *connect* mode. The list is host-served logic: the
115
+ * card carries no copy of it.
116
+ */
117
+ export const WINDOWS_CHROME_CANDIDATES = [
118
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
119
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
120
+ "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
121
+ "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"
122
+ ];
123
+ /** The debugging port every Windows Chrome launch carries first — kept
124
+ * coherent with the port the **Flags WSL** control recommends. */
125
+ export const WINDOWS_CHROME_PORT = 9222;
126
+ /** The debugging-port pool the run falls through when the first port is already
127
+ * carrying a listener: [first, first+1, …]. A listener answers "reachable",
128
+ * which is what makes the port's own reachability sample its in-use signal. */
129
+ export const WINDOWS_CHROME_PORT_POOL = [9222, 9223, 9224];
130
+ /**
131
+ * Ports that **only Windows ever listens on** (RPC endpoint mapper, RDP). The
132
+ * prereq check connects one of them: on a `mirrored` networkingMode host the
133
+ * connect succeeds, on a NAT host it gives up — the sample must discriminate,
134
+ * which is exactly what makes it the prereq (measured: 135/3389 CONNECTED
135
+ * under mirrored, closed ports TIME OUT, never refuse). A port that WSL might
136
+ * hold locally (445 via Samba) must never be used: it would answer reachable
137
+ * even when the Windows loopback is unreachable.
138
+ */
139
+ export const WINDOWS_CHROME_PREREQ_PORTS = [135, 3389];
140
+ /** How long to wait for the launched Windows Chrome to open its port. */
141
+ export const WINDOWS_CHROME_WAIT_MS = 10000;
142
+ /** How long one TCP reachability sample waits before calling the port
143
+ * unreachable (a NAT host's closed port times out; it never refuses). */
144
+ export const WINDOWS_CHROME_SAMPLE_MS = 1000;
145
+ /** Poll cadence while waiting for a launched Windows Chrome to bind its port. */
146
+ export const WINDOWS_CHROME_POLL_MS = 500;
147
+ /**
148
+ * The Windows-side working directory the launch runs with. Nothing depends on
149
+ * it (every argument is absolute), it only keeps relative path resolution off
150
+ * any UNC (WSL-side) path — the shape the old `cmd.exe /c start` form needed
151
+ * too (measured 2026-10-04, see .probe/windows-chrome-connect-probe.log).
152
+ */
153
+ export const WINDOWS_CHROME_START_CWD = "/mnt/c/Windows";
154
+ /** The UNC root modern WSL exports to Windows-side processes. */
155
+ export const WSL_UNC_ROOT = "\\\\wsl.localhost\\";
156
+ /**
157
+ * The Windows-side directory name the run profiles under
158
+ * (`%LOCALAPPDATA%\dsh-chrome-mcp\<.chrome|\.chrome-<port>>`). The profile
159
+ * must live on the Windows filesystem: the WSL-side directories, served to
160
+ * Windows through 9P, cannot carry the locks Chrome's SQLite tables and its
161
+ * singleton socket take — a profile rooted there opens every table as a
162
+ * zero-byte file and shows "an error occurred while opening your profile" on
163
+ * every launch (measured live 2026-10-04 against Chrome 154). The workspace
164
+ * keeps a `.chrome` view of this directory through the link the run refreshes
165
+ * (see `linkWindowsChromeProfile`).
166
+ */
167
+ export const WINDOWS_CHROME_PROFILE_DIRNAME = "dsh-chrome-mcp";
168
+ /** The connect-mode flag the button writes; its value is the debugging URL. */
169
+ export const BROWSER_URL_FLAG_PREFIX = "--browserUrl=";
170
+ /**
171
+ * The flags a brand-new Windows profile is launched with, beside the port and
172
+ * the profile directory. A fresh `--user-data-dir` IS a first run for the
173
+ * browser: Windows Chrome opens the welcome tab and the default-search-engine
174
+ * chooser on it, and in connect mode nothing clicks through it — the debugging
175
+ * session is left sitting on the FRE page. Both flags suppress that
176
+ * experience, exactly like the manual recipe the button replaces. They travel
177
+ * on the **browser's own** argv (the `start` line), never as `--chromeArg=`:
178
+ * here it is Chrome being told at launch what not to show, not the bridge
179
+ * parser (see the browser-switches bullet under Hard constraints).
180
+ */
181
+ export const WINDOWS_CHROME_FIRST_RUN_FLAGS = ["--no-first-run", "--no-default-browser-check"];
182
+ /**
183
+ * The page the Windows launch opens, package-root-relative. It is the one asset
184
+ * this plugin ships outside `lib`, and it is **read host-side**: the run renders
185
+ * it with its own answers, writes it through the interop mount next to the
186
+ * profile directory (so the Windows-side browser can actually read it), and hands
187
+ * the browser the `file:///…` URL. It exists because the honest answer to "why
188
+ * did a window just open?" is prose the argv cannot carry — and the usual
189
+ * placeholder is not an option: `example.com` carries an explicit notice against
190
+ * being used for automation and tests.
191
+ */
192
+ export const WINDOWS_CHROME_LANDING_ASSET = "assets/prelaunch-windows-chrome.html";
193
+ /** The base name of the landing file written beside each profile directory
194
+ * (`…\dsh-chrome-mcp\prelaunch-<port>.html`) — a sibling of the profile, never a
195
+ * file inside it, so it never reads as profile churn. */
196
+ export const WINDOWS_CHROME_LANDING_BASENAME = "prelaunch";
197
+ /** What one Windows Chrome run may currently be doing (the card derives its
198
+ * button state, pill muting and disabled reason from this single field).
199
+ * `"not-applicable"` is the non-WSL answer the plan asks the button to read as
200
+ * disabled-off; `"launch-failed"` and the rest are the §4 failure states. */
201
+ export const WINDOWS_CHROME_STATES = [
202
+ "off",
203
+ "launching",
204
+ "connected",
205
+ "unreachable",
206
+ "not-found",
207
+ "launch-failed",
208
+ "not-applicable"
209
+ ];
210
+ /** Schema pattern for `windowsChromeStatus.state`. */
211
+ export const WINDOWS_CHROME_STATE_PATTERN = /^(?:off|launching|connected|unreachable|not-found|launch-failed|not-applicable)$/u;
212
+ /**
213
+ * The prereq answer — the one manual step that is not the button's job
214
+ * (configuring `~/.wslconfig` is out of scope, reading/reaching it is not).
215
+ */
216
+ export const WINDOWS_CHROME_PREREQ_ERROR = "Windows Chrome needs networkingMode=mirrored in %USERPROFILE%\\.wslconfig, then wsl --shutdown: today the Windows loopback is not reachable from this WSL host";
217
+ /** Where the effective selection came from (`effectiveSource`): a `chromePath`
218
+ * written by the card (or seeded once by the host) or the row config. Nothing
219
+ * resolves on its own, so there is no "auto". */
220
+ export const EFFECTIVE_SOURCES = ["selected", "row-config"];
221
+ /**
222
+ * Environment variables WSL exports into every distro session. Their presence
223
+ * answers "are we running inside WSL?" — the first one carries the distro name,
224
+ * `WSL_INTEROP` the interop-path directory (`/run/WSL/<pid>`).
225
+ */
226
+ export const WSL_ENV_VARS = ["WSL_DISTRO_NAME", "WSL_DISTRO_LABEL", "WSL_INTEROP"];
227
+ /**
228
+ * The `binfmt_misc` registration WSL installs for PE binaries: its presence
229
+ * marks a WSL kernel. It is only the **fallback** signal — inside a host the
230
+ * launch environment is authoritative, and an empty one is an answer (see
231
+ * ./platform.ts).
232
+ */
233
+ export const WSL_INTEROP_MARKER = "/proc/sys/fs/binfmt_misc/WSLInterop";
234
+ /** Windows executables reachable through the interop mount (`/mnt/c/...`) or by
235
+ * their Windows spelling (UNC, drive letter). */
236
+ export const WINDOWS_PATH_PATTERN = /^(?:\/mnt\/[a-z]\/|\\\\[\w.-]+\\|[a-z]:[\\/])/iu;
237
+ /** The PE suffix. WSL execve's a `.exe` from any path (the Windows loader reads
238
+ * the Linux filesystem over UNC), so the suffix alone marks a Windows binary. */
239
+ export const WINDOWS_EXE_PATTERN = /\.exe$/iu;
240
+ /** Schema pattern for `effectiveSource` ("" = nothing resolved yet). */
241
+ export const EFFECTIVE_SOURCE_PATTERN = /^(?:selected|row-config)?$/u;
242
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/host/constants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Controller — bridge switches.
3
+ *
4
+ * How a flag, executable or stderr-mode change reaches the running bridge: the
5
+ * logical argv is rebuilt through `bridgeConfig` for the current state and
6
+ * applied as a `fiber.update()`, chained on the fiber's own readiness so a
7
+ * switch that lands while the first connection is still loading actually takes
8
+ * effect (cordis restarts a fiber only from `ACTIVE`).
9
+ */
10
+ import { bridgeConfig } from "../bridge.js";
11
+ import type { BridgeStderrMode, ChromeMcpConfig } from "../types/index.js";
12
+ import type { Controller } from "./state.js";
13
+ /** The row config as the bridge/config builders see it: effective executable,
14
+ * cleaned row flags. The logical argv shape is unchanged. */
15
+ export declare function configForBridge(controller: Controller, flags: string[]): ChromeMcpConfig;
16
+ /**
17
+ * Restart the bridge connection with one built config.
18
+ *
19
+ * A cordis fiber restarts only from `ACTIVE`: an `update` issued while the
20
+ * bridge's first connection is still loading is stored but never applied (the
21
+ * load in flight resolves with the config it started with), and the update
22
+ * settles `undefined` as if it had worked — so the bridge would silently keep
23
+ * running with the argv it first spawned with. Chaining the update onto the
24
+ * fiber's own readiness (the returned thenable settles with the load) is what
25
+ * makes the switches that land while the bridge is still starting — the first
26
+ * ones the saved connect-mode flags produce — actually take effect.
27
+ */
28
+ export declare function restartBridge(controller: Controller, built: ReturnType<typeof bridgeConfig>, note: string): void;
29
+ /** Re-apply the nested bridge with one flags list; failures logged, never thrown. */
30
+ export declare function applyFlags(controller: Controller, flags: string[]): void;
31
+ /**
32
+ * Switch the effective bridge stderr mode in place — the card's "Reduce log
33
+ * output" toggle. Only the spawn wrapper changes, but that means restarting the
34
+ * bridge connection (the stderr routing is fixed at spawn); the flags and the
35
+ * executable stay exactly as they were.
36
+ */
37
+ export declare function applyStderrMode(controller: Controller, mode: BridgeStderrMode): void;
38
+ //# sourceMappingURL=bridge-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-run.d.ts","sourceRoot":"","sources":["../../../src/host/controller/bridge-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;6DAC6D;AAC7D,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,CAExF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAUhH;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAWxE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAOpF"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Controller — bridge switches.
3
+ *
4
+ * How a flag, executable or stderr-mode change reaches the running bridge: the
5
+ * logical argv is rebuilt through `bridgeConfig` for the current state and
6
+ * applied as a `fiber.update()`, chained on the fiber's own readiness so a
7
+ * switch that lands while the first connection is still loading actually takes
8
+ * effect (cordis restarts a fiber only from `ACTIVE`).
9
+ */
10
+ import { bridgeConfig } from "../bridge.js";
11
+ import { flagsSkipCheck } from "../flags.js";
12
+ import { noteStderr } from "./notes.js";
13
+ /** The row config as the bridge/config builders see it: effective executable,
14
+ * cleaned row flags. The logical argv shape is unchanged. */
15
+ export function configForBridge(controller, flags) {
16
+ return { ...controller.config, extraFlags: flags, chromePath: controller.state.runningChromePath };
17
+ }
18
+ /**
19
+ * Restart the bridge connection with one built config.
20
+ *
21
+ * A cordis fiber restarts only from `ACTIVE`: an `update` issued while the
22
+ * bridge's first connection is still loading is stored but never applied (the
23
+ * load in flight resolves with the config it started with), and the update
24
+ * settles `undefined` as if it had worked — so the bridge would silently keep
25
+ * running with the argv it first spawned with. Chaining the update onto the
26
+ * fiber's own readiness (the returned thenable settles with the load) is what
27
+ * makes the switches that land while the bridge is still starting — the first
28
+ * ones the saved connect-mode flags produce — actually take effect.
29
+ */
30
+ export function restartBridge(controller, built, note) {
31
+ void Promise.resolve(controller.bridge)
32
+ .then(() => controller.bridge.update(built.config))
33
+ .then(() => {
34
+ controller.state.entry.lastError = "";
35
+ // Intentionally NOT clearing `chromeMissing`: a successful bridge
36
+ // update proves the flags took (clears the captured connection error)
37
+ // but says nothing about the executable.
38
+ })
39
+ .catch((error) => controller.ctx.logger.error(`chrome-devtools-mcp(${controller.config.serverName}): ${note}: ${String(error)}`));
40
+ }
41
+ /** Re-apply the nested bridge with one flags list; failures logged, never thrown. */
42
+ export function applyFlags(controller, flags) {
43
+ const { state } = controller;
44
+ state.runningFlags = flags;
45
+ state.entry.carriesConnectionMode = flagsSkipCheck(flags);
46
+ // Connect mode launches no local executable, so the known "executable
47
+ // missing" state simply does not apply while it is set.
48
+ if (state.entry.carriesConnectionMode)
49
+ state.entry.chromeMissing = false;
50
+ controller.ctx.logger.info(`chrome-devtools-mcp(${controller.config.serverName}): relaunching bridge with flags [${flags.join(" ")}]`);
51
+ const built = bridgeConfig(configForBridge(controller, flags), flags, state.runningStderrMode);
52
+ noteStderr(controller, built);
53
+ restartBridge(controller, built, "flags update failed");
54
+ }
55
+ /**
56
+ * Switch the effective bridge stderr mode in place — the card's "Reduce log
57
+ * output" toggle. Only the spawn wrapper changes, but that means restarting the
58
+ * bridge connection (the stderr routing is fixed at spawn); the flags and the
59
+ * executable stay exactly as they were.
60
+ */
61
+ export function applyStderrMode(controller, mode) {
62
+ const { state } = controller;
63
+ state.runningStderrMode = mode;
64
+ controller.ctx.logger.info(`chrome-devtools-mcp(${controller.config.serverName}): bridge stderr mode → "${mode}" (restarting the bridge connection)`);
65
+ const built = bridgeConfig(configForBridge(controller, state.runningFlags), state.runningFlags, mode);
66
+ noteStderr(controller, built);
67
+ restartBridge(controller, built, `bridge stderr mode switch to "${mode}" failed`);
68
+ }
69
+ //# sourceMappingURL=bridge-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-run.js","sourceRoot":"","sources":["../../../src/host/controller/bridge-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxC;6DAC6D;AAC7D,MAAM,UAAU,eAAe,CAAC,UAAsB,EAAE,KAAe;IACnE,OAAO,EAAE,GAAG,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC;AACvG,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,UAAsB,EAAE,KAAsC,EAAE,IAAY;IACtG,KAAK,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;SAClC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;SAClD,IAAI,CAAC,GAAG,EAAE;QACP,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;QACtC,kEAAkE;QAClE,sEAAsE;QACtE,yCAAyC;IAC7C,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,UAAU,CAAC,MAAM,CAAC,UAAU,MAAM,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1I,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,UAAU,CAAC,UAAsB,EAAE,KAAe;IAC9D,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC;IAC7B,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;IAC3B,KAAK,CAAC,KAAK,CAAC,qBAAqB,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1D,sEAAsE;IACtE,wDAAwD;IACxD,IAAI,KAAK,CAAC,KAAK,CAAC,qBAAqB;QAAE,KAAK,CAAC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;IACzE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,UAAU,CAAC,MAAM,CAAC,UAAU,qCAAqC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvI,MAAM,KAAK,GAAG,YAAY,CAAC,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC/F,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC9B,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,qBAAqB,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,UAAsB,EAAE,IAAsB;IAC1E,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC;IAC7B,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,UAAU,CAAC,MAAM,CAAC,UAAU,4BAA4B,IAAI,sCAAsC,CAAC,CAAC;IACtJ,MAAM,KAAK,GAAG,YAAY,CAAC,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACtG,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC9B,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,iCAAiC,IAAI,UAAU,CAAC,CAAC;AACtF,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Controller } from "./state.js";
2
+ /**
3
+ * Fold bridge log lines into the base layer. dsh-mcp-client reports connection
4
+ * problems only through its logger (reconnect warnings with "connection
5
+ * failed/lost", give-up errors, spawn failures), and a module plugin has no
6
+ * push channel to clients — so the newest failure line becomes `lastError`, and
7
+ * the "reconnected and re-synced tools" success line clears it. The wrapper
8
+ * lives only on the bridge scope; our own logs bypass it, so our own warns never
9
+ * masquerade as connection errors.
10
+ */
11
+ export declare function noteBridgeLog(controller: Controller, level: string, args: unknown[]): void;
12
+ /**
13
+ * Register the tool-call status capture: browser-level failures surface only as
14
+ * the calling agent's tool result, never on the bridge logger, so a settled
15
+ * result whose registered name is prefixed `mcp__<serverName>__` and carries
16
+ * `isError` writes the failure into the base layer; a successful chrome-tool call
17
+ * clears both, so the card reports current health. The body is wrapped in
18
+ * try/catch (warn only — a capture fault must never break a tool call) and
19
+ * returns `next()` unchanged.
20
+ */
21
+ export declare function registerToolCapture(controller: Controller): void;
22
+ //# sourceMappingURL=captures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captures.d.ts","sourceRoot":"","sources":["../../../src/host/controller/captures.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAW1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAgBhE"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Controller — the two capture paths that reach the base layer.
3
+ *
4
+ * A module plugin has no push channel to clients, so both capture paths write
5
+ * straight into the base layer: the newest bridge-log failure line becomes
6
+ * `lastError`, and a settled browser-tool result that failed does the same (the
7
+ * two never duplicate — bridge logs never reach the tool surface and vice
8
+ * versa). Each capture is classified into the known "executable missing" flag so
9
+ * the single red status dot keeps its whole story.
10
+ */
11
+ import { BRIDGE_RECONNECTED } from "../constants.js";
12
+ import { firstLine, isChromeMissing, toolResultNote } from "../status.js";
13
+ /**
14
+ * Fold bridge log lines into the base layer. dsh-mcp-client reports connection
15
+ * problems only through its logger (reconnect warnings with "connection
16
+ * failed/lost", give-up errors, spawn failures), and a module plugin has no
17
+ * push channel to clients — so the newest failure line becomes `lastError`, and
18
+ * the "reconnected and re-synced tools" success line clears it. The wrapper
19
+ * lives only on the bridge scope; our own logs bypass it, so our own warns never
20
+ * masquerade as connection errors.
21
+ */
22
+ export function noteBridgeLog(controller, level, args) {
23
+ const message = args
24
+ .map((arg) => (arg instanceof Error ? (arg.stack ?? arg.message ?? String(arg)) : String(arg)))
25
+ .join(" ");
26
+ if (level === "error" || level === "warn") {
27
+ controller.state.entry.lastError = firstLine(message);
28
+ controller.state.entry.chromeMissing = isChromeMissing(message);
29
+ }
30
+ else if (level === "info" && BRIDGE_RECONNECTED.test(message)) {
31
+ controller.state.entry.lastError = "";
32
+ controller.state.entry.chromeMissing = false;
33
+ }
34
+ }
35
+ /**
36
+ * Register the tool-call status capture: browser-level failures surface only as
37
+ * the calling agent's tool result, never on the bridge logger, so a settled
38
+ * result whose registered name is prefixed `mcp__<serverName>__` and carries
39
+ * `isError` writes the failure into the base layer; a successful chrome-tool call
40
+ * clears both, so the card reports current health. The body is wrapped in
41
+ * try/catch (warn only — a capture fault must never break a tool call) and
42
+ * returns `next()` unchanged.
43
+ */
44
+ export function registerToolCapture(controller) {
45
+ const toolPrefix = `mcp__${controller.config.serverName}__`;
46
+ controller.ctx.on("tools/post-execute", async (exec, result, next) => {
47
+ try {
48
+ if (typeof exec.name === "string" && exec.name.startsWith(toolPrefix)) {
49
+ const note = toolResultNote(exec.name, result);
50
+ controller.state.entry.lastError = note ?? "";
51
+ controller.state.entry.chromeMissing = note === null ? false : isChromeMissing(note);
52
+ }
53
+ }
54
+ catch (error) {
55
+ controller.ctx.logger.warn(`chrome-devtools-mcp(${controller.config.serverName}): tool status capture failed: ${String(error)}`);
56
+ }
57
+ return next();
58
+ });
59
+ }
60
+ //# sourceMappingURL=captures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captures.js","sourceRoot":"","sources":["../../../src/host/controller/captures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAK1E;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,UAAsB,EAAE,KAAa,EAAE,IAAe;IAChF,MAAM,OAAO,GAAG,IAAI;SACf,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;SAC9F,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACxC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QACtD,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACpE,CAAC;SAAM,IAAI,KAAK,KAAK,MAAM,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9D,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;QACtC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;IACjD,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAsB;IACtD,MAAM,UAAU,GAAG,QAAQ,UAAU,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC;IAC3D,UAAU,CAAC,GAAG,CAAC,EAA0H,CACtI,oBAAoB,EACpB,KAAK,EAAE,IAAuB,EAAE,MAAsB,EAAE,IAAmB,EAAE,EAAE;QAC3E,IAAI,CAAC;YACD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpE,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC/C,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;gBAC9C,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACzF,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,UAAU,CAAC,MAAM,CAAC,UAAU,kCAAkC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrI,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Controller } from "./state.js";
2
+ /** Route one settings commit. */
3
+ export declare function onSettingsChange(controller: Controller): void;
4
+ //# sourceMappingURL=commits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commits.d.ts","sourceRoot":"","sources":["../../../src/host/controller/commits.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,iCAAiC;AACjC,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAoD7D"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Controller — settings-commit routing.
3
+ *
4
+ * One settings commit may carry any of five changes; this routes them in the
5
+ * reference's order: a flags drift restarts the bridge, an executable change
6
+ * picked by the card restarts it and re-runs the discovery, a rows change is
7
+ * folded into the same run (never a second one), the stderr toggle restarts the
8
+ * connection with/without the stderr wrapper, and the two trigger nonces (the
9
+ * fetch button, the Windows button) fire their runs. The first commit is the
10
+ * registration run — a persisted Windows trigger folds in silently so booting a
11
+ * host never launches a Windows browser by itself.
12
+ */
13
+ import { normalizeEntries } from "../catalog.js";
14
+ import { sameFlags } from "../flags.js";
15
+ import { resolveEffectiveChromePath, resolveStderrMode } from "../values.js";
16
+ import { applyFlags, applyStderrMode } from "./bridge-run.js";
17
+ import { applyChromePath, queue, sameEntries } from "./runs.js";
18
+ /** Route one settings commit. */
19
+ export function onSettingsChange(controller) {
20
+ const { state } = controller;
21
+ try {
22
+ const current = state.source();
23
+ if (!sameFlags(current.extraFlags, state.runningFlags))
24
+ applyFlags(controller, current.extraFlags);
25
+ // Executable changes come from the persisted `chromePath` the card wrote
26
+ // (a row's `id`). They restart the bridge connection and re-run the
27
+ // discovery, because a different executable reads a different machine. A
28
+ // path the host seeded itself keeps standing until the user picks one:
29
+ // the seed is written into the persisted layer, and until that write is
30
+ // visible a commit must not resolve back to "nothing selected".
31
+ const userLayer = typeof current.chromePath === "string" ? current.chromePath.trim() : "";
32
+ if (userLayer !== "")
33
+ state.seenUserSelection = true;
34
+ const seededCarry = state.seededPath !== "" && !state.seenUserSelection ? state.seededPath : "";
35
+ const nextChromePath = resolveEffectiveChromePath(userLayer !== "" ? userLayer : seededCarry, controller.rowChromePath);
36
+ const nextSource = userLayer !== "" ? "selected" : seededCarry !== "" ? "selected" : controller.rowChromePath !== "" ? "row-config" : "";
37
+ if (nextChromePath !== state.runningChromePath)
38
+ applyChromePath(controller, nextChromePath, nextSource);
39
+ // Rows changes touch the list, never the connection.
40
+ const nextExecutables = normalizeEntries(current.executables);
41
+ const rowsChanged = !sameEntries(nextExecutables, state.runningExecutables);
42
+ // The "Reduce log output" toggle lives here too: a change of the
43
+ // effective stderr mode restarts the bridge connection with / without
44
+ // the stderr-capturing wrapper.
45
+ const nextStderrMode = resolveStderrMode(current.stderrMode, controller.config.bridgeStderr);
46
+ if (nextStderrMode !== state.runningStderrMode)
47
+ applyStderrMode(controller, nextStderrMode);
48
+ const firstCommit = state.lastRefreshNonce === void 0;
49
+ const refreshChanged = !firstCommit && current.refreshExecutablesNonce !== state.lastRefreshNonce;
50
+ const windowsChanged = !firstCommit && current.openWindowsChromeNonce !== state.lastWindowsNonce;
51
+ state.lastRefreshNonce = current.refreshExecutablesNonce;
52
+ state.lastWindowsNonce = current.openWindowsChromeNonce;
53
+ if (firstCommit) {
54
+ // The registration run: discovery + probe, no separate call. A
55
+ // persisted Windows trigger folds in silently — booting a host must
56
+ // never launch a Windows browser by itself.
57
+ queue(controller, "probe");
58
+ }
59
+ else {
60
+ // The card's **Search executables** button. A rows change is folded
61
+ // into the same run rather than a second one, so the client waits on
62
+ // exactly one revision bump.
63
+ if (refreshChanged || rowsChanged)
64
+ queue(controller, "probe");
65
+ // The card's **Prelaunch Windows Chrome** button: one Windows run,
66
+ // sharing this chain (one run at a time).
67
+ if (windowsChanged)
68
+ queue(controller, "windows");
69
+ }
70
+ }
71
+ catch (error) {
72
+ controller.ctx.logger.error(`chrome-devtools-mcp(${controller.config.serverName}): ${String(error)}`);
73
+ }
74
+ }
75
+ //# sourceMappingURL=commits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commits.js","sourceRoot":"","sources":["../../../src/host/controller/commits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type WindowsChromeRunOptions } from "../windows/index.js";
2
+ import type { ChromeMcpConfig, PluginContext, SettingsResolved, WindowsChromeRun } from "../types/index.js";
3
+ /** The live state one apply() keeps in memory. */
4
+ export interface ChromeMcpController {
5
+ /** The composition base layer served alongside the user layer. */
6
+ readonly entry: SettingsResolved;
7
+ /** Swap the live resolved settings source (installSection hook). */
8
+ setSource(next: () => SettingsResolved): void;
9
+ /** Route one settings commit: relaunch on flag or executable drift, and run
10
+ * one executable refresh on the search trigger. */
11
+ onSettingsChange(): void;
12
+ /** Run one executable refresh (probe the candidates, merge what is found
13
+ * into the saved rows, seed a never-configured host). Runs are serialized so
14
+ * rapid actions never interleave. */
15
+ run(): Promise<unknown>;
16
+ /** Run the Windows Chrome path once (locate, prereq, port, launch, connect)
17
+ * on the same one-run-at-a-time chain the button drives through, and answer
18
+ * with what the run stated. This is the MCP-tool surface of the button: the
19
+ * trigger is the call itself — nothing is persisted, so a booting host never
20
+ * launches a browser on its own. Resolves after the run settles. */
21
+ prelaunchWindowsChrome(): Promise<string>;
22
+ }
23
+ /**
24
+ * Optional overrides for tests and diagnostics: the Windows Chrome run is the
25
+ * only half that reaches outside the process (it opens TCP sockets and spawns
26
+ * through the interop mount), so a wiring test replaces just this one — the same
27
+ * injectability rule every other platform half follows.
28
+ */
29
+ export type ChromeMcpControllerDeps = {
30
+ runWindowsChrome?: (options: WindowsChromeRunOptions) => Promise<WindowsChromeRun>;
31
+ };
32
+ /**
33
+ * Build the live state for one bridge + card pairing.
34
+ * @param ctx - plugin context.
35
+ * @param config - validated row config.
36
+ * @param deps - optional host overrides (tests replace the Windows run;
37
+ * everything else resolves from this process).
38
+ */
39
+ export declare function createChromeMcpController(ctx: PluginContext, config: ChromeMcpConfig, deps?: ChromeMcpControllerDeps): ChromeMcpController;
40
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/host/controller/controller.ts"],"names":[],"mappings":"AA6BA,OAAO,EAA2B,KAAK,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE5F,OAAO,KAAK,EAER,eAAe,EAEf,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,mBAAmB,CAAC;AAS3B,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAChC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,oEAAoE;IACpE,SAAS,CAAC,IAAI,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;IAC9C;uDACmD;IACnD,gBAAgB,IAAI,IAAI,CAAC;IACzB;;yCAEqC;IACrC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACxB;;;;wEAIoE;IACpE,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG;IAClC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACtF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,GAAE,uBAA4B,GAAG,mBAAmB,CAwI9I"}