From 5294195b985eecaa570fe12890630e36d4699ff9 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 26 Mar 2026 00:17:13 +0000 Subject: [PATCH] Use public container IP in agent prompts --- .../tests/docker-git/entrypoint-auth.test.ts | 10 ++++++ packages/lib/src/core/templates-entrypoint.ts | 2 ++ .../lib/src/core/templates-entrypoint/base.ts | 34 +++++++++++++++++++ .../claude-extra-config.ts | 11 ++++++ .../src/core/templates-entrypoint/codex.ts | 11 ++++++ .../src/core/templates-entrypoint/gemini.ts | 11 ++++++ 6 files changed, 79 insertions(+) diff --git a/packages/app/tests/docker-git/entrypoint-auth.test.ts b/packages/app/tests/docker-git/entrypoint-auth.test.ts index d863bcb7..cf435247 100644 --- a/packages/app/tests/docker-git/entrypoint-auth.test.ts +++ b/packages/app/tests/docker-git/entrypoint-auth.test.ts @@ -79,12 +79,22 @@ describe("renderEntrypoint auth bridge", () => { expect(entrypoint).toContain("CLAUDE_GLOBAL_PROMPT_FILE=\"/home/dev/.claude/CLAUDE.md\"") expect(entrypoint).toContain("CLAUDE_AUTO_SYSTEM_PROMPT=\"${CLAUDE_AUTO_SYSTEM_PROMPT:-1}\"") expect(entrypoint).toContain("docker-git-managed:claude-md") + expect(entrypoint).toContain("DOCKER_GIT_PUBLIC_IP=\"${DOCKER_GIT_PUBLIC_IP:-}\"") + expect(entrypoint).toContain("docker_git_detect_public_ip()") + expect(entrypoint).toContain("PUBLIC_IP_PROFILE=\"/etc/profile.d/docker-git-public-ip.sh\"") + expect(entrypoint).toContain("docker_git_upsert_ssh_env \"DOCKER_GIT_PUBLIC_IP\" \"$DOCKER_GIT_PUBLIC_IP\"") + expect(entrypoint).toContain("Публичный IP контейнера: $DOCKER_GIT_PUBLIC_IP") + expect(entrypoint).toContain( + "Если даёшь пользователю URL для HTTP API, dev-сервера, UI или другого сервиса из контейнера, используй этот IP вместо localhost и 127.0.0.1." + ) + expect(entrypoint).toContain("Формат внешнего адреса: http://$DOCKER_GIT_PUBLIC_IP:") expect(entrypoint).toContain( "SUBAGENTS_LINE=\"Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.\"" ) expect(entrypoint.split("Для решения задач обязательно используй subagents.").length - 1).toBeGreaterThanOrEqual( 2 ) + expect(entrypoint.split("Публичный IP контейнера: $DOCKER_GIT_PUBLIC_IP").length - 1).toBeGreaterThanOrEqual(3) expect(entrypoint).toContain("token=\"${GITHUB_TOKEN:-}\"") expect(entrypoint).toContain("token=\"${GH_TOKEN:-}\"") expect(entrypoint).toContain(String.raw`printf "%s\n" "password=$token"`) diff --git a/packages/lib/src/core/templates-entrypoint.ts b/packages/lib/src/core/templates-entrypoint.ts index aeaea8bb..0daf3be1 100644 --- a/packages/lib/src/core/templates-entrypoint.ts +++ b/packages/lib/src/core/templates-entrypoint.ts @@ -7,6 +7,7 @@ import { renderEntrypointHeader, renderEntrypointInputRc, renderEntrypointPackageCache, + renderEntrypointPublicIp, renderEntrypointSshd, renderEntrypointZshShell, renderEntrypointZshUserRc @@ -37,6 +38,7 @@ export const renderEntrypoint = (config: TemplateConfig): string => renderEntrypointHeader(config), renderEntrypointDnsRepair(), renderEntrypointPackageCache(config), + renderEntrypointPublicIp(), renderEntrypointAuthorizedKeys(config), renderEntrypointCodexHome(config), renderEntrypointCodexSharedAuth(config), diff --git a/packages/lib/src/core/templates-entrypoint/base.ts b/packages/lib/src/core/templates-entrypoint/base.ts index 9b3f7a8f..ca873515 100644 --- a/packages/lib/src/core/templates-entrypoint/base.ts +++ b/packages/lib/src/core/templates-entrypoint/base.ts @@ -76,6 +76,40 @@ docker_git_upsert_ssh_env "NPM_CONFIG_CACHE" "$PACKAGE_NPM_CACHE" docker_git_upsert_ssh_env "npm_config_cache" "$PACKAGE_NPM_CACHE" docker_git_upsert_ssh_env "YARN_CACHE_FOLDER" "$PACKAGE_YARN_CACHE"` +export const renderEntrypointPublicIp = (): string => + `# Detect a non-local container IP and expose it for user-facing service URLs +DOCKER_GIT_PUBLIC_IP="\${DOCKER_GIT_PUBLIC_IP:-}" + +docker_git_detect_public_ip() { + local candidate="" + if [[ -n "$DOCKER_GIT_PUBLIC_IP" ]]; then + printf "%s" "$DOCKER_GIT_PUBLIC_IP" + return 0 + fi + + if command -v ip >/dev/null 2>&1; then + candidate="$(ip -o -4 addr show scope global 2>/dev/null \ + | awk '{split($4, parts, "/"); if (parts[1] != "127.0.0.1") { print parts[1]; exit }}')" + fi + + if [[ -z "$candidate" ]] && command -v hostname >/dev/null 2>&1; then + candidate="$(hostname -I 2>/dev/null | awk '{for (i = 1; i <= NF; i += 1) if ($i != "127.0.0.1") { print $i; exit }}')" + fi + + printf "%s" "$candidate" +} + +DOCKER_GIT_PUBLIC_IP="$(docker_git_detect_public_ip)" +export DOCKER_GIT_PUBLIC_IP + +PUBLIC_IP_PROFILE="/etc/profile.d/docker-git-public-ip.sh" +cat < "$PUBLIC_IP_PROFILE" +export DOCKER_GIT_PUBLIC_IP="$DOCKER_GIT_PUBLIC_IP" +EOF +chmod 0644 "$PUBLIC_IP_PROFILE" || true + +docker_git_upsert_ssh_env "DOCKER_GIT_PUBLIC_IP" "$DOCKER_GIT_PUBLIC_IP"` + export const renderEntrypointAuthorizedKeys = (config: TemplateConfig): string => `# 1) Authorized keys are mounted from host at /authorized_keys mkdir -p /home/${config.sshUser}/.ssh diff --git a/packages/lib/src/core/templates-entrypoint/claude-extra-config.ts b/packages/lib/src/core/templates-entrypoint/claude-extra-config.ts index e376aa66..f8c0a1ad 100644 --- a/packages/lib/src/core/templates-entrypoint/claude-extra-config.ts +++ b/packages/lib/src/core/templates-entrypoint/claude-extra-config.ts @@ -5,6 +5,7 @@ const entrypointClaudeGlobalPromptTemplate = String CLAUDE_GLOBAL_PROMPT_FILE="/home/__SSH_USER__/.claude/CLAUDE.md" CLAUDE_AUTO_SYSTEM_PROMPT="${"$"}{CLAUDE_AUTO_SYSTEM_PROMPT:-1}" CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: repository" +CLAUDE_PUBLIC_ACCESS_BLOCK="" REPO_REF_VALUE="${"$"}{REPO_REF:-__REPO_REF_DEFAULT__}" REPO_URL_VALUE="${"$"}{REPO_URL:-__REPO_URL_DEFAULT__}" @@ -40,6 +41,15 @@ elif [[ "$REPO_REF_VALUE" == refs/pull/*/head ]]; then fi fi +if [[ -n "$DOCKER_GIT_PUBLIC_IP" ]]; then + CLAUDE_PUBLIC_ACCESS_BLOCK="$(cat < +EOF +)" +fi + if [[ "$CLAUDE_AUTO_SYSTEM_PROMPT" == "1" ]]; then mkdir -p "$(dirname "$CLAUDE_GLOBAL_PROMPT_FILE")" chown 1000:1000 "$(dirname "$CLAUDE_GLOBAL_PROMPT_FILE")" 2>/dev/null || true @@ -52,6 +62,7 @@ if [[ "$CLAUDE_AUTO_SYSTEM_PROMPT" == "1" ]]; then $CLAUDE_WORKSPACE_CONTEXT Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__ Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе. +$CLAUDE_PUBLIC_ACCESS_BLOCK Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю. Если ты видишь файлы AGENTS.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции. diff --git a/packages/lib/src/core/templates-entrypoint/codex.ts b/packages/lib/src/core/templates-entrypoint/codex.ts index a2b3b932..76fca311 100644 --- a/packages/lib/src/core/templates-entrypoint/codex.ts +++ b/packages/lib/src/core/templates-entrypoint/codex.ts @@ -129,6 +129,15 @@ WORKSPACES_LINE="Доступные workspace пути: __TARGET_DIR__" WORKSPACE_INFO_LINE="Контекст workspace: repository" FOCUS_LINE="Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__" INTERNET_LINE="Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе." +PUBLIC_ACCESS_BLOCK="" +if [[ -n "$DOCKER_GIT_PUBLIC_IP" ]]; then + PUBLIC_ACCESS_BLOCK="$(cat < +EOF +)" +fi SUBAGENTS_LINE="Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю." if [[ "$REPO_REF" == issue-* ]]; then ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')" @@ -171,6 +180,7 @@ $WORKSPACES_LINE $WORKSPACE_INFO_LINE $FOCUS_LINE $INTERNET_LINE +$PUBLIC_ACCESS_BLOCK $SUBAGENTS_LINE $MANAGED_END EOF @@ -190,6 +200,7 @@ $WORKSPACES_LINE $WORKSPACE_INFO_LINE $FOCUS_LINE $INTERNET_LINE +$PUBLIC_ACCESS_BLOCK $SUBAGENTS_LINE $MANAGED_END EOF diff --git a/packages/lib/src/core/templates-entrypoint/gemini.ts b/packages/lib/src/core/templates-entrypoint/gemini.ts index b7a6f8a0..4b1a54df 100644 --- a/packages/lib/src/core/templates-entrypoint/gemini.ts +++ b/packages/lib/src/core/templates-entrypoint/gemini.ts @@ -232,6 +232,7 @@ docker_git_upsert_ssh_env "GEMINI_CLI_APPROVAL_MODE" "yolo"` const entrypointGeminiNoticeTemplate = String.raw`# Ensure global GEMINI.md exists for container context GEMINI_MD_PATH="__GEMINI_HOME__/GEMINI.md" GEMINI_WORKSPACE_CONTEXT="Контекст workspace: repository" +GEMINI_PUBLIC_ACCESS_BLOCK="" if [[ "$REPO_REF" == issue-* ]]; then ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')" ISSUE_URL="" @@ -264,6 +265,15 @@ elif [[ "$REPO_REF" == refs/pull/*/head ]]; then fi fi +if [[ -n "$DOCKER_GIT_PUBLIC_IP" ]]; then + GEMINI_PUBLIC_ACCESS_BLOCK="$(cat < +EOF +)" +fi + cat < "$GEMINI_MD_PATH" Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, gemini, claude, opencode, oh-my-opencode, sshpass, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~ @@ -272,6 +282,7 @@ cat < "$GEMINI_MD_PATH" $GEMINI_WORKSPACE_CONTEXT Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__ Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе. +$GEMINI_PUBLIC_ACCESS_BLOCK Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю. Если ты видишь файлы AGENTS.md, GEMINI.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.