@comecaramelos/dsh-chrome-mcp 0.0.1 → 2.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 (214) hide show
  1. package/README.md +44 -95
  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 +3005 -568
  6. package/lib/client.js.map +7 -0
  7. package/lib/host/apply.d.ts +40 -0
  8. package/lib/host/apply.d.ts.map +1 -0
  9. package/lib/host/apply.js +57 -0
  10. package/lib/host/apply.js.map +1 -0
  11. package/lib/host/bridge.d.ts +74 -0
  12. package/lib/host/bridge.d.ts.map +1 -0
  13. package/lib/host/bridge.js +168 -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/commands/chrome-path.d.ts +4 -0
  20. package/lib/host/commands/chrome-path.d.ts.map +1 -0
  21. package/lib/host/commands/chrome-path.js +65 -0
  22. package/lib/host/commands/chrome-path.js.map +1 -0
  23. package/lib/host/commands/chrome-refresh.d.ts +17 -0
  24. package/lib/host/commands/chrome-refresh.d.ts.map +1 -0
  25. package/lib/host/commands/chrome-refresh.js +37 -0
  26. package/lib/host/commands/chrome-refresh.js.map +1 -0
  27. package/lib/host/commands/index.d.ts +26 -0
  28. package/lib/host/commands/index.d.ts.map +1 -0
  29. package/lib/host/commands/index.js +41 -0
  30. package/lib/host/commands/index.js.map +1 -0
  31. package/lib/host/constants.d.ts +306 -0
  32. package/lib/host/constants.d.ts.map +1 -0
  33. package/lib/host/constants.js +319 -0
  34. package/lib/host/constants.js.map +1 -0
  35. package/lib/host/controller/_trace.d.ts +3 -0
  36. package/lib/host/controller/_trace.d.ts.map +1 -0
  37. package/lib/host/controller/_trace.js +14 -0
  38. package/lib/host/controller/_trace.js.map +1 -0
  39. package/lib/host/controller/bridge-run.d.ts +40 -0
  40. package/lib/host/controller/bridge-run.d.ts.map +1 -0
  41. package/lib/host/controller/bridge-run.js +85 -0
  42. package/lib/host/controller/bridge-run.js.map +1 -0
  43. package/lib/host/controller/captures.d.ts +15 -0
  44. package/lib/host/controller/captures.d.ts.map +1 -0
  45. package/lib/host/controller/captures.js +85 -0
  46. package/lib/host/controller/captures.js.map +1 -0
  47. package/lib/host/controller/commits.d.ts +4 -0
  48. package/lib/host/controller/commits.d.ts.map +1 -0
  49. package/lib/host/controller/commits.js +131 -0
  50. package/lib/host/controller/commits.js.map +1 -0
  51. package/lib/host/controller/connections.d.ts +38 -0
  52. package/lib/host/controller/connections.d.ts.map +1 -0
  53. package/lib/host/controller/connections.js +71 -0
  54. package/lib/host/controller/connections.js.map +1 -0
  55. package/lib/host/controller/controller.d.ts +35 -0
  56. package/lib/host/controller/controller.d.ts.map +1 -0
  57. package/lib/host/controller/controller.js +234 -0
  58. package/lib/host/controller/controller.js.map +1 -0
  59. package/lib/host/controller/index.d.ts +30 -0
  60. package/lib/host/controller/index.d.ts.map +1 -0
  61. package/lib/host/controller/index.js +27 -0
  62. package/lib/host/controller/index.js.map +1 -0
  63. package/lib/host/controller/notes.d.ts +26 -0
  64. package/lib/host/controller/notes.d.ts.map +1 -0
  65. package/lib/host/controller/notes.js +44 -0
  66. package/lib/host/controller/notes.js.map +1 -0
  67. package/lib/host/controller/persist.d.ts +16 -0
  68. package/lib/host/controller/persist.d.ts.map +1 -0
  69. package/lib/host/controller/persist.js +98 -0
  70. package/lib/host/controller/persist.js.map +1 -0
  71. package/lib/host/controller/publish.d.ts +4 -0
  72. package/lib/host/controller/publish.d.ts.map +1 -0
  73. package/lib/host/controller/publish.js +56 -0
  74. package/lib/host/controller/publish.js.map +1 -0
  75. package/lib/host/controller/runs.d.ts +80 -0
  76. package/lib/host/controller/runs.d.ts.map +1 -0
  77. package/lib/host/controller/runs.js +394 -0
  78. package/lib/host/controller/runs.js.map +1 -0
  79. package/lib/host/controller/state.d.ts +172 -0
  80. package/lib/host/controller/state.d.ts.map +1 -0
  81. package/lib/host/controller/state.js +2 -0
  82. package/lib/host/controller/state.js.map +1 -0
  83. package/lib/host/controller/windows-run.d.ts +17 -0
  84. package/lib/host/controller/windows-run.d.ts.map +1 -0
  85. package/lib/host/controller/windows-run.js +109 -0
  86. package/lib/host/controller/windows-run.js.map +1 -0
  87. package/lib/host/discovery/candidates.d.ts +47 -0
  88. package/lib/host/discovery/candidates.d.ts.map +1 -0
  89. package/lib/host/discovery/candidates.js +68 -0
  90. package/lib/host/discovery/candidates.js.map +1 -0
  91. package/lib/host/discovery/index.d.ts +19 -0
  92. package/lib/host/discovery/index.d.ts.map +1 -0
  93. package/lib/host/discovery/index.js +19 -0
  94. package/lib/host/discovery/index.js.map +1 -0
  95. package/lib/host/discovery/probe.d.ts +29 -0
  96. package/lib/host/discovery/probe.d.ts.map +1 -0
  97. package/lib/host/discovery/probe.js +155 -0
  98. package/lib/host/discovery/probe.js.map +1 -0
  99. package/lib/host/discovery/scan.d.ts +72 -0
  100. package/lib/host/discovery/scan.d.ts.map +1 -0
  101. package/lib/host/discovery/scan.js +195 -0
  102. package/lib/host/discovery/scan.js.map +1 -0
  103. package/lib/host/flags.d.ts +65 -0
  104. package/lib/host/flags.d.ts.map +1 -0
  105. package/lib/host/flags.js +186 -0
  106. package/lib/host/flags.js.map +1 -0
  107. package/lib/host/kind.d.ts +45 -0
  108. package/lib/host/kind.d.ts.map +1 -0
  109. package/lib/host/kind.js +251 -0
  110. package/lib/host/kind.js.map +1 -0
  111. package/lib/host/manager.d.ts +21 -0
  112. package/lib/host/manager.d.ts.map +1 -0
  113. package/lib/host/manager.js +113 -0
  114. package/lib/host/manager.js.map +1 -0
  115. package/lib/host/migration.d.ts +26 -0
  116. package/lib/host/migration.d.ts.map +1 -0
  117. package/lib/host/migration.js +127 -0
  118. package/lib/host/migration.js.map +1 -0
  119. package/lib/host/platform.d.ts +38 -0
  120. package/lib/host/platform.d.ts.map +1 -0
  121. package/lib/host/platform.js +64 -0
  122. package/lib/host/platform.js.map +1 -0
  123. package/lib/host/plugin-meta.d.ts +21 -0
  124. package/lib/host/plugin-meta.d.ts.map +1 -0
  125. package/lib/host/plugin-meta.js +21 -0
  126. package/lib/host/plugin-meta.js.map +1 -0
  127. package/lib/host/schema.d.ts +361 -0
  128. package/lib/host/schema.d.ts.map +1 -0
  129. package/lib/host/schema.js +196 -0
  130. package/lib/host/schema.js.map +1 -0
  131. package/lib/host/settings.d.ts +28 -0
  132. package/lib/host/settings.d.ts.map +1 -0
  133. package/lib/host/settings.js +91 -0
  134. package/lib/host/settings.js.map +1 -0
  135. package/lib/host/status.d.ts +25 -0
  136. package/lib/host/status.d.ts.map +1 -0
  137. package/lib/host/status.js +45 -0
  138. package/lib/host/status.js.map +1 -0
  139. package/lib/host/tool.d.ts +65 -0
  140. package/lib/host/tool.d.ts.map +1 -0
  141. package/lib/host/tool.js +52 -0
  142. package/lib/host/tool.js.map +1 -0
  143. package/lib/host/types/config.d.ts +101 -0
  144. package/lib/host/types/config.d.ts.map +1 -0
  145. package/lib/host/types/config.js +2 -0
  146. package/lib/host/types/config.js.map +1 -0
  147. package/lib/host/types/executable.d.ts +35 -0
  148. package/lib/host/types/executable.d.ts.map +1 -0
  149. package/lib/host/types/executable.js +9 -0
  150. package/lib/host/types/executable.js.map +1 -0
  151. package/lib/host/types/index.d.ts +24 -0
  152. package/lib/host/types/index.d.ts.map +1 -0
  153. package/lib/host/types/index.js +2 -0
  154. package/lib/host/types/index.js.map +1 -0
  155. package/lib/host/types/plugin.d.ts +38 -0
  156. package/lib/host/types/plugin.d.ts.map +1 -0
  157. package/lib/host/types/plugin.js +10 -0
  158. package/lib/host/types/plugin.js.map +1 -0
  159. package/lib/host/types/services.d.ts +26 -0
  160. package/lib/host/types/services.d.ts.map +1 -0
  161. package/lib/host/types/services.js +9 -0
  162. package/lib/host/types/services.js.map +1 -0
  163. package/lib/host/types/settings.d.ts +180 -0
  164. package/lib/host/types/settings.d.ts.map +1 -0
  165. package/lib/host/types/settings.js +2 -0
  166. package/lib/host/types/settings.js.map +1 -0
  167. package/lib/host/types/stderr.d.ts +51 -0
  168. package/lib/host/types/stderr.d.ts.map +1 -0
  169. package/lib/host/types/stderr.js +9 -0
  170. package/lib/host/types/stderr.js.map +1 -0
  171. package/lib/host/types/windows.d.ts +39 -0
  172. package/lib/host/types/windows.d.ts.map +1 -0
  173. package/lib/host/types/windows.js +9 -0
  174. package/lib/host/types/windows.js.map +1 -0
  175. package/lib/host/values.d.ts +204 -0
  176. package/lib/host/values.d.ts.map +1 -0
  177. package/lib/host/values.js +404 -0
  178. package/lib/host/values.js.map +1 -0
  179. package/lib/host/windows/connect-flags.d.ts +27 -0
  180. package/lib/host/windows/connect-flags.d.ts.map +1 -0
  181. package/lib/host/windows/connect-flags.js +97 -0
  182. package/lib/host/windows/connect-flags.js.map +1 -0
  183. package/lib/host/windows/index.d.ts +31 -0
  184. package/lib/host/windows/index.d.ts.map +1 -0
  185. package/lib/host/windows/index.js +30 -0
  186. package/lib/host/windows/index.js.map +1 -0
  187. package/lib/host/windows/landing.d.ts +47 -0
  188. package/lib/host/windows/landing.d.ts.map +1 -0
  189. package/lib/host/windows/landing.js +174 -0
  190. package/lib/host/windows/landing.js.map +1 -0
  191. package/lib/host/windows/link.d.ts +11 -0
  192. package/lib/host/windows/link.d.ts.map +1 -0
  193. package/lib/host/windows/link.js +52 -0
  194. package/lib/host/windows/link.js.map +1 -0
  195. package/lib/host/windows/paths.d.ts +64 -0
  196. package/lib/host/windows/paths.d.ts.map +1 -0
  197. package/lib/host/windows/paths.js +148 -0
  198. package/lib/host/windows/paths.js.map +1 -0
  199. package/lib/host/windows/run.d.ts +74 -0
  200. package/lib/host/windows/run.d.ts.map +1 -0
  201. package/lib/host/windows/run.js +211 -0
  202. package/lib/host/windows/run.js.map +1 -0
  203. package/lib/host/windows/spawn.d.ts +22 -0
  204. package/lib/host/windows/spawn.d.ts.map +1 -0
  205. package/lib/host/windows/spawn.js +73 -0
  206. package/lib/host/windows/spawn.js.map +1 -0
  207. package/lib/index.d.ts +102 -0
  208. package/lib/index.d.ts.map +1 -0
  209. package/lib/index.js +102 -636
  210. package/lib/index.js.map +1 -0
  211. package/locale/en.json +6 -0
  212. package/locale/zh.json +6 -0
  213. package/package.json +57 -14
  214. package/cordis.patch.yml +0 -5
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Host-half assembly — the wiring file.
3
+ *
4
+ * `apply` is deliberately the only host module that knows the parts exist; each
5
+ * part keeps its own behavior:
6
+ *
7
+ * ./controller/ the live mirror, the executable runs, the Windows run, the
8
+ * capture paths
9
+ * ./manager.ts the `chrome` + `chrome-windows` kinds and the standalone
10
+ * translation — what this component says to the manager
11
+ * ./settings.ts the configuration form's attach + commit routing + id rule
12
+ * ./commands/ the two headless-mirror slash commands
13
+ * ./tool.ts the MCP-side `prelaunch_windows_chrome` trigger
14
+ *
15
+ * The ordering rule that must survive the split: read the live layers through
16
+ * `source()` at commit time, never capture them — cordis starts the `settings`
17
+ * injection callback after `apply` returns, so a captured value would pin every
18
+ * consumer to the row config and no mid-session GUI edit would ever reach a
19
+ * live connection.
20
+ *
21
+ * What changed with the component model: this row **no longer starts a bridge
22
+ * of its own**. It answers live `chrome` / `chrome-windows` connections instead,
23
+ * and answers nothing at all when no manager is mounted. Mounting therefore
24
+ * stands up the machine half (the mirror, the executable catalog, the runs) and
25
+ * registers the kinds; whether anything runs is the pool's decision.
26
+ *
27
+ * @param ctx - plugin context.
28
+ * @param config - validated config (every volatile field a reference).
29
+ */
30
+ import { type ChromeMcpControllerDeps } from "./controller/index.js";
31
+ import type { ChromeMcpConfig, PluginContext } from "./types/index.js";
32
+ /**
33
+ * @param deps - the controller's optional host overrides, passed straight
34
+ * through. Nothing ordinary supplies them — the wiring reaches the machine
35
+ * through the controller's own defaults, so this parameter is what lets a test
36
+ * or a diagnostic answer the machine half (the Windows run, the Windows-side
37
+ * executables the run folds into a candidate list) with a known list.
38
+ */
39
+ export declare function apply(ctx: PluginContext, config: ChromeMcpConfig, deps?: ChromeMcpControllerDeps): void;
40
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/host/apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAA6B,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAKhG,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEvE;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,GAAE,uBAA4B,GAAG,IAAI,CAc3G"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Host-half assembly — the wiring file.
3
+ *
4
+ * `apply` is deliberately the only host module that knows the parts exist; each
5
+ * part keeps its own behavior:
6
+ *
7
+ * ./controller/ the live mirror, the executable runs, the Windows run, the
8
+ * capture paths
9
+ * ./manager.ts the `chrome` + `chrome-windows` kinds and the standalone
10
+ * translation — what this component says to the manager
11
+ * ./settings.ts the configuration form's attach + commit routing + id rule
12
+ * ./commands/ the two headless-mirror slash commands
13
+ * ./tool.ts the MCP-side `prelaunch_windows_chrome` trigger
14
+ *
15
+ * The ordering rule that must survive the split: read the live layers through
16
+ * `source()` at commit time, never capture them — cordis starts the `settings`
17
+ * injection callback after `apply` returns, so a captured value would pin every
18
+ * consumer to the row config and no mid-session GUI edit would ever reach a
19
+ * live connection.
20
+ *
21
+ * What changed with the component model: this row **no longer starts a bridge
22
+ * of its own**. It answers live `chrome` / `chrome-windows` connections instead,
23
+ * and answers nothing at all when no manager is mounted. Mounting therefore
24
+ * stands up the machine half (the mirror, the executable catalog, the runs) and
25
+ * registers the kinds; whether anything runs is the pool's decision.
26
+ *
27
+ * @param ctx - plugin context.
28
+ * @param config - validated config (every volatile field a reference).
29
+ */
30
+ import { createChromeMcpController } from "./controller/index.js";
31
+ import { installChromeMcpCommands } from "./commands/index.js";
32
+ import { attachToManager } from "./manager.js";
33
+ import { installChromeMcpForm } from "./settings.js";
34
+ import { installPrelaunchWindowsChromeTool } from "./tool.js";
35
+ /**
36
+ * @param deps - the controller's optional host overrides, passed straight
37
+ * through. Nothing ordinary supplies them — the wiring reaches the machine
38
+ * through the controller's own defaults, so this parameter is what lets a test
39
+ * or a diagnostic answer the machine half (the Windows run, the Windows-side
40
+ * executables the run folds into a candidate list) with a known list.
41
+ */
42
+ export function apply(ctx, config, deps = {}) {
43
+ const controller = createChromeMcpController(ctx, config, deps);
44
+ // Stand the instance down when the mount goes away: a hot re-apply must not
45
+ // leave the previous instance's late answers (a scheduled retry above all)
46
+ // firing runs no one asked for.
47
+ ctx.effect(() => () => {
48
+ controller.dispose();
49
+ }, "chrome-mcp: controller teardown");
50
+ // The kinds come first, before the form: a connection saved under either
51
+ // kind is answered through them, and a proposal names them by id.
52
+ attachToManager(ctx, controller);
53
+ installChromeMcpForm(ctx, controller);
54
+ installPrelaunchWindowsChromeTool(ctx, controller);
55
+ installChromeMcpCommands(ctx, config, controller);
56
+ }
57
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/host/apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,yBAAyB,EAAgC,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,iCAAiC,EAAE,MAAM,WAAW,CAAC;AAG9D;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CAAC,GAAkB,EAAE,MAAuB,EAAE,OAAgC,EAAE;IACjG,MAAM,UAAU,GAAG,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAChE,4EAA4E;IAC5E,2EAA2E;IAC3E,gCAAgC;IAChC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE;QAClB,UAAU,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,EAAE,iCAAiC,CAAC,CAAC;IACtC,yEAAyE;IACzE,kEAAkE;IAClE,eAAe,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACjC,oBAAoB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACtC,iCAAiC,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACnD,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,74 @@
1
+ import type * as McpClient from "@deepseek-ai/dsh-mcp-client";
2
+ import type { BridgeSpawnOptions, BridgeSpawnResult, BridgeSpawnTarget, BridgeStderrLogPathOptions, BridgeStderrMode, ChromeMcpConfig } from "./types/index.js";
3
+ /** The CLI of the `chrome-devtools-mcp` this package depends on (pinned). */
4
+ export declare function pinnedServerCli(): string;
5
+ /** Whether a row runs the pinned server: it names no launcher of its own (the
6
+ * schema defaults, or blanks). */
7
+ export declare function usesPinnedServer(entry: Pick<ChromeMcpConfig, "command" | "package">): boolean;
8
+ /** The executable one row's server runs under: this Node for the pinned
9
+ * server, the row's own `command` otherwise. */
10
+ export declare function serverCommand(entry: Pick<ChromeMcpConfig, "command" | "package">, execPath?: string): string;
11
+ /**
12
+ * Build the stdio argv tail for one flags list — `<pinned cli> [extraFlags]`
13
+ * for the pinned server, `-y <package> [extraFlags]` for a row's own launcher;
14
+ * the argv {@link buildBridgeSpawn} runs after {@link serverCommand}.
15
+ * When `chromePath` is set and the flags do not already carry an executable-path
16
+ * option, `--executablePath=<path>` is appended (upstream option:
17
+ * https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md).
18
+ * A connect-mode flag takes the whole executable out of the argv: with
19
+ * `--browserUrl` / `--wsEndpoint` present there is nothing local to launch, so
20
+ * the path is never appended next to it — the bridge connects, it does not
21
+ * launch.
22
+ */
23
+ export declare function buildServerArgs(entry: ChromeMcpConfig, extraFlags: string[]): string[];
24
+ /**
25
+ * Default location of the bridge stderr log — `@comecaramelos/dsh-ancesters`'
26
+ * private per-host directory (`mkdtemp`, mode `0700`), one
27
+ * `<serverName>-bridge.log` per bridge, truncated on every spawn. Nothing in it
28
+ * can be planted by another local principal before the first spawn.
29
+ *
30
+ * `options.tmpdir` keeps a flat, deterministic name for tests and diagnostics.
31
+ * @param serverName - the bridge server namespace.
32
+ * @param options - tmpdir/pid overrides for tests and diagnostics.
33
+ */
34
+ export declare function bridgeStderrLogPath(serverName: unknown, options?: BridgeStderrLogPathOptions): string;
35
+ /**
36
+ * Build the stdio spawn target for one bridge run, honouring the effective
37
+ * stderr mode. The MCP stdio transport spawns the child with the default
38
+ * `stderr: "inherit"`, so every line it prints lands on the dsh console.
39
+ * "log" (default) launches the argv through ancestros' Node wrapper (D5:
40
+ * `process.execPath` + a shipped script, the same route on Linux, WSL and
41
+ * Windows): stdout keeps flowing as the MCP protocol stream, stderr goes to
42
+ * the log file, the argv reaches the child verbatim. "console" returns the raw
43
+ * command/args (inherited console output).
44
+ * @param target - { command, args, stderr?, logPath?, serverName? }.
45
+ * @param options - execPath/tmpdir/pid overrides for tests.
46
+ * @returns { command, args, redirect } — the log file when redirected, "" when
47
+ * the console echoes. There is no fallback left: Node is always there.
48
+ */
49
+ export declare function buildBridgeSpawn(target: BridgeSpawnTarget, options?: BridgeSpawnOptions): BridgeSpawnResult;
50
+ /**
51
+ * stdio config for the nested mcp-client bridge for one flags list + effective
52
+ * stderr mode. The args keep the user's original logical invocation shape
53
+ * (`npx -y <package> [extraFlags]`, run as `command`), wrapped through
54
+ * {@link buildBridgeSpawn} so the child's stderr lands in a log file instead of
55
+ * the console when the mode is "log". The returned config carries only
56
+ * dsh-mcp-client fields; `redirect` describes the wrapper for the
57
+ * host's own diagnostics.
58
+ */
59
+ export declare function bridgeConfig(entry: ChromeMcpConfig, extraFlags: string[], stderrMode?: BridgeStderrMode): {
60
+ config: McpClient.StdioConfig;
61
+ redirect: string;
62
+ };
63
+ /**
64
+ * Wrap a logger so a callback observes every level call before the
65
+ * original runs. Used to capture the nested bridge's own log lines (the only
66
+ * surface dsh-mcp-client exposes for connection failures — reconnect
67
+ * backoff warnings, spawn failures, give-ups) into the live mirror.
68
+ * The wrapper shares the underlying LoggerService (exporters/buffer included)
69
+ * and only shadows the level methods.
70
+ * @param base - the base logger to wrap (cordis LoggerService).
71
+ * @param note - callback invoked for every log level before the original runs.
72
+ */
73
+ export declare function captureLogger(base: Record<string, (...args: unknown[]) => unknown>, note: (level: string, args: unknown[]) => void): Record<string, (...args: unknown[]) => unknown>;
74
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/host/bridge.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,KAAK,SAAS,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,EACR,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,0BAA0B,EAC1B,gBAAgB,EAChB,eAAe,EAClB,MAAM,kBAAkB,CAAC;AAO1B,6EAA6E;AAC7E,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;kCACkC;AAClC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG,OAAO,CAI7F;AAED;gDACgD;AAChD,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,SAAS,CAAC,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAE9H;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAUtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAEzG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,kBAAuB,GAAG,iBAAiB,CAY/G;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,EAAE,gBAAgB;;;EAwBvG;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,EACrD,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAC/C,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAcjD"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * The nested `dsh-mcp-client` bridge: its stdio argv + config, the stderr
3
+ * capture wrapper, and the logger wrapper that captures its own log lines into
4
+ * the live mirror.
5
+ *
6
+ * The bridge's *logical* argv is the public contract the tests and the README
7
+ * quote. By default it is the `chrome-devtools-mcp` this package depends on,
8
+ * pinned, run by the same Node that runs the host —
9
+ * `<node> <chrome-devtools-mcp cli> [extraFlags]`, the way dsh's own
10
+ * chrome-devtools provider runs it: no `npx`, no registry on every start, the
11
+ * same route on Linux, WSL and Windows (breaking in 2.0). A row that names its
12
+ * own `command` or `package` keeps `<command> -y <package> [extraFlags]`.
13
+ * `--executablePath=<chromePath>` is appended only when a path is configured
14
+ * and the flags list does not already pin an executable. In the default "log"
15
+ * stderr mode that argv is not spawned
16
+ * raw: {@link buildBridgeSpawn} launches it through ancestros' Node wrapper, which
17
+ * hands the argv over verbatim (the child never sees the wrapper) and sends only
18
+ * stderr into a per-spawn log file, leaving stdout to carry the MCP protocol
19
+ * stream. Without this wrapper every startup/progress line npx prints would
20
+ * land directly on the dsh console — exactly what makes the connection feel
21
+ * loud. "console" mode spawns the argv unwrapped (inherited stderr, debug).
22
+ *
23
+ * The logger wrapper exists because dsh-mcp-client reports connection problems
24
+ * only through `ctx.logger`: without it the card would never surface a
25
+ * failed/recovered bridge.
26
+ */
27
+ import { fileURLToPath } from "node:url";
28
+ import { buildSpawn, stderrLogPath } from "@comecaramelos/dsh-ancesters/host";
29
+ import { LOG_LEVELS } from "./constants.js";
30
+ import { carriesExecutablePath, flagsSkipCheck } from "./flags.js";
31
+ import { readConfigField } from "./values.js";
32
+ /** The row defaults that mean "the pinned server": a row that never named its
33
+ * own launcher. */
34
+ const DEFAULT_COMMAND = "npx";
35
+ const DEFAULT_PACKAGE = "chrome-devtools-mcp@latest";
36
+ /** The CLI of the `chrome-devtools-mcp` this package depends on (pinned). */
37
+ export function pinnedServerCli() {
38
+ return fileURLToPath(import.meta.resolve("chrome-devtools-mcp/build/src/bin/chrome-devtools-mcp.js"));
39
+ }
40
+ /** Whether a row runs the pinned server: it names no launcher of its own (the
41
+ * schema defaults, or blanks). */
42
+ export function usesPinnedServer(entry) {
43
+ const command = String(entry.command ?? "").trim();
44
+ const pkg = String(entry.package ?? "").trim();
45
+ return (command === "" || command === DEFAULT_COMMAND) && (pkg === "" || pkg === DEFAULT_PACKAGE);
46
+ }
47
+ /** The executable one row's server runs under: this Node for the pinned
48
+ * server, the row's own `command` otherwise. */
49
+ export function serverCommand(entry, execPath = process.execPath) {
50
+ return usesPinnedServer(entry) ? execPath : String(entry.command);
51
+ }
52
+ /**
53
+ * Build the stdio argv tail for one flags list — `<pinned cli> [extraFlags]`
54
+ * for the pinned server, `-y <package> [extraFlags]` for a row's own launcher;
55
+ * the argv {@link buildBridgeSpawn} runs after {@link serverCommand}.
56
+ * When `chromePath` is set and the flags do not already carry an executable-path
57
+ * option, `--executablePath=<path>` is appended (upstream option:
58
+ * https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md).
59
+ * A connect-mode flag takes the whole executable out of the argv: with
60
+ * `--browserUrl` / `--wsEndpoint` present there is nothing local to launch, so
61
+ * the path is never appended next to it — the bridge connects, it does not
62
+ * launch.
63
+ */
64
+ export function buildServerArgs(entry, extraFlags) {
65
+ const flags = Array.isArray(extraFlags) ? extraFlags.filter((flag) => typeof flag === "string") : [];
66
+ const args = usesPinnedServer(entry) ? [pinnedServerCli(), ...flags] : ["-y", entry.package, ...flags];
67
+ // `chromePath` is a volatile field, so what it carries is either a reference
68
+ // or the value a caller handed in explicitly (the resolved selection).
69
+ const executable = readConfigField(entry.chromePath, "").trim();
70
+ if (executable !== "" && !flags.some((flag) => carriesExecutablePath(flag)) && !flagsSkipCheck(flags)) {
71
+ args.push(`--executablePath=${executable}`);
72
+ }
73
+ return args;
74
+ }
75
+ /**
76
+ * Default location of the bridge stderr log — `@comecaramelos/dsh-ancesters`'
77
+ * private per-host directory (`mkdtemp`, mode `0700`), one
78
+ * `<serverName>-bridge.log` per bridge, truncated on every spawn. Nothing in it
79
+ * can be planted by another local principal before the first spawn.
80
+ *
81
+ * `options.tmpdir` keeps a flat, deterministic name for tests and diagnostics.
82
+ * @param serverName - the bridge server namespace.
83
+ * @param options - tmpdir/pid overrides for tests and diagnostics.
84
+ */
85
+ export function bridgeStderrLogPath(serverName, options = {}) {
86
+ return stderrLogPath(serverName, options);
87
+ }
88
+ /**
89
+ * Build the stdio spawn target for one bridge run, honouring the effective
90
+ * stderr mode. The MCP stdio transport spawns the child with the default
91
+ * `stderr: "inherit"`, so every line it prints lands on the dsh console.
92
+ * "log" (default) launches the argv through ancestros' Node wrapper (D5:
93
+ * `process.execPath` + a shipped script, the same route on Linux, WSL and
94
+ * Windows): stdout keeps flowing as the MCP protocol stream, stderr goes to
95
+ * the log file, the argv reaches the child verbatim. "console" returns the raw
96
+ * command/args (inherited console output).
97
+ * @param target - { command, args, stderr?, logPath?, serverName? }.
98
+ * @param options - execPath/tmpdir/pid overrides for tests.
99
+ * @returns { command, args, redirect } — the log file when redirected, "" when
100
+ * the console echoes. There is no fallback left: Node is always there.
101
+ */
102
+ export function buildBridgeSpawn(target, options = {}) {
103
+ const mode = target.stderr === void 0 ? "log" : target.stderr;
104
+ return buildSpawn({
105
+ command: target.command,
106
+ args: [...target.args],
107
+ stderr: mode,
108
+ ...(typeof target.logPath === "string" && target.logPath !== "" ? { logPath: target.logPath } : {}),
109
+ serverName: target.serverName ?? "bridge"
110
+ }, options);
111
+ }
112
+ /**
113
+ * stdio config for the nested mcp-client bridge for one flags list + effective
114
+ * stderr mode. The args keep the user's original logical invocation shape
115
+ * (`npx -y <package> [extraFlags]`, run as `command`), wrapped through
116
+ * {@link buildBridgeSpawn} so the child's stderr lands in a log file instead of
117
+ * the console when the mode is "log". The returned config carries only
118
+ * dsh-mcp-client fields; `redirect` describes the wrapper for the
119
+ * host's own diagnostics.
120
+ */
121
+ export function bridgeConfig(entry, extraFlags, stderrMode) {
122
+ const spawned = buildBridgeSpawn({
123
+ command: serverCommand(entry),
124
+ args: buildServerArgs(entry, extraFlags),
125
+ stderr: stderrMode === void 0 ? entry.bridgeStderr : stderrMode,
126
+ logPath: entry.bridgeStderrLog,
127
+ serverName: entry.serverName
128
+ }, {});
129
+ const config = {
130
+ serverName: entry.serverName,
131
+ transport: "stdio",
132
+ command: spawned.command,
133
+ args: spawned.args,
134
+ env: entry.env,
135
+ cwd: entry.cwd,
136
+ toolCallTimeoutMs: entry.toolCallTimeoutMs,
137
+ maxInstructionBytes: entry.maxInstructionBytes,
138
+ failOnStartupError: entry.failOnStartupError,
139
+ reconnect: entry.reconnect
140
+ };
141
+ return { config, redirect: spawned.redirect };
142
+ }
143
+ /**
144
+ * Wrap a logger so a callback observes every level call before the
145
+ * original runs. Used to capture the nested bridge's own log lines (the only
146
+ * surface dsh-mcp-client exposes for connection failures — reconnect
147
+ * backoff warnings, spawn failures, give-ups) into the live mirror.
148
+ * The wrapper shares the underlying LoggerService (exporters/buffer included)
149
+ * and only shadows the level methods.
150
+ * @param base - the base logger to wrap (cordis LoggerService).
151
+ * @param note - callback invoked for every log level before the original runs.
152
+ */
153
+ export function captureLogger(base, note) {
154
+ const wrapper = ((...args) => {
155
+ const callBase = base;
156
+ return callBase(...args);
157
+ });
158
+ Object.setPrototypeOf(wrapper, Object.getPrototypeOf(base));
159
+ Object.assign(wrapper, base);
160
+ for (const level of LOG_LEVELS) {
161
+ wrapper[level] = (...args) => {
162
+ note(level, args);
163
+ return base[level]?.(...args);
164
+ };
165
+ }
166
+ return wrapper;
167
+ }
168
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/host/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** One catalog row: the path on the wire + the custom UI label. */
2
+ export type ChromeExecutableEntry = {
3
+ id: string;
4
+ name: string;
5
+ };
6
+ /** Whether one id is usable as an executable row (a path with no control
7
+ * characters, capped like the `chromePath` it stands for). */
8
+ export declare function isValidEntryId(id: unknown): boolean;
9
+ /** One row label: the custom name when it has one, else the path. */
10
+ export declare function entryLabel(entry: ChromeExecutableEntry): string;
11
+ /**
12
+ * Coerce anything into well-formed rows: trim both fields, drop ids that cannot
13
+ * go on the wire, keep the first row per id, cap the name, cap the list (the
14
+ * tail drops off). A served array is already normalized; one read straight from a
15
+ * form write is not, so the row writer is the place that makes it safe.
16
+ */
17
+ export declare function normalizeEntries(value: unknown): ChromeExecutableEntry[];
18
+ /**
19
+ * Merge the ids a scan found into the stored rows: rows keep their order and
20
+ * their custom names, ids that are not represented land last, nameless.
21
+ * @returns the merged rows, plus `changed` so a caller can skip a no-op write.
22
+ */
23
+ export declare function mergeEntries(stored: unknown, ids: readonly string[]): {
24
+ entries: ChromeExecutableEntry[];
25
+ changed: boolean;
26
+ };
27
+ /** The ids a catalog currently carries, in row order. */
28
+ export declare function entryIds(entries: readonly ChromeExecutableEntry[] | undefined): string[];
29
+ /** One row lookup by id (`undefined` when absent). */
30
+ export declare function findEntry(entries: readonly ChromeExecutableEntry[], id: string): ChromeExecutableEntry | undefined;
31
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/host/catalog.ts"],"names":[],"mappings":"AAuBA,mEAAmE;AACnE,MAAM,MAAM,qBAAqB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE;8DAC8D;AAC9D,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAKnD;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAG/D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,EAAE,CAexE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CACxB,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,SAAS,MAAM,EAAE,GACvB;IAAE,OAAO,EAAE,qBAAqB,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAYxD;AAED,yDAAyD;AACzD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,CAExF;AAED,sDAAsD;AACtD,wBAAgB,SAAS,CACrB,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,EAAE,EAAE,MAAM,GACX,qBAAqB,GAAG,SAAS,CAEnC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Host half — the named rows behind the executable picker.
3
+ *
4
+ * One row is `{ id, name }`: the `id` is what goes on the wire (the path the
5
+ * bridge launches with, appended as `--executablePath=<id>`), while `name` is
6
+ * purely the label the user chose for the UI — the same id-is-path / name-is-
7
+ * label split the reference plugin (`dsh-docker-desktop-mcp`) carries for its
8
+ * executables catalog, so the card is one widget shape instead of a bespoke
9
+ * dropdown-plus-text-box.
10
+ *
11
+ * Everything here is pure and total: nothing throws on odd input, so the
12
+ * settings `validate` hook can never stall the settings gate on a malformed
13
+ * row. The browser half mirrors these rules in `src/client/catalog.ts`
14
+ * (separate bundle, one source of truth per rule in each half).
15
+ *
16
+ * Merge rule (the reference's "Fetch executables" behaviour): keep every stored
17
+ * row in order with its custom name, append the ids the scan found that are not
18
+ * represented yet with an empty name. A row the user deleted only comes back if
19
+ * a later source still reports its id, which is why the legacy `chromeCustomPaths`
20
+ * MRU folds in exactly once, before the rows are written by anything else.
21
+ */
22
+ import { CATALOG_MAX_LENGTH, CATALOG_NAME_MAX_LENGTH, CHROME_PATH_PATTERN } from "./constants.js";
23
+ /** Whether one id is usable as an executable row (a path with no control
24
+ * characters, capped like the `chromePath` it stands for). */
25
+ export function isValidEntryId(id) {
26
+ if (typeof id !== "string")
27
+ return false;
28
+ const path = id.trim();
29
+ if (path === "" || path.length > 1024)
30
+ return false;
31
+ return CHROME_PATH_PATTERN.test(path);
32
+ }
33
+ /** One row label: the custom name when it has one, else the path. */
34
+ export function entryLabel(entry) {
35
+ const name = entry.name.trim();
36
+ return name === "" ? entry.id : name;
37
+ }
38
+ /**
39
+ * Coerce anything into well-formed rows: trim both fields, drop ids that cannot
40
+ * go on the wire, keep the first row per id, cap the name, cap the list (the
41
+ * tail drops off). A served array is already normalized; one read straight from a
42
+ * form write is not, so the row writer is the place that makes it safe.
43
+ */
44
+ export function normalizeEntries(value) {
45
+ if (!Array.isArray(value))
46
+ return [];
47
+ const out = [];
48
+ const seen = new Set();
49
+ for (const item of value) {
50
+ if (out.length === CATALOG_MAX_LENGTH)
51
+ break;
52
+ if (item === null || typeof item !== "object" || Array.isArray(item))
53
+ continue;
54
+ const raw = item;
55
+ const id = typeof raw.id === "string" ? raw.id.trim() : "";
56
+ if (!isValidEntryId(id) || seen.has(id))
57
+ continue;
58
+ seen.add(id);
59
+ const name = typeof raw.name === "string" ? raw.name.trim() : "";
60
+ out.push({ id, name: name.slice(0, CATALOG_NAME_MAX_LENGTH) });
61
+ }
62
+ return out;
63
+ }
64
+ /**
65
+ * Merge the ids a scan found into the stored rows: rows keep their order and
66
+ * their custom names, ids that are not represented land last, nameless.
67
+ * @returns the merged rows, plus `changed` so a caller can skip a no-op write.
68
+ */
69
+ export function mergeEntries(stored, ids) {
70
+ const entries = normalizeEntries(stored);
71
+ const present = new Set(entries.map((entry) => entry.id));
72
+ const merged = entries.slice();
73
+ for (const id of ids) {
74
+ if (merged.length >= CATALOG_MAX_LENGTH)
75
+ break;
76
+ const path = typeof id === "string" ? id.trim() : "";
77
+ if (!isValidEntryId(path) || present.has(path))
78
+ continue;
79
+ present.add(path);
80
+ merged.push({ id: path, name: "" });
81
+ }
82
+ return { entries: merged, changed: JSON.stringify(merged) !== JSON.stringify(entries) };
83
+ }
84
+ /** The ids a catalog currently carries, in row order. */
85
+ export function entryIds(entries) {
86
+ return (entries === void 0 ? [] : entries).map((entry) => entry.id);
87
+ }
88
+ /** One row lookup by id (`undefined` when absent). */
89
+ export function findEntry(entries, id) {
90
+ return entries.find((entry) => entry.id === id);
91
+ }
92
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/host/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ChromeMcpController } from "../controller/index.js";
2
+ import type { CommandDefinition } from "../types/index.js";
3
+ export declare function createChromePathCommand(controller: ChromeMcpController): CommandDefinition;
4
+ //# sourceMappingURL=chrome-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome-path.d.ts","sourceRoot":"","sources":["../../../src/host/commands/chrome-path.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,mBAAmB,GAAG,iBAAiB,CAgD1F"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `/chrome-path` — the headless mirror of the card's executable picker.
3
+ *
4
+ * It answers what the pill states and changes what it changes, reading the saved
5
+ * rows and the live mirror exactly the way the card reads them, and writing the
6
+ * one same persisted `chromePath` field through the controller's write-back
7
+ * channel (`selectChromePath` — `persistChromePath`, i.e. one `settings.mutate`
8
+ * on the row's entry id, the live clear riding along) — never a route of its
9
+ * own. `none` clears the user layer back to what the row config carries under
10
+ * it, which is the same answer a cleared picker reads with. And a path write
11
+ * answers against the same validation gate a GUI write runs through: the
12
+ * field's own schema pattern, so control characters are refused here exactly as
13
+ * they are refused on the card door.
14
+ */
15
+ import { entryLabel } from "../catalog.js";
16
+ import { CHROME_PATH_PATTERN } from "../constants.js";
17
+ import { readSavedExecutables } from "../values.js";
18
+ const USAGE = "Usage: /chrome-path <path> — select the Chrome executable the row launches by default. Use /chrome-path none to clear the selection back to the row config.";
19
+ export function createChromePathCommand(controller) {
20
+ return {
21
+ name: "chrome-path",
22
+ description: "Show or change the Chrome executable the row launches by default",
23
+ input: { hint: "[<path>|none]" },
24
+ async handler(invocation) {
25
+ const input = invocation.rawInput.trim();
26
+ // No argument answers the question the pill states, with the saved
27
+ // rows the picker would offer beneath it.
28
+ if (input === "") {
29
+ const live = controller.live;
30
+ const saved = readSavedExecutables(controller.source());
31
+ const lines = [
32
+ `Current executable: ${live.effectiveChromePath === "" ? "(nothing)" : live.effectiveChromePath}`,
33
+ `Source: ${live.effectiveSource === "" ? "nothing selected" : live.effectiveSource}.`
34
+ ];
35
+ lines.push(saved.length === 0 ? "Saved executables: (none — /chrome-refresh discovers them)." : `Saved executables:\n${saved.map((entry) => ` - ${entryLabel(entry)}`).join("\n")}`);
36
+ lines.push(USAGE);
37
+ return { kind: "success", text: lines.join("\n") };
38
+ }
39
+ // The one cleared write: the user layer answers empty again and the
40
+ // row config's own path stands underneath it.
41
+ if (input === "none") {
42
+ if (controller.live.effectiveChromePath === "" && controller.live.effectiveSource === "") {
43
+ return { kind: "success", text: "No executable is selected — there is nothing to clear." };
44
+ }
45
+ await controller.selectChromePath("");
46
+ return { kind: "success", text: "Executable selection cleared — the row launches by default with whatever the row config carries." };
47
+ }
48
+ // Same gate the field's write runs through (../schema.ts).
49
+ if (!CHROME_PATH_PATTERN.test(input)) {
50
+ return { kind: "error", text: "Invalid executable path: control characters (or a length over the path limit) are never valid." };
51
+ }
52
+ // Already what the bridge runs with: same write the picker makes on
53
+ // a no-change pick — nothing lands anywhere new.
54
+ if (controller.live.effectiveChromePath === input && controller.live.effectiveSource === "selected") {
55
+ return { kind: "success", text: `The row default already stands at ${input}.` };
56
+ }
57
+ await controller.selectChromePath(input);
58
+ return {
59
+ kind: "success",
60
+ text: `Chrome executable selected: ${input} — a default shifts with it: live connections without their own pick re-answer, and the executable run re-runs behind the write.`
61
+ };
62
+ }
63
+ };
64
+ }
65
+ //# sourceMappingURL=chrome-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome-path.js","sourceRoot":"","sources":["../../../src/host/commands/chrome-path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `/chrome-refresh` — the headless mirror of the card's **Fetch executables**.
3
+ *
4
+ * One executable run on demand: probe every candidate the row config, what this
5
+ * host carries, the saved rows and the selection carry, and merge what exists
6
+ * into the saved rows. The merge already stands inside the run (see
7
+ * ../controller/runs.ts — `runExecutableDiscovery` merges as its own step), so
8
+ * the headless path needs no merge step of its own: it fires the run and reads
9
+ * the answers off the live mirror the run publishes, never more than the run
10
+ * answered. The bump of `executableDiscoveryRevision` around the awaited run is
11
+ * what tells a reportable run from one that never completed — the statuses on
12
+ * the mirror could otherwise be from the last run.
13
+ */
14
+ import type { ChromeMcpController } from "../controller/index.js";
15
+ import type { CommandDefinition } from "../types/index.js";
16
+ export declare function createChromeRefreshCommand(controller: ChromeMcpController): CommandDefinition;
17
+ //# sourceMappingURL=chrome-refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome-refresh.d.ts","sourceRoot":"","sources":["../../../src/host/commands/chrome-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,mBAAmB,GAAG,iBAAiB,CAgC7F"}