Skip to content

Commit 8cd0d91

Browse files
committed
fix(browser): delegate start lifecycle to rust mcp
1 parent b3919b2 commit 8cd0d91

7 files changed

Lines changed: 43 additions & 110 deletions

File tree

packages/app/src/lib/core/templates-entrypoint/tasks.ts

Lines changed: 9 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -268,54 +268,19 @@ fi
268268
269269
${renderAgentLaunch(config)}
270270
) &`
271-
// CHANGE: start the external Rust browser module from the project entrypoint.
272-
// WHY: issue #347 moves browser ownership to ProverCoderAI/rust-browser-connection while keeping docker-git as caller.
271+
// CHANGE: leave browser start/reuse ownership with the external Rust MCP module.
272+
// WHY: issue #347 moves browser lifecycle to ProverCoderAI/rust-browser-connection; docker-git only wires MCP config and cleanup.
273273
// QUOTE(ТЗ): "Вынести noVNC + MCP Playright в единый модуль."
274274
// REF: issue-347
275275
// SOURCE: n/a
276-
// FORMAT THEOREM: MCP_PLAYWRIGHT_ENABLE=1 -> eventually running(DOCKER_GIT_BROWSER_CONTAINER_NAME)
276+
// FORMAT THEOREM: MCP_PLAYWRIGHT_ENABLE=1 -> browser-connection owns start/reuse(DOCKER_GIT_BROWSER_CONTAINER_NAME)
277277
// PURITY: SHELL
278-
// EFFECT: generated bash calls docker-git-browser-connection, which calls Docker.
279-
// INVARIANT: browser shares the project container network namespace, so CDP is http://127.0.0.1:9223 from agents.
280-
// COMPLEXITY: O(1) entrypoint orchestration; Docker build/run is delegated to Rust.
281-
const renderEntrypointRustBrowserConnectionStart = (): ReadonlyArray<string> => [
282-
"# Unified Rust browser connection (noVNC + CDP) for MCP Playwright + Hermes — per #347.",
283-
"# Defaults are safe no-ops unless MCP Playwright is enabled.",
284-
"docker_git_start_rust_browser_connection() {",
285-
" if [[ \"${MCP_PLAYWRIGHT_ENABLE:-0}\" != \"1\" ]]; then",
286-
" return 0",
287-
" fi",
288-
"",
289-
" local browser_bin=\"\"",
290-
" local candidate",
291-
" for candidate in /usr/local/bin/docker-git-browser-connection /root/.cargo/bin/docker-git-browser-connection /usr/local/cargo/bin/docker-git-browser-connection $(command -v docker-git-browser-connection 2>/dev/null || true); do",
292-
" if [[ -x \"$candidate\" ]]; then",
293-
" browser_bin=\"$candidate\"",
294-
" break",
295-
" fi",
296-
" done",
297-
"",
298-
" if [[ -z \"$browser_bin\" ]]; then",
299-
" echo \"[browser] WARNING: docker-git-browser-connection not found; Playwright MCP browser is unavailable\" >&2",
300-
" MCP_PLAYWRIGHT_ENABLE=0",
301-
" export MCP_PLAYWRIGHT_ENABLE",
302-
" return 0",
303-
" fi",
304-
"",
305-
" local project_container=\"${DOCKER_GIT_PROJECT_CONTAINER_NAME:-$(hostname)}\"",
306-
" local network_mode=\"container:${project_container}\"",
307-
" mkdir -p /var/log",
308-
" \"$browser_bin\" start --project \"$project_container\" --network \"$network_mode\" >> /var/log/docker-git-browser.log 2>&1 || {",
309-
" echo \"[browser] WARNING: Rust browser connection failed; see /var/log/docker-git-browser.log\" >&2",
310-
" MCP_PLAYWRIGHT_ENABLE=0",
311-
" export MCP_PLAYWRIGHT_ENABLE",
312-
" return 0",
313-
" }",
314-
" echo \"[browser] Rust browser connection is ready via $browser_bin on $network_mode\"",
315-
"}"
316-
]
317-
278+
// EFFECT: generated bash calls docker-git-browser-connection stop during teardown.
279+
// INVARIANT: docker-git entrypoint never eagerly starts the browser; browser-connection starts/reuses it on demand.
280+
// COMPLEXITY: O(1) cleanup orchestration; Docker lifecycle is delegated to Rust.
318281
const renderEntrypointRustBrowserConnectionStop = (): ReadonlyArray<string> => [
282+
"# Rust browser connection cleanup only; browser-connection owns start/reuse on demand.",
283+
"# Do not call docker-git-browser-connection start here, or lifecycle ownership is duplicated.",
319284
"docker_git_stop_playwright_browser() {",
320285
" if [[ \"${MCP_PLAYWRIGHT_ENABLE:-0}\" != \"1\" ]]; then",
321286
" return 0",
@@ -341,9 +306,5 @@ const renderEntrypointRustBrowserConnectionStop = (): ReadonlyArray<string> => [
341306

342307
export const renderEntrypointRustBrowserConnection = (): string =>
343308
[
344-
...renderEntrypointRustBrowserConnectionStart(),
345-
"",
346-
...renderEntrypointRustBrowserConnectionStop(),
347-
"",
348-
"docker_git_start_rust_browser_connection"
309+
...renderEntrypointRustBrowserConnectionStop()
349310
].join("\n")

packages/app/src/lib/core/templates/dockerfile.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ const dockerGitSessionSyncPackage = "@prover-coder-ai/docker-git-session-sync@la
8686

8787
const renderDockerfilePlaywrightRuntime = (config: TemplateConfig): string =>
8888
config.enableMcpPlaywright
89-
? `# Unified Rust browser (dg-*-browser) is started by docker-git-browser-connection binary
90-
# No more COPY of separate browser files — single session guaranteed by Rust module (see entrypoint and rust-browser-connection repo)
89+
? `# Unified Rust browser (dg-*-browser) start/reuse is owned by browser-connection
90+
# No more COPY of separate browser files — single session guaranteed by Rust module (see rust-browser-connection repo)
9191
# Old browser-vnc + cdp-guard duplication removed per #347`
9292
: ""
9393

packages/app/tests/docker-git/core-templates.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,13 @@ describe("app planFiles", () => {
6666
expect(dockerfile.contents).toContain("make build-essential docker.io")
6767
expect(dockerfile.contents).toContain("/usr/local/bin/browser-connection --version")
6868
expect(dockerfile.contents).not.toContain("docker-git-playwright-mcp")
69-
expect(entrypoint.contents).toContain("docker_git_start_rust_browser_connection")
69+
expect(entrypoint.contents).not.toContain("docker_git_start_rust_browser_connection")
70+
expect(entrypoint.contents).not.toContain("start --project")
71+
expect(entrypoint.contents).not.toContain("--no-start-browser")
7072
expect(entrypoint.contents).toContain("docker_git_stop_playwright_browser()")
7173
expect(entrypoint.contents).toContain("docker-git-browser-connection")
72-
expect(entrypoint.contents).toContain("local network_mode=\"container:${project_container}\"")
7374
expect(entrypoint.contents).toContain("stop --project \"$project_container\"")
75+
expect(entrypoint.contents).toContain('command = "browser-connection"')
76+
expect(entrypoint.contents).toContain('args = ["--project", "$DOCKER_GIT_BROWSER_PROJECT", "--network", "$DOCKER_GIT_BROWSER_NETWORK"]')
7477
})
7578
})

packages/lib/src/core/templates-entrypoint/tasks.ts

Lines changed: 9 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -268,54 +268,19 @@ fi
268268
269269
${renderAgentLaunch(config)}
270270
) &`
271-
// CHANGE: start the external Rust browser module from the project entrypoint.
272-
// WHY: issue #347 moves browser ownership to ProverCoderAI/rust-browser-connection while keeping docker-git as caller.
271+
// CHANGE: leave browser start/reuse ownership with the external Rust MCP module.
272+
// WHY: issue #347 moves browser lifecycle to ProverCoderAI/rust-browser-connection; docker-git only wires MCP config and cleanup.
273273
// QUOTE(ТЗ): "Вынести noVNC + MCP Playright в единый модуль."
274274
// REF: issue-347
275275
// SOURCE: n/a
276-
// FORMAT THEOREM: MCP_PLAYWRIGHT_ENABLE=1 -> eventually running(DOCKER_GIT_BROWSER_CONTAINER_NAME)
276+
// FORMAT THEOREM: MCP_PLAYWRIGHT_ENABLE=1 -> browser-connection owns start/reuse(DOCKER_GIT_BROWSER_CONTAINER_NAME)
277277
// PURITY: SHELL
278-
// EFFECT: generated bash calls docker-git-browser-connection, which calls Docker.
279-
// INVARIANT: browser shares the project container network namespace, so CDP is http://127.0.0.1:9223 from agents.
280-
// COMPLEXITY: O(1) entrypoint orchestration; Docker build/run is delegated to Rust.
281-
const renderEntrypointRustBrowserConnectionStart = (): ReadonlyArray<string> => [
282-
"# Unified Rust browser connection (noVNC + CDP) for MCP Playwright + Hermes — per #347.",
283-
"# Defaults are safe no-ops unless MCP Playwright is enabled.",
284-
"docker_git_start_rust_browser_connection() {",
285-
" if [[ \"${MCP_PLAYWRIGHT_ENABLE:-0}\" != \"1\" ]]; then",
286-
" return 0",
287-
" fi",
288-
"",
289-
" local browser_bin=\"\"",
290-
" local candidate",
291-
" for candidate in /usr/local/bin/docker-git-browser-connection /root/.cargo/bin/docker-git-browser-connection /usr/local/cargo/bin/docker-git-browser-connection $(command -v docker-git-browser-connection 2>/dev/null || true); do",
292-
" if [[ -x \"$candidate\" ]]; then",
293-
" browser_bin=\"$candidate\"",
294-
" break",
295-
" fi",
296-
" done",
297-
"",
298-
" if [[ -z \"$browser_bin\" ]]; then",
299-
" echo \"[browser] WARNING: docker-git-browser-connection not found; Playwright MCP browser is unavailable\" >&2",
300-
" MCP_PLAYWRIGHT_ENABLE=0",
301-
" export MCP_PLAYWRIGHT_ENABLE",
302-
" return 0",
303-
" fi",
304-
"",
305-
" local project_container=\"${DOCKER_GIT_PROJECT_CONTAINER_NAME:-$(hostname)}\"",
306-
" local network_mode=\"container:${project_container}\"",
307-
" mkdir -p /var/log",
308-
" \"$browser_bin\" start --project \"$project_container\" --network \"$network_mode\" >> /var/log/docker-git-browser.log 2>&1 || {",
309-
" echo \"[browser] WARNING: Rust browser connection failed; see /var/log/docker-git-browser.log\" >&2",
310-
" MCP_PLAYWRIGHT_ENABLE=0",
311-
" export MCP_PLAYWRIGHT_ENABLE",
312-
" return 0",
313-
" }",
314-
" echo \"[browser] Rust browser connection is ready via $browser_bin on $network_mode\"",
315-
"}"
316-
]
317-
278+
// EFFECT: generated bash calls docker-git-browser-connection stop during teardown.
279+
// INVARIANT: docker-git entrypoint never eagerly starts the browser; browser-connection starts/reuses it on demand.
280+
// COMPLEXITY: O(1) cleanup orchestration; Docker lifecycle is delegated to Rust.
318281
const renderEntrypointRustBrowserConnectionStop = (): ReadonlyArray<string> => [
282+
"# Rust browser connection cleanup only; browser-connection owns start/reuse on demand.",
283+
"# Do not call docker-git-browser-connection start here, or lifecycle ownership is duplicated.",
319284
"docker_git_stop_playwright_browser() {",
320285
" if [[ \"${MCP_PLAYWRIGHT_ENABLE:-0}\" != \"1\" ]]; then",
321286
" return 0",
@@ -341,9 +306,5 @@ const renderEntrypointRustBrowserConnectionStop = (): ReadonlyArray<string> => [
341306

342307
export const renderEntrypointRustBrowserConnection = (): string =>
343308
[
344-
...renderEntrypointRustBrowserConnectionStart(),
345-
"",
346-
...renderEntrypointRustBrowserConnectionStop(),
347-
"",
348-
"docker_git_start_rust_browser_connection"
309+
...renderEntrypointRustBrowserConnectionStop()
349310
].join("\n")

packages/lib/src/core/templates/dockerfile.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ const dockerGitSessionSyncPackage = "@prover-coder-ai/docker-git-session-sync@la
8686

8787
const renderDockerfilePlaywrightRuntime = (config: TemplateConfig): string =>
8888
config.enableMcpPlaywright
89-
? `# Unified Rust browser (dg-*-browser) is started by docker-git-browser-connection binary
90-
# No more COPY of separate browser files — single session guaranteed by Rust module (see entrypoint and rust-browser-connection repo)
89+
? `# Unified Rust browser (dg-*-browser) start/reuse is owned by browser-connection
90+
# No more COPY of separate browser files — single session guaranteed by Rust module (see rust-browser-connection repo)
9191
# Old browser-vnc + cdp-guard duplication removed per #347`
9292
: ""
9393

packages/lib/tests/core/templates.test.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ describe("renderDockerfile", () => {
237237
"cargo install --git https://github.com/ProverCoderAI/rust-browser-connection --rev acd76d19a96763c8b5616076443d15be59fc7f78 --locked --bins --root /usr/local",
238238
"/usr/local/bin/docker-git-browser-connection --version",
239239
"/usr/local/bin/browser-connection --version",
240-
"# Unified Rust browser (dg-*-browser) is started by docker-git-browser-connection binary"
240+
"# Unified Rust browser (dg-*-browser) start/reuse is owned by browser-connection"
241241
])
242242
expect(dockerfile).not.toContain("docker-git-playwright-mcp")
243243
expect(dockerfile).not.toContain("@playwright/mcp")
@@ -763,19 +763,25 @@ describe("renderDockerCompose", () => {
763763
expect(dockerfile?.contents).toContain("/usr/local/bin/browser-connection --version")
764764
expect(dockerfile?.contents).not.toContain("docker-git-playwright-mcp")
765765
expect(dockerfile?.contents).not.toContain("COPY Dockerfile.browser")
766-
expect(entrypoint?.contents).toContain("docker_git_start_rust_browser_connection")
766+
expect(entrypoint?.contents).not.toContain("docker_git_start_rust_browser_connection")
767+
expect(entrypoint?.contents).not.toContain("start --project")
768+
expect(entrypoint?.contents).not.toContain("--no-start-browser")
767769
expect(entrypoint?.contents).toContain("docker_git_stop_playwright_browser()")
768770
expect(entrypoint?.contents).toContain("docker-git-browser-connection")
769-
expect(entrypoint?.contents).toContain('local network_mode="container:${project_container}"')
770771
expect(entrypoint?.contents).toContain('stop --project "$project_container"')
772+
expect(entrypoint?.contents).toContain('command = "browser-connection"')
773+
expect(entrypoint?.contents).toContain('args = ["--project", "$DOCKER_GIT_BROWSER_PROJECT", "--network", "$DOCKER_GIT_BROWSER_NETWORK"]')
771774
})
772-
it("renders Rust browser startup before MCP client config", () => {
775+
it("renders Rust browser cleanup before MCP client config without eager startup", () => {
773776
const entrypoint = renderEntrypoint(makeTemplateConfig({ enableMcpPlaywright: true }))
774-
const browserRuntimeIndex = entrypoint.indexOf("docker_git_start_rust_browser_connection")
777+
const cleanupIndex = entrypoint.indexOf("docker_git_stop_playwright_browser()")
775778
const mcpConfigIndex = entrypoint.indexOf("[mcp_servers.playwright]")
776779

777-
expect(browserRuntimeIndex).toBeGreaterThanOrEqual(0)
778-
expect(mcpConfigIndex).toBeGreaterThan(browserRuntimeIndex)
780+
expect(cleanupIndex).toBeGreaterThanOrEqual(0)
781+
expect(mcpConfigIndex).toBeGreaterThan(cleanupIndex)
782+
expect(entrypoint).not.toContain("docker_git_start_rust_browser_connection")
783+
expect(entrypoint).not.toContain("start --project")
784+
expect(entrypoint).not.toContain("--no-start-browser")
779785
})
780786

781787
it("renders Browser MCP project fallback without set -u unbound variables", () => {

packages/lib/tests/usecases/mcp-playwright.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ describe("enableMcpPlaywrightProjectFiles", () => {
135135
expect(dockerfileAfter).not.toContain("COPY Dockerfile.browser")
136136

137137
const entrypointAfter = yield* _(fs.readFileString(path.join(outDir, "entrypoint.sh")))
138-
expect(entrypointAfter).toContain("docker_git_start_rust_browser_connection")
138+
expect(entrypointAfter).not.toContain("docker_git_start_rust_browser_connection")
139+
expect(entrypointAfter).not.toContain("start --project")
140+
expect(entrypointAfter).not.toContain("--no-start-browser")
139141
expect(entrypointAfter).toContain("docker_git_stop_playwright_browser()")
140142
expect(entrypointAfter).toContain("docker-git-browser-connection")
141143
expect(entrypointAfter).toContain('stop --project "$project_container"')

0 commit comments

Comments
 (0)