Skip to content
Open
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
10 changes: 10 additions & 0 deletions packages/app/tests/docker-git/entrypoint-auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:<port>")
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"`)
Expand Down
2 changes: 2 additions & 0 deletions packages/lib/src/core/templates-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
renderEntrypointHeader,
renderEntrypointInputRc,
renderEntrypointPackageCache,
renderEntrypointPublicIp,
renderEntrypointSshd,
renderEntrypointZshShell,
renderEntrypointZshUserRc
Expand Down Expand Up @@ -37,6 +38,7 @@ export const renderEntrypoint = (config: TemplateConfig): string =>
renderEntrypointHeader(config),
renderEntrypointDnsRepair(),
renderEntrypointPackageCache(config),
renderEntrypointPublicIp(),
renderEntrypointAuthorizedKeys(config),
renderEntrypointCodexHome(config),
renderEntrypointCodexSharedAuth(config),
Expand Down
34 changes: 34 additions & 0 deletions packages/lib/src/core/templates-entrypoint/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<EOF > "$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
Expand Down
11 changes: 11 additions & 0 deletions packages/lib/src/core/templates-entrypoint/claude-extra-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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__}"

Expand Down Expand Up @@ -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
Публичный IP контейнера: $DOCKER_GIT_PUBLIC_IP
Если даёшь пользователю URL для HTTP API, dev-сервера, UI или другого сервиса из контейнера, используй этот IP вместо localhost и 127.0.0.1.
Формат внешнего адреса: http://$DOCKER_GIT_PUBLIC_IP:<port>
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
Expand All @@ -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 внутри проекта, ты обязан их читать и соблюдать инструкции.
<!-- /docker-git-managed:claude-md -->
Expand Down
11 changes: 11 additions & 0 deletions packages/lib/src/core/templates-entrypoint/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Публичный IP контейнера: $DOCKER_GIT_PUBLIC_IP
Если даёшь пользователю URL для HTTP API, dev-сервера, UI или другого сервиса из контейнера, используй этот IP вместо localhost и 127.0.0.1.
Формат внешнего адреса: http://$DOCKER_GIT_PUBLIC_IP:<port>
EOF
)"
fi
SUBAGENTS_LINE="Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю."
if [[ "$REPO_REF" == issue-* ]]; then
ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
Expand Down Expand Up @@ -171,6 +180,7 @@ $WORKSPACES_LINE
$WORKSPACE_INFO_LINE
$FOCUS_LINE
$INTERNET_LINE
$PUBLIC_ACCESS_BLOCK
$SUBAGENTS_LINE
$MANAGED_END
EOF
Expand All @@ -190,6 +200,7 @@ $WORKSPACES_LINE
$WORKSPACE_INFO_LINE
$FOCUS_LINE
$INTERNET_LINE
$PUBLIC_ACCESS_BLOCK
$SUBAGENTS_LINE
$MANAGED_END
EOF
Expand Down
11 changes: 11 additions & 0 deletions packages/lib/src/core/templates-entrypoint/gemini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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=""
Expand Down Expand Up @@ -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
Публичный IP контейнера: $DOCKER_GIT_PUBLIC_IP
Если даёшь пользователю URL для HTTP API, dev-сервера, UI или другого сервиса из контейнера, используй этот IP вместо localhost и 127.0.0.1.
Формат внешнего адреса: http://$DOCKER_GIT_PUBLIC_IP:<port>
EOF
)"
fi

cat <<EOF > "$GEMINI_MD_PATH"
<!-- docker-git-managed:gemini-md -->
Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, gemini, claude, opencode, oh-my-opencode, sshpass, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
Expand All @@ -272,6 +282,7 @@ cat <<EOF > "$GEMINI_MD_PATH"
$GEMINI_WORKSPACE_CONTEXT
Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__
Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.
$GEMINI_PUBLIC_ACCESS_BLOCK
Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.
Если ты видишь файлы AGENTS.md, GEMINI.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.
<!-- /docker-git-managed:gemini-md -->
Expand Down