Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions packages/app/src/docker-git/api-client-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import type {
AuthGithubLoginCommand,
AuthGithubLogoutCommand,
AuthGithubStatusCommand,
AuthGrokLogoutCommand,
AuthGrokStatusCommand,
AuthGitlabLoginCommand,
AuthGitlabLogoutCommand,
AuthGitlabStatusCommand
AuthGitlabStatusCommand,
AuthGrokLogoutCommand,
AuthGrokStatusCommand
} from "./frontend-lib/core/domain.js"
import { resolvePathFromCwd } from "./frontend-lib/usecases/path-helpers.js"
import type { ApiAuthRequiredError, ApiRequestError } from "./host-errors.js"
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/docker-git/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ export {
githubLogin,
githubLogout,
githubStatus,
grokLogout,
grokStatus,
gitlabLogin,
gitlabLogout,
gitlabStatus
gitlabStatus,
grokLogout,
grokStatus
} from "./api-client-auth.js"
export {
type ApiContainerTask,
Expand Down
9 changes: 6 additions & 3 deletions packages/app/src/docker-git/cli/usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,12 @@ Container runtime env (set via .orch/env/project.env):
MCP_PLAYWRIGHT_ISOLATED=1|0 Isolated browser contexts; default 0 shares the VNC session
MCP_PLAYWRIGHT_CDP_GUARD=1|0 Guard CDP so MCP cannot close/crash shared Chromium (default: 1)
MCP_PLAYWRIGHT_BLOCK_BROWSER_CLOSE=1|0 Block destructive Browser.close/crash CDP methods (default: 1)
MCP_PLAYWRIGHT_CDP_ENDPOINT=http://... Override CDP endpoint (default: http://dg-<repo>-browser:9223)
MCP_PLAYWRIGHT_RETRY_ATTEMPTS=<n> Retry attempts for nested browser startup wait (default: 10)
MCP_PLAYWRIGHT_RETRY_DELAY=<seconds> Delay between retry attempts (default: 2)
MCP_PLAYWRIGHT_CDP_ENDPOINT=http://... Override CDP endpoint (default: http://127.0.0.1:9223)
MCP_PLAYWRIGHT_CDP_TIMEOUT=<ms> CDP connect timeout passed to Playwright MCP (default: 60000)
MCP_PLAYWRIGHT_READY_ATTEMPTS=<n> Startup readiness attempts before disabling broken MCP (default: 60)
MCP_PLAYWRIGHT_READY_DELAY=<seconds> Delay between startup readiness attempts (default: 1)
MCP_PLAYWRIGHT_RETRY_ATTEMPTS=<n> Legacy CDP preflight attempts when CDP guard is disabled (default: 10)
MCP_PLAYWRIGHT_RETRY_DELAY=<seconds> Delay between legacy preflight attempts (default: 2)

Auth providers:
github, gh GitHub CLI auth (tokens saved to env file)
Expand Down
8 changes: 4 additions & 4 deletions packages/app/src/docker-git/program-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ import {
githubLogin,
githubLogout,
githubStatus,
grokLogout,
grokStatus,
gitlabLogin,
gitlabLogout,
gitlabStatus,
grokLogout,
grokStatus,
type JsonValue,
renderJsonPayload
} from "./api-client.js"
import { type ControllerRuntime, ensureControllerReady } from "./controller.js"
import type { Command } from "./frontend-lib/core/domain.js"
import type { ApiRequestError, CliError } from "./host-errors.js"
import { terminalAuthTitle } from "./menu-auth-shared.js"
import { attachTerminalSession } from "./terminal-session-client.js"
import { attachTerminalSession, type TerminalSessionClientError } from "./terminal-session-client.js"

type OperationalCommand = Exclude<Command, { readonly _tag: "Help" }>

Expand Down Expand Up @@ -111,7 +111,7 @@ const handleGrokLoginCommand = (
) =>
withControllerReady(
createAuthTerminalSession("GrokOauth", command.label).pipe(
Effect.flatMap((session) =>
Effect.flatMap((session): Effect.Effect<void, ApiRequestError | TerminalSessionClientError> =>
session === null
? Effect.fail(missingAuthTerminalSessionError("GrokOauth"))
: attachTerminalSession({
Expand Down
4 changes: 3 additions & 1 deletion packages/app/src/lib/core/templates-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { renderEntrypointGitConfig, renderEntrypointGitHooks } from "./templates
import { renderEntrypointGrokConfig } from "./templates-entrypoint/grok.js"
import { renderEntrypointDockerGitBootstrap } from "./templates-entrypoint/nested-docker-git.js"
import { renderEntrypointOpenCodeConfig } from "./templates-entrypoint/opencode.js"
import { renderEntrypointPlaywrightBrowserRuntime } from "./templates-entrypoint/playwright-browser.js"
import { renderEntrypointProjectAgentRules } from "./templates-entrypoint/project-rules.js"
import { renderEntrypointRtkConfig } from "./templates-entrypoint/rtk.js"
import { renderEntrypointBackgroundTasks } from "./templates-entrypoint/tasks.js"
Expand All @@ -47,7 +48,6 @@ export const renderEntrypoint = (config: TemplateConfig): string =>
renderEntrypointCodexHome(config),
renderEntrypointCodexSharedAuth(config),
renderEntrypointOpenCodeConfig(config),
renderEntrypointMcpPlaywright(config),
renderEntrypointZshShell(config),
renderEntrypointZshUserRc(config),
renderEntrypointPrompt(),
Expand All @@ -60,6 +60,8 @@ export const renderEntrypoint = (config: TemplateConfig): string =>
renderEntrypointProjectAgentRules(),
renderEntrypointAgentsNotice(config),
renderEntrypointDockerSocket(config),
renderEntrypointPlaywrightBrowserRuntime(config),
renderEntrypointMcpPlaywright(config),
renderEntrypointGitConfig(config),
renderEntrypointClaudeConfig(config),
renderEntrypointGeminiConfig(config),
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/lib/core/templates-entrypoint/claude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ const renderClaudeMcpPlaywrightConfig = (): string =>
String.raw`# Claude Code: keep Playwright MCP config in sync with container settings
CLAUDE_SETTINGS_FILE="${"$"}{CLAUDE_HOME_JSON:-$CLAUDE_CONFIG_DIR/.claude.json}"
docker_git_sync_claude_playwright_mcp() {
CLAUDE_SETTINGS_FILE="$CLAUDE_SETTINGS_FILE" MCP_PLAYWRIGHT_ENABLE="$MCP_PLAYWRIGHT_ENABLE" node - <<'NODE'
CLAUDE_SETTINGS_FILE="$CLAUDE_SETTINGS_FILE" MCP_PLAYWRIGHT_ENABLE="${"$"}{MCP_PLAYWRIGHT_ENABLE:-0}" node - <<'NODE'
const fs = require("node:fs")
const path = require("node:path")

Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/lib/core/templates-entrypoint/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ EOF
fi

if [[ -z "$MCP_PLAYWRIGHT_CDP_ENDPOINT" ]]; then
MCP_PLAYWRIGHT_CDP_ENDPOINT="http://__SERVICE_NAME__-browser:9223"
MCP_PLAYWRIGHT_CDP_ENDPOINT="http://127.0.0.1:9223"
fi

# Replace the docker-git Playwright block to allow upgrades via --force without manual edits.
Expand Down
47 changes: 35 additions & 12 deletions packages/app/src/lib/core/templates-entrypoint/gemini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,6 @@ const geminiSettingsJsonTemplate = `{
"selectedType": "oauth-personal"
},
"disableYoloMode": false
},
"mcpServers": {
"playwright": {
"command": "docker-git-playwright-mcp",
"args": [],
"trust": true
}
}
}`

Expand Down Expand Up @@ -204,10 +197,40 @@ if [[ -d /etc/sudoers.d ]]; then
chmod 0440 /etc/sudoers.d/gemini-agent
fi`

const renderGeminiMcpPlaywrightConfig = (_config: TemplateConfig): string =>
String.raw`# Gemini CLI: keep Playwright MCP config in sync (TODO: Gemini CLI MCP integration format)
# For now, Gemini CLI uses MCP via ~/.gemini/settings.json or command line.
# We'll ensure it has the same Playwright capability as Claude/Codex once format is confirmed.`
const renderGeminiMcpPlaywrightConfig = (): string =>
String.raw`# Gemini CLI: keep Playwright MCP config in sync with container settings
docker_git_sync_gemini_playwright_mcp() {
GEMINI_CONFIG_SETTINGS_FILE="$GEMINI_CONFIG_SETTINGS_FILE" MCP_PLAYWRIGHT_ENABLE="${"$"}{MCP_PLAYWRIGHT_ENABLE:-0}" node - <<'NODE'
const fs = require("node:fs")
const path = require("node:path")
const settingsPath = process.env.GEMINI_CONFIG_SETTINGS_FILE
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
if (typeof settingsPath !== "string" || settingsPath.length === 0) process.exit(0)

let settings = {}
try {
const parsed = JSON.parse(fs.readFileSync(settingsPath, "utf8"))
if (isRecord(parsed)) settings = parsed
} catch {}

const nextServers = { ...(isRecord(settings.mcpServers) ? settings.mcpServers : {}) }
if (process.env.MCP_PLAYWRIGHT_ENABLE === "1") {
nextServers.playwright = { command: "docker-git-playwright-mcp", args: [], trust: true }
} else {
delete nextServers.playwright
}

const nextSettings = { ...settings }
Object.keys(nextServers).length > 0 ? nextSettings.mcpServers = nextServers : delete nextSettings.mcpServers

if (JSON.stringify(settings) === JSON.stringify(nextSettings)) process.exit(0)

fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
fs.writeFileSync(settingsPath, JSON.stringify(nextSettings, null, 2) + "\n", { mode: 0o600 })
NODE
}

docker_git_sync_gemini_playwright_mcp`
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const renderGeminiProfileSetup = (config: TemplateConfig): string =>
String.raw`GEMINI_PROFILE="/etc/profile.d/gemini-config.sh"
Expand Down Expand Up @@ -311,7 +334,7 @@ export const renderEntrypointGeminiConfig = (config: TemplateConfig): string =>
[
renderGeminiAuthConfig(config),
renderGeminiPermissionSettingsConfig(config),
renderGeminiMcpPlaywrightConfig(config),
renderGeminiMcpPlaywrightConfig(),
renderGeminiSudoConfig(config),
renderGeminiProfileSetup(config),
renderEntrypointGeminiNotice(config)
Expand Down
45 changes: 37 additions & 8 deletions packages/app/src/lib/core/templates-entrypoint/grok.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,7 @@ const renderGrokAuthConfig = (config: TemplateConfig): string =>

const grokSettingsJsonTemplate = `{
"sandboxMode": "off",
"confirmBeforeToolUse": false,
"mcpServers": {
"playwright": {
"command": "docker-git-playwright-mcp",
"args": [],
"trust": true
}
}
"confirmBeforeToolUse": false
}`

const grokUserSettingsJsonTemplate = `{
Expand Down Expand Up @@ -189,6 +182,41 @@ GROK_SETTINGS_OWNER_GID="$(id -g "${config.sshUser}" 2>/dev/null || id -g)"
chown -R "$GROK_SETTINGS_OWNER_UID:$GROK_SETTINGS_OWNER_GID" "$GROK_SETTINGS_DIR" || true
chmod 0600 "$GROK_CONFIG_SETTINGS_FILE" "$GROK_USER_SETTINGS_FILE" 2>/dev/null || true`

const renderGrokMcpPlaywrightConfig = (): string =>
String.raw`# Grok CLI: keep Playwright MCP config in sync with container settings
docker_git_sync_grok_playwright_mcp() {
GROK_CONFIG_SETTINGS_FILE="$GROK_CONFIG_SETTINGS_FILE" MCP_PLAYWRIGHT_ENABLE="${"$"}{MCP_PLAYWRIGHT_ENABLE:-0}" node - <<'NODE'
const fs = require("node:fs")
const path = require("node:path")
const settingsPath = process.env.GROK_CONFIG_SETTINGS_FILE
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
if (typeof settingsPath !== "string" || settingsPath.length === 0) process.exit(0)

let settings = {}
try {
const parsed = JSON.parse(fs.readFileSync(settingsPath, "utf8"))
if (isRecord(parsed)) settings = parsed
} catch {}

const nextServers = { ...(isRecord(settings.mcpServers) ? settings.mcpServers : {}) }
if (process.env.MCP_PLAYWRIGHT_ENABLE === "1") {
nextServers.playwright = { command: "docker-git-playwright-mcp", args: [], trust: true }
} else {
delete nextServers.playwright
}

const nextSettings = { ...settings }
Object.keys(nextServers).length > 0 ? nextSettings.mcpServers = nextServers : delete nextSettings.mcpServers

if (JSON.stringify(settings) === JSON.stringify(nextSettings)) process.exit(0)

fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
fs.writeFileSync(settingsPath, JSON.stringify(nextSettings, null, 2) + "\n", { mode: 0o600 })
NODE
}

docker_git_sync_grok_playwright_mcp`
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const renderGrokSudoConfig = (config: TemplateConfig): string =>
String.raw`# Grok CLI: allow passwordless sudo for agent tasks
# Risk rationale: Grok runs inside an isolated per-project container. The sshUser
Expand Down Expand Up @@ -308,6 +336,7 @@ export const renderEntrypointGrokConfig = (config: TemplateConfig): string =>
[
renderGrokAuthConfig(config),
renderGrokPermissionSettingsConfig(config),
renderGrokMcpPlaywrightConfig(),
renderGrokSudoConfig(config),
renderGrokProfileSetup(config),
renderEntrypointGrokNotice(config)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { TemplateConfig } from "../domain.js"

// CHANGE: source and start the nested browser runtime from the main project entrypoint.
// WHY: issue #306 follow-up requires dg-*-browser to be owned by dg-* lifecycle, not a host-compose sibling.
// QUOTE(ТЗ): "раз это браузер контейнер от нашего контейнера то хотелось бы что бы он внутри нашего контейрнера и поднимался бы"
// REF: issue-306-browser-nested-runtime
// SOURCE: n/a
// FORMAT THEOREM: enable_mcp_playwright(project) -> entrypoint(project) attempts nested_browser_start(project)
// PURITY: SHELL
// EFFECT: sourced shell functions may call Docker when enabled
// INVARIANT: stop function is always defined before sshd lifecycle traps are installed
// COMPLEXITY: O(1)
export const renderEntrypointPlaywrightBrowserRuntime = (_config: TemplateConfig): string =>
String.raw`# Nested Playwright browser runtime. Defaults are no-ops so sshd cleanup can call them unconditionally.
docker_git_start_playwright_browser() { return 0; }
docker_git_stop_playwright_browser() { return 0; }

DOCKER_GIT_BROWSER_RUNTIME="/opt/docker-git/browser/docker-git-browser-runtime.sh"
if [[ -f "$DOCKER_GIT_BROWSER_RUNTIME" ]]; then
# shellcheck disable=SC1090
source "$DOCKER_GIT_BROWSER_RUNTIME"
fi

if [[ "$MCP_PLAYWRIGHT_ENABLE" == "1" ]]; then
docker_git_start_playwright_browser || true
else
docker_git_stop_playwright_browser || true
fi`
Loading
Loading