@prover-coder-ai/docker-git 1.0.44 → 1.0.46

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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { NodeContext, NodeRuntime } from "@effect/platform-node";
3
- import { Data, Duration, Effect, Either, Fiber, Match, Option, Schedule, pipe } from "effect";
3
+ import { Data, Deferred, Duration, Effect, Either, Fiber, Match, Option, Schedule, pipe } from "effect";
4
4
  import * as FileSystem from "@effect/platform/FileSystem";
5
5
  import * as Path from "@effect/platform/Path";
6
6
  import * as Command from "@effect/platform/Command";
@@ -286,7 +286,7 @@ var runCommandWithExitCodes = (spec, okExitCodes, onFailure) => Effect.gen(funct
286
286
  const exitCode = yield* _(Command.exitCode(buildCommand(spec, "inherit", "inherit", "inherit")));
287
287
  yield* _(ensureExitCode(Number(exitCode), okExitCodes, onFailure));
288
288
  });
289
- var runCommandExitCode = (spec) => Effect.map(Command.exitCode(buildCommand(spec, "pipe", "pipe", "inherit")), Number);
289
+ var runCommandExitCode = (spec) => Effect.map(Command.exitCode(buildCommand(spec, "pipe", "pipe", "pipe")), Number);
290
290
  var collectUint8Array$1 = (chunks) => Chunk.reduce(chunks, new Uint8Array(), (acc, curr) => {
291
291
  const next = new Uint8Array(acc.length + curr.length);
292
292
  next.set(acc);
@@ -1394,7 +1394,7 @@ var readProjectConfig = (baseDir) => Effect.gen(function* (_) {
1394
1394
  //#endregion
1395
1395
  //#region ../lib/src/usecases/docker-git-config-search.ts
1396
1396
  var isDockerGitConfig = (entry) => entry.endsWith("docker-git.json");
1397
- var shouldSkipDir = (entry) => entry === ".git" || entry === ".orch" || entry === ".docker-git" || entry === ".cache";
1397
+ var shouldSkipDir = (entry) => entry === ".git" || entry === ".orch" || entry === ".docker-git" || entry === ".cache" || entry === "node_modules";
1398
1398
  var isNotFoundStatError = (error) => error._tag === "SystemError" && error.reason === "NotFound";
1399
1399
  var processDockerGitEntry = (fs, path, dir, entry, state) => Effect.gen(function* (_) {
1400
1400
  if (shouldSkipDir(entry)) return;
@@ -1620,6 +1620,7 @@ var resolveTemplateResourceLimits = (template) => Effect.succeed(withDefaultReso
1620
1620
  var successExitCode = Number(ExitCode(0));
1621
1621
  var gitBaseEnv$1 = {
1622
1622
  GIT_TERMINAL_PROMPT: "0",
1623
+ GIT_SSH_COMMAND: "ssh -o BatchMode=yes",
1623
1624
  GIT_AUTHOR_NAME: "docker-git",
1624
1625
  GIT_AUTHOR_EMAIL: "docker-git@users.noreply.github.com",
1625
1626
  GIT_COMMITTER_NAME: "docker-git",
@@ -2178,6 +2179,7 @@ elif [[ "$TARGET_DIR" == "~/"* ]]; then
2178
2179
  fi
2179
2180
  CLAUDE_AUTH_LABEL="\${CLAUDE_AUTH_LABEL:-}"
2180
2181
  CODEX_AUTH_LABEL="\${CODEX_AUTH_LABEL:-}"
2182
+ GEMINI_AUTH_LABEL="\${GEMINI_AUTH_LABEL:-}"
2181
2183
  GIT_AUTH_USER="\${GIT_AUTH_USER:-\${GITHUB_USER:-x-access-token}}"
2182
2184
  GIT_AUTH_TOKEN="\${GIT_AUTH_TOKEN:-\${GITHUB_TOKEN:-\${GH_TOKEN:-}}}"
2183
2185
  GH_TOKEN="\${GH_TOKEN:-\${GIT_AUTH_TOKEN:-}}"
@@ -2980,8 +2982,8 @@ var renderEntrypointAgentsNotice = (config) => entrypointAgentsNoticeTemplate.re
2980
2982
  //#endregion
2981
2983
  //#region ../lib/src/core/templates-entrypoint/gemini.ts
2982
2984
  var geminiAuthRootContainerPath = (sshUser) => `/home/${sshUser}/.docker-git/.orch/auth/gemini`;
2983
- var geminiAuthConfigTemplate = String.raw`# Gemini CLI: expose GEMINI_API_KEY for SSH sessions (API key stored under ~/.docker-git/.orch/auth/gemini)
2984
- GEMINI_LABEL_RAW="${"$"}{GEMINI_AUTH_LABEL:-}"
2985
+ var geminiAuthConfigTemplate = String.raw`# Gemini CLI: expose GEMINI_HOME for sessions (OAuth cache lives under ~/.docker-git/.orch/auth/gemini)
2986
+ GEMINI_LABEL_RAW="$GEMINI_AUTH_LABEL"
2985
2987
  if [[ -z "$GEMINI_LABEL_RAW" ]]; then
2986
2988
  GEMINI_LABEL_RAW="default"
2987
2989
  fi
@@ -2994,29 +2996,16 @@ if [[ -z "$GEMINI_LABEL_NORM" ]]; then
2994
2996
  fi
2995
2997
 
2996
2998
  GEMINI_AUTH_ROOT="__GEMINI_AUTH_ROOT__"
2997
- GEMINI_AUTH_DIR="$GEMINI_AUTH_ROOT/$GEMINI_LABEL_NORM"
2999
+ export GEMINI_CONFIG_DIR="$GEMINI_AUTH_ROOT/$GEMINI_LABEL_NORM"
2998
3000
 
2999
- # Backward compatibility: if default auth is stored directly under gemini root, reuse it.
3000
- if [[ "$GEMINI_LABEL_NORM" == "default" ]]; then
3001
- GEMINI_ROOT_ENV_FILE="$GEMINI_AUTH_ROOT/.env"
3002
- if [[ -f "$GEMINI_ROOT_ENV_FILE" ]]; then
3003
- GEMINI_AUTH_DIR="$GEMINI_AUTH_ROOT"
3004
- fi
3005
- fi
3006
-
3007
- mkdir -p "$GEMINI_AUTH_DIR" || true
3001
+ mkdir -p "$GEMINI_CONFIG_DIR" || true
3008
3002
  GEMINI_HOME_DIR="__GEMINI_HOME_DIR__"
3009
3003
  mkdir -p "$GEMINI_HOME_DIR" || true
3010
3004
 
3011
- GEMINI_API_KEY_FILE="$GEMINI_AUTH_DIR/.api-key"
3012
- GEMINI_ENV_FILE="$GEMINI_AUTH_DIR/.env"
3013
- GEMINI_HOME_ENV_FILE="$GEMINI_HOME_DIR/.env"
3014
-
3015
3005
  docker_git_link_gemini_file() {
3016
3006
  local source_path="$1"
3017
3007
  local link_path="$2"
3018
3008
 
3019
- # Preserve user-created regular files and seed config dir once.
3020
3009
  if [[ -e "$link_path" && ! -L "$link_path" ]]; then
3021
3010
  if [[ -f "$link_path" && ! -e "$source_path" ]]; then
3022
3011
  cp "$link_path" "$source_path" || true
@@ -3028,91 +3017,170 @@ docker_git_link_gemini_file() {
3028
3017
  ln -sfn "$source_path" "$link_path" || true
3029
3018
  }
3030
3019
 
3031
- # Link Gemini .env file from auth dir to home dir
3032
- docker_git_link_gemini_file "$GEMINI_ENV_FILE" "$GEMINI_HOME_ENV_FILE"
3033
-
3034
- docker_git_refresh_gemini_api_key() {
3035
- local api_key=""
3036
- # Try to read from dedicated API key file first
3037
- if [[ -f "$GEMINI_API_KEY_FILE" ]]; then
3038
- api_key="$(tr -d '\r\n' < "$GEMINI_API_KEY_FILE")"
3039
- fi
3040
- # Fall back to .env file
3041
- if [[ -z "$api_key" && -f "$GEMINI_ENV_FILE" ]]; then
3042
- api_key="$(grep -E '^GEMINI_API_KEY=' "$GEMINI_ENV_FILE" 2>/dev/null | head -1 | cut -d'=' -f2- | tr -d '\r\n' | sed "s/^['\"]//;s/['\"]$//")"
3043
- fi
3044
- if [[ -n "$api_key" ]]; then
3045
- export GEMINI_API_KEY="$api_key"
3046
- else
3047
- unset GEMINI_API_KEY || true
3048
- fi
3049
- }
3020
+ # Link .api-key and .env from central auth storage to container home
3021
+ docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.api-key" "$GEMINI_HOME_DIR/.api-key"
3022
+ docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.env" "$GEMINI_HOME_DIR/.env"
3050
3023
 
3051
- docker_git_refresh_gemini_api_key`;
3052
- var renderGeminiAuthConfig = (config) => geminiAuthConfigTemplate.replaceAll("__GEMINI_AUTH_ROOT__", geminiAuthRootContainerPath(config.sshUser)).replaceAll("__GEMINI_HOME_DIR__", `/home/${config.sshUser}/.gemini`);
3053
- var renderGeminiCliInstall = () => String.raw`# Gemini CLI: ensure CLI command exists (non-blocking startup self-heal)
3054
- docker_git_ensure_gemini_cli() {
3055
- if command -v gemini >/dev/null 2>&1; then
3056
- return 0
3024
+ # Ensure gemini YOLO wrapper exists
3025
+ GEMINI_REAL_BIN="$(command -v gemini || echo "/usr/local/bin/gemini")"
3026
+ GEMINI_WRAPPER_BIN="/usr/local/bin/gemini-wrapper"
3027
+ if [[ -f "$GEMINI_REAL_BIN" && "$GEMINI_REAL_BIN" != "$GEMINI_WRAPPER_BIN" ]]; then
3028
+ if [[ ! -f "$GEMINI_WRAPPER_BIN" ]]; then
3029
+ cat <<'EOF' > "$GEMINI_WRAPPER_BIN"
3030
+ #!/usr/bin/env bash
3031
+ GEMINI_ORIGINAL_BIN="__GEMINI_REAL_BIN__"
3032
+ exec "$GEMINI_ORIGINAL_BIN" --yolo "$@"
3033
+ EOF
3034
+ sed -i "s#__GEMINI_REAL_BIN__#$GEMINI_REAL_BIN#g" "$GEMINI_WRAPPER_BIN" || true
3035
+ chmod 0755 "$GEMINI_WRAPPER_BIN" || true
3036
+ # Create an alias or symlink if needed, but here we just ensure it exists
3057
3037
  fi
3038
+ fi
3058
3039
 
3059
- if ! command -v npm >/dev/null 2>&1; then
3060
- return 0
3040
+ # Special case for .gemini folder: we want the folder itself to be the link if it doesn't exist
3041
+ # or its content to be linked if we want to manage it.
3042
+ if [[ -d "$GEMINI_CONFIG_DIR/.gemini" ]]; then
3043
+ if [[ -L "$GEMINI_HOME_DIR" ]]; then
3044
+ rm -f "$GEMINI_HOME_DIR"
3045
+ elif [[ -d "$GEMINI_HOME_DIR" ]]; then
3046
+ # If it's a real directory, move it aside if it's empty or just has our managed files
3047
+ mv "$GEMINI_HOME_DIR" "$GEMINI_HOME_DIR.bak-$(date +%s)" || true
3061
3048
  fi
3049
+ ln -sfn "$GEMINI_CONFIG_DIR/.gemini" "$GEMINI_HOME_DIR"
3050
+ fi
3062
3051
 
3063
- NPM_ROOT="$(npm root -g 2>/dev/null || true)"
3064
- GEMINI_CLI_JS="$NPM_ROOT/@google/gemini-cli/build/cli.js"
3065
- if [[ -z "$NPM_ROOT" || ! -f "$GEMINI_CLI_JS" ]]; then
3066
- echo "docker-git: gemini cli.js not found under npm global root; skip shim restore" >&2
3067
- return 0
3052
+ docker_git_refresh_gemini_env() {
3053
+ # If .api-key exists, export it as GEMINI_API_KEY
3054
+ if [[ -f "$GEMINI_HOME_DIR/.api-key" ]]; then
3055
+ export GEMINI_API_KEY="$(cat "$GEMINI_HOME_DIR/.api-key" | tr -d '\r\n')"
3056
+ elif [[ -f "$GEMINI_HOME_DIR/.env" ]]; then
3057
+ # Parse GEMINI_API_KEY from .env
3058
+ API_KEY="$(grep "^GEMINI_API_KEY=" "$GEMINI_HOME_DIR/.env" | cut -d'=' -f2- | sed "s/^['\"]//;s/['\"]$//")"
3059
+ if [[ -n "$API_KEY" ]]; then
3060
+ export GEMINI_API_KEY="$API_KEY"
3061
+ fi
3068
3062
  fi
3063
+ }
3069
3064
 
3070
- # Rebuild a minimal shim when npm package exists but binary link is missing.
3071
- cat <<'EOF' > /usr/local/bin/gemini
3072
- #!/usr/bin/env bash
3073
- set -euo pipefail
3065
+ docker_git_refresh_gemini_env`;
3066
+ var renderGeminiAuthConfig = (config) => geminiAuthConfigTemplate.replaceAll("__GEMINI_AUTH_ROOT__", geminiAuthRootContainerPath(config.sshUser)).replaceAll("__GEMINI_HOME_DIR__", config.geminiHome);
3067
+ var renderGeminiPermissionSettingsConfig = (config) => String.raw`# Gemini CLI: keep trust settings in sync with docker-git defaults
3068
+ GEMINI_SETTINGS_DIR="${config.geminiHome}"
3069
+ GEMINI_TRUST_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/trustedFolders.json"
3070
+ GEMINI_CONFIG_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/settings.json"
3074
3071
 
3075
- if ! command -v npm >/dev/null 2>&1; then
3076
- echo "gemini: npm is required but missing" >&2
3077
- exit 127
3078
- fi
3072
+ # Wait for symlink to be established by the auth config step
3073
+ mkdir -p "$GEMINI_SETTINGS_DIR" || true
3079
3074
 
3080
- NPM_ROOT="$(npm root -g 2>/dev/null || true)"
3081
- GEMINI_CLI_JS="$NPM_ROOT/@google/gemini-cli/build/cli.js"
3082
- if [[ -z "$NPM_ROOT" || ! -f "$GEMINI_CLI_JS" ]]; then
3083
- echo "gemini: cli.js not found under npm global root" >&2
3084
- exit 127
3075
+ # Disable folder trust prompt and enable auto-approval in settings.json
3076
+ if [[ ! -f "$GEMINI_CONFIG_SETTINGS_FILE" ]]; then
3077
+ cat <<'EOF' > "$GEMINI_CONFIG_SETTINGS_FILE"
3078
+ {
3079
+ "security": {
3080
+ "folderTrust": {
3081
+ "enabled": false
3082
+ },
3083
+ "approvalPolicy": "never"
3084
+ }
3085
+ }
3086
+ EOF
3085
3087
  fi
3086
3088
 
3087
- exec node "$GEMINI_CLI_JS" "$@"
3088
- EOF
3089
- chmod 0755 /usr/local/bin/gemini || true
3090
- ln -sf /usr/local/bin/gemini /usr/bin/gemini || true
3089
+ # Pre-trust important directories in trustedFolders.json
3090
+ # Use flat mapping as required by recent Gemini CLI versions
3091
+ cat <<'EOF' > "$GEMINI_TRUST_SETTINGS_FILE"
3092
+ {
3093
+ "/": "TRUST_FOLDER",
3094
+ "${config.geminiHome}": "TRUST_FOLDER",
3095
+ "${config.targetDir}": "TRUST_FOLDER"
3091
3096
  }
3097
+ EOF
3092
3098
 
3093
- docker_git_ensure_gemini_cli`;
3094
- var renderGeminiProfileSetup = () => String.raw`GEMINI_PROFILE="/etc/profile.d/gemini-config.sh"
3095
- printf "export GEMINI_AUTH_LABEL=%q\n" "${"$"}{GEMINI_AUTH_LABEL:-default}" > "$GEMINI_PROFILE"
3099
+ chown -R 1000:1000 "$GEMINI_SETTINGS_DIR" || true
3100
+ chmod 0600 "$GEMINI_TRUST_SETTINGS_FILE" "$GEMINI_CONFIG_SETTINGS_FILE" 2>/dev/null || true`;
3101
+ var renderGeminiSudoConfig = (config) => String.raw`# Gemini CLI: allow passwordless sudo for agent tasks
3102
+ if [[ -d /etc/sudoers.d ]]; then
3103
+ echo "${config.sshUser} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/gemini-agent
3104
+ chmod 0440 /etc/sudoers.d/gemini-agent
3105
+ fi`;
3106
+ var renderGeminiMcpPlaywrightConfig = (_config) => String.raw`# Gemini CLI: keep Playwright MCP config in sync (TODO: Gemini CLI MCP integration format)
3107
+ # For now, Gemini CLI uses MCP via ~/.gemini/settings.json or command line.
3108
+ # We'll ensure it has the same Playwright capability as Claude/Codex once format is confirmed.`;
3109
+ var renderGeminiProfileSetup = (config) => String.raw`GEMINI_PROFILE="/etc/profile.d/gemini-config.sh"
3110
+ printf "export GEMINI_AUTH_LABEL=%q\n" "$GEMINI_AUTH_LABEL" > "$GEMINI_PROFILE"
3111
+ printf "export GEMINI_HOME=%q\n" "${config.geminiHome}" >> "$GEMINI_PROFILE"
3112
+ printf "export GEMINI_CLI_DISABLE_UPDATE_CHECK=true\n" >> "$GEMINI_PROFILE"
3113
+ printf "export GEMINI_CLI_NONINTERACTIVE=true\n" >> "$GEMINI_PROFILE"
3114
+ printf "export GEMINI_CLI_APPROVAL_MODE=yolo\n" >> "$GEMINI_PROFILE"
3115
+ printf "alias gemini='/usr/local/bin/gemini-wrapper'\n" >> "$GEMINI_PROFILE"
3096
3116
  cat <<'EOF' >> "$GEMINI_PROFILE"
3097
- GEMINI_API_KEY_FILE="${"$"}{GEMINI_AUTH_DIR:-$HOME/.gemini}/.api-key"
3098
- GEMINI_ENV_FILE="${"$"}{GEMINI_AUTH_DIR:-$HOME/.gemini}/.env"
3099
- if [[ -f "$GEMINI_API_KEY_FILE" ]]; then
3100
- export GEMINI_API_KEY="$(tr -d '\r\n' < "$GEMINI_API_KEY_FILE")"
3101
- elif [[ -f "$GEMINI_ENV_FILE" ]]; then
3102
- GEMINI_KEY="$(grep -E '^GEMINI_API_KEY=' "$GEMINI_ENV_FILE" 2>/dev/null | head -1 | cut -d'=' -f2- | tr -d '\r\n' | sed "s/^['\"]//;s/['\"]$//")"
3103
- if [[ -n "$GEMINI_KEY" ]]; then
3104
- export GEMINI_API_KEY="$GEMINI_KEY"
3105
- fi
3117
+ if [[ -f "$GEMINI_HOME/.api-key" ]]; then
3118
+ export GEMINI_API_KEY="$(cat "$GEMINI_HOME/.api-key" | tr -d '\r\n')"
3106
3119
  fi
3107
3120
  EOF
3108
3121
  chmod 0644 "$GEMINI_PROFILE" || true
3109
3122
 
3110
- docker_git_upsert_ssh_env "GEMINI_AUTH_LABEL" "${"$"}{GEMINI_AUTH_LABEL:-default}"
3111
- docker_git_upsert_ssh_env "GEMINI_API_KEY" "${"$"}{GEMINI_API_KEY:-}"`;
3123
+ docker_git_upsert_ssh_env "GEMINI_AUTH_LABEL" "$GEMINI_AUTH_LABEL"
3124
+ docker_git_upsert_ssh_env "GEMINI_API_KEY" "\${GEMINI_API_KEY:-}"
3125
+ docker_git_upsert_ssh_env "GEMINI_CLI_DISABLE_UPDATE_CHECK" "true"
3126
+ docker_git_upsert_ssh_env "GEMINI_CLI_NONINTERACTIVE" "true"
3127
+ docker_git_upsert_ssh_env "GEMINI_CLI_APPROVAL_MODE" "yolo"`;
3128
+ var entrypointGeminiNoticeTemplate = String.raw`# Ensure global GEMINI.md exists for container context
3129
+ GEMINI_MD_PATH="__GEMINI_HOME__/GEMINI.md"
3130
+ GEMINI_WORKSPACE_CONTEXT="Контекст workspace: repository"
3131
+ if [[ "$REPO_REF" == issue-* ]]; then
3132
+ ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
3133
+ ISSUE_URL=""
3134
+ if [[ "$REPO_URL" == https://github.com/* ]]; then
3135
+ ISSUE_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')"
3136
+ if [[ -n "$ISSUE_REPO" ]]; then
3137
+ ISSUE_URL="https://github.com/$ISSUE_REPO/issues/$ISSUE_ID"
3138
+ fi
3139
+ fi
3140
+ if [[ -n "$ISSUE_URL" ]]; then
3141
+ GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID ($ISSUE_URL)"
3142
+ else
3143
+ GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID"
3144
+ fi
3145
+ elif [[ "$REPO_REF" == refs/pull/*/head ]]; then
3146
+ PR_ID="$(printf "%s" "$REPO_REF" | sed -nE 's#^refs/pull/([0-9]+)/head$#\1#p')"
3147
+ PR_URL=""
3148
+ if [[ "$REPO_URL" == https://github.com/* && -n "$PR_ID" ]]; then
3149
+ PR_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')"
3150
+ if [[ -n "$PR_REPO" ]]; then
3151
+ PR_URL="https://github.com/$PR_REPO/pull/$PR_ID"
3152
+ fi
3153
+ fi
3154
+ if [[ -n "$PR_ID" && -n "$PR_URL" ]]; then
3155
+ GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID ($PR_URL)"
3156
+ elif [[ -n "$PR_ID" ]]; then
3157
+ GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID"
3158
+ else
3159
+ GEMINI_WORKSPACE_CONTEXT="Контекст workspace: pull request ($REPO_REF)"
3160
+ fi
3161
+ fi
3162
+
3163
+ cat <<EOF > "$GEMINI_MD_PATH"
3164
+ <!-- docker-git-managed:gemini-md -->
3165
+ Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, gemini, claude, opencode, oh-my-opencode, sshpass, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
3166
+ Рабочая папка проекта (git clone): __TARGET_DIR__
3167
+ Доступные workspace пути: __TARGET_DIR__
3168
+ $GEMINI_WORKSPACE_CONTEXT
3169
+ Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__
3170
+ Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.
3171
+ Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.
3172
+ Если ты видишь файлы AGENTS.md, GEMINI.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.
3173
+ <!-- /docker-git-managed:gemini-md -->
3174
+ EOF
3175
+ chown 1000:1000 "$GEMINI_MD_PATH" || true`;
3176
+ var renderEntrypointGeminiNotice = (config) => entrypointGeminiNoticeTemplate.replaceAll("__GEMINI_HOME__", config.geminiHome).replaceAll("__TARGET_DIR__", config.targetDir);
3112
3177
  var renderEntrypointGeminiConfig = (config) => [
3113
3178
  renderGeminiAuthConfig(config),
3114
- renderGeminiCliInstall(),
3115
- renderGeminiProfileSetup()
3179
+ renderGeminiPermissionSettingsConfig(config),
3180
+ renderGeminiMcpPlaywrightConfig(config),
3181
+ renderGeminiSudoConfig(config),
3182
+ renderGeminiProfileSetup(config),
3183
+ renderEntrypointGeminiNotice(config)
3116
3184
  ].join("\n\n");
3117
3185
  //#endregion
3118
3186
  //#region ../lib/src/core/templates-entrypoint/git.ts
@@ -3680,6 +3748,7 @@ AGENT_ENV_FILE="/run/docker-git/agent-env.sh"
3680
3748
  {
3681
3749
  [[ -f /etc/profile.d/gh-token.sh ]] && cat /etc/profile.d/gh-token.sh
3682
3750
  [[ -f /etc/profile.d/claude-config.sh ]] && cat /etc/profile.d/claude-config.sh
3751
+ [[ -f /etc/profile.d/gemini-config.sh ]] && cat /etc/profile.d/gemini-config.sh
3683
3752
  } > "$AGENT_ENV_FILE" 2>/dev/null || true
3684
3753
  chmod 644 "$AGENT_ENV_FILE"`,
3685
3754
  renderAgentPrompt(),
@@ -3689,7 +3758,7 @@ if [[ -n "$AGENT_PROMPT" ]]; then
3689
3758
  chmod 644 "$AGENT_PROMPT_FILE"
3690
3759
  fi`
3691
3760
  ].join("\n\n");
3692
- var renderAgentPromptCommand = (mode) => mode === "claude" ? String.raw`claude --dangerously-skip-permissions -p \"\$(cat \"$AGENT_PROMPT_FILE\")\"` : String.raw`codex exec \"\$(cat \"$AGENT_PROMPT_FILE\")\"`;
3761
+ var renderAgentPromptCommand = (mode) => Match.value(mode).pipe(Match.when("claude", () => String.raw`claude --dangerously-skip-permissions -p \"\$(cat \"$AGENT_PROMPT_FILE\")\"`), Match.when("codex", () => String.raw`codex exec \"\$(cat \"$AGENT_PROMPT_FILE\")\"`), Match.when("gemini", () => String.raw`gemini --approval-mode=yolo \"\$(cat \"$AGENT_PROMPT_FILE\")\"`), Match.exhaustive);
3693
3762
  var renderAgentAutoLaunchCommand = (config, mode) => String.raw`su - ${config.sshUser} -s /bin/bash -c "bash -lc '. /etc/profile 2>/dev/null || true; . \"$AGENT_ENV_FILE\" 2>/dev/null || true; cd \"$TARGET_DIR\" && ${renderAgentPromptCommand(mode)}'"`;
3694
3763
  var renderAgentModeBlock = (config, mode) => {
3695
3764
  const startMessage = `[agent] starting ${mode}...`;
@@ -3710,6 +3779,7 @@ var renderAgentModeCase = (config) => [
3710
3779
  String.raw`case "$AGENT_MODE" in`,
3711
3780
  indentBlock(renderAgentModeBlock(config, "claude")),
3712
3781
  indentBlock(renderAgentModeBlock(config, "codex")),
3782
+ indentBlock(renderAgentModeBlock(config, "gemini")),
3713
3783
  indentBlock(String.raw`*)
3714
3784
  echo "[agent] unknown agent mode: $AGENT_MODE"
3715
3785
  ;;`),
@@ -4182,7 +4252,9 @@ RUN set -eu; \
4182
4252
  npm install -g oh-my-opencode@latest "oh-my-opencode-linux-\${OH_MY_OPENCODE_ARCH}@latest"
4183
4253
  RUN oh-my-opencode --version
4184
4254
  RUN npm install -g @anthropic-ai/claude-code@latest
4185
- RUN claude --version`;
4255
+ RUN claude --version
4256
+ RUN npm install -g @google/gemini-cli@latest --force
4257
+ RUN gemini --version`;
4186
4258
  var renderDockerfileOpenCode = () => `# Tooling: OpenCode (binary)
4187
4259
  RUN set -eu; \
4188
4260
  for attempt in 1 2 3 4 5; do \
@@ -6494,6 +6566,150 @@ var authCodexStatus = (command) => withCodexAuth(command, ({ accountPath, cwd })
6494
6566
  }));
6495
6567
  var authCodexLogout = (command) => withCodexAuth(command, ({ accountPath, cwd }) => runCodexLogout(cwd, accountPath)).pipe(Effect.zipRight(autoSyncState(`chore(state): auth codex logout ${normalizeAccountLabel(command.label, "default")}`)));
6496
6568
  //#endregion
6569
+ //#region ../lib/src/usecases/auth-gemini-helpers.ts
6570
+ var geminiImageName = "docker-git-auth-gemini:latest";
6571
+ var geminiImageDir = ".docker-git/.orch/auth/gemini/.image";
6572
+ var geminiContainerHomeDir = "/gemini-home";
6573
+ var geminiCredentialsDir$1 = ".gemini";
6574
+ var geminiAuthRoot = ".docker-git/.orch/auth/gemini";
6575
+ var geminiApiKeyFileName = ".api-key";
6576
+ var geminiEnvFileName = ".env";
6577
+ var geminiApiKeyPath = (accountPath) => `${accountPath}/${geminiApiKeyFileName}`;
6578
+ var geminiEnvFilePath = (accountPath) => `${accountPath}/${geminiEnvFileName}`;
6579
+ var geminiCredentialsPath = (accountPath) => `${accountPath}/${geminiCredentialsDir$1}`;
6580
+ var renderGeminiDockerfile = () => String.raw`FROM ubuntu:24.04
6581
+ ENV DEBIAN_FRONTEND=noninteractive
6582
+ RUN apt-get update \
6583
+ && apt-get install -y --no-install-recommends ca-certificates curl bsdutils \
6584
+ && rm -rf /var/lib/apt/lists/*
6585
+ RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
6586
+ && apt-get install -y --no-install-recommends nodejs \
6587
+ && rm -rf /var/lib/apt/lists/*
6588
+ RUN npm install -g @google/gemini-cli@0.33.2
6589
+ `;
6590
+ var ensureGeminiOrchLayout = (cwd) => migrateLegacyOrchLayout(cwd, {
6591
+ envGlobalPath: defaultTemplateConfig.envGlobalPath,
6592
+ envProjectPath: defaultTemplateConfig.envProjectPath,
6593
+ codexAuthPath: defaultTemplateConfig.codexAuthPath,
6594
+ ghAuthPath: ".docker-git/.orch/auth/gh",
6595
+ claudeAuthPath: ".docker-git/.orch/auth/claude",
6596
+ geminiAuthPath: ".docker-git/.orch/auth/gemini"
6597
+ });
6598
+ var resolveGeminiAccountPath = (path, rootPath, label) => {
6599
+ const accountLabel = normalizeAccountLabel(label, "default");
6600
+ return {
6601
+ accountLabel,
6602
+ accountPath: path.join(rootPath, accountLabel)
6603
+ };
6604
+ };
6605
+ var withGeminiAuth = (command, run, options = {}) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
6606
+ yield* _(ensureGeminiOrchLayout(cwd));
6607
+ const { accountLabel, accountPath } = resolveGeminiAccountPath(path, resolvePathFromCwd(path, cwd, command.geminiAuthPath), command.label);
6608
+ yield* _(fs.makeDirectory(accountPath, { recursive: true }));
6609
+ if (options.buildImage === true) yield* _(ensureDockerImage(fs, path, cwd, {
6610
+ imageName: geminiImageName,
6611
+ imageDir: geminiImageDir,
6612
+ dockerfile: renderGeminiDockerfile(),
6613
+ buildLabel: "gemini auth"
6614
+ }));
6615
+ return yield* _(run({
6616
+ accountLabel,
6617
+ accountPath,
6618
+ cwd,
6619
+ fs
6620
+ }));
6621
+ }));
6622
+ var readApiKey = (fs, accountPath) => Effect.gen(function* (_) {
6623
+ const apiKeyFilePath = geminiApiKeyPath(accountPath);
6624
+ if (yield* _(isRegularFile$1(fs, apiKeyFilePath))) {
6625
+ const trimmed = (yield* _(fs.readFileString(apiKeyFilePath), Effect.orElseSucceed(() => ""))).trim();
6626
+ if (trimmed.length > 0) return trimmed;
6627
+ }
6628
+ const envFilePath = geminiEnvFilePath(accountPath);
6629
+ if (yield* _(isRegularFile$1(fs, envFilePath))) {
6630
+ const lines = (yield* _(fs.readFileString(envFilePath), Effect.orElseSucceed(() => ""))).split("\n");
6631
+ for (const line of lines) {
6632
+ const trimmed = line.trim();
6633
+ if (trimmed.startsWith("GEMINI_API_KEY=")) {
6634
+ const value = trimmed.slice(15).replaceAll(/^['"]|['"]$/g, "").trim();
6635
+ if (value.length > 0) return value;
6636
+ }
6637
+ }
6638
+ }
6639
+ return null;
6640
+ });
6641
+ var hasOauthCredentials$1 = (fs, accountPath) => Effect.gen(function* (_) {
6642
+ const credentialsDir = geminiCredentialsPath(accountPath);
6643
+ if (!(yield* _(fs.exists(credentialsDir)))) return false;
6644
+ const possibleFiles = [
6645
+ `${credentialsDir}/oauth-tokens.json`,
6646
+ `${credentialsDir}/credentials.json`,
6647
+ `${credentialsDir}/application_default_credentials.json`
6648
+ ];
6649
+ for (const filePath of possibleFiles) if (yield* _(isRegularFile$1(fs, filePath))) return true;
6650
+ return false;
6651
+ });
6652
+ var resolveGeminiAuthMethod = (fs, accountPath) => Effect.gen(function* (_) {
6653
+ if ((yield* _(readApiKey(fs, accountPath))) !== null) return "api-key";
6654
+ return (yield* _(hasOauthCredentials$1(fs, accountPath))) ? "oauth" : "none";
6655
+ });
6656
+ var prepareGeminiCredentialsDir = (cwd, accountPath, fs) => Effect.gen(function* (_) {
6657
+ const credentialsDir = geminiCredentialsPath(accountPath);
6658
+ const removeFallback = pipe(runCommandExitCode({
6659
+ cwd,
6660
+ command: "docker",
6661
+ args: [
6662
+ "run",
6663
+ "--rm",
6664
+ "-v",
6665
+ `${accountPath}:/target`,
6666
+ "alpine",
6667
+ "rm",
6668
+ "-rf",
6669
+ "/target/.gemini"
6670
+ ]
6671
+ }), Effect.asVoid, Effect.orElse(() => Effect.void));
6672
+ yield* _(fs.remove(credentialsDir, {
6673
+ recursive: true,
6674
+ force: true
6675
+ }).pipe(Effect.orElse(() => removeFallback)));
6676
+ yield* _(fs.makeDirectory(credentialsDir, { recursive: true }));
6677
+ yield* _(runCommandExitCode({
6678
+ cwd,
6679
+ command: "chmod",
6680
+ args: [
6681
+ "-R",
6682
+ "777",
6683
+ credentialsDir
6684
+ ]
6685
+ }).pipe(Effect.orElse(() => Effect.succeed(0))));
6686
+ return credentialsDir;
6687
+ });
6688
+ var writeInitialSettings = (credentialsDir, fs) => Effect.gen(function* (_) {
6689
+ const settingsPath = `${credentialsDir}/settings.json`;
6690
+ yield* _(fs.writeFileString(settingsPath, JSON.stringify({
6691
+ model: {
6692
+ name: "gemini-2.0-flash",
6693
+ compressionThreshold: .9,
6694
+ disableLoopDetection: true
6695
+ },
6696
+ general: { defaultApprovalMode: "auto_edit" },
6697
+ yolo: true,
6698
+ sandbox: { enabled: false },
6699
+ security: {
6700
+ folderTrust: { enabled: false },
6701
+ auth: { selectedType: "oauth-personal" },
6702
+ approvalPolicy: "never"
6703
+ }
6704
+ }, null, 2)));
6705
+ const trustedFoldersPath = `${credentialsDir}/trustedFolders.json`;
6706
+ yield* _(fs.writeFileString(trustedFoldersPath, JSON.stringify({
6707
+ "/": "TRUST_FOLDER",
6708
+ [geminiContainerHomeDir]: "TRUST_FOLDER"
6709
+ })));
6710
+ return settingsPath;
6711
+ });
6712
+ //#endregion
6497
6713
  //#region ../lib/src/usecases/auth-gemini-oauth.ts
6498
6714
  var outputWindowSize = 262144;
6499
6715
  var authSuccessPatterns = [
@@ -6512,22 +6728,29 @@ var authFailurePatterns = [
6512
6728
  ];
6513
6729
  var detectAuthResult = (output) => {
6514
6730
  const normalized = stripAnsi(output).toLowerCase();
6515
- for (const pattern of authSuccessPatterns) if (normalized.includes(pattern.toLowerCase())) return "success";
6516
- for (const pattern of authFailurePatterns) if (normalized.includes(pattern.toLowerCase())) return "failure";
6731
+ const authInitiated = [
6732
+ "please visit the following url",
6733
+ "enter the authorization code",
6734
+ "authorized the application"
6735
+ ].some((m) => normalized.includes(m));
6736
+ if (authSuccessPatterns.some((pattern) => normalized.includes(pattern.toLowerCase()) && (authInitiated || normalized.includes("logged in with google")))) return "success";
6737
+ if (authFailurePatterns.some((pattern) => normalized.includes(pattern.toLowerCase()))) return "failure";
6517
6738
  return "pending";
6518
6739
  };
6519
- var geminiOauthCallbackPort = 38751;
6520
- var buildDockerGeminiAuthSpec = (cwd, accountPath, image, containerPath) => ({
6740
+ var defaultGeminiOauthCallbackPort = 38751;
6741
+ var buildDockerGeminiAuthSpec = (cwd, accountPath, image, containerPath, port) => ({
6521
6742
  cwd,
6522
6743
  image,
6523
6744
  hostPath: accountPath,
6524
6745
  containerPath,
6525
- callbackPort: geminiOauthCallbackPort,
6746
+ callbackPort: port,
6526
6747
  env: [
6527
6748
  `HOME=${containerPath}`,
6528
6749
  "NO_BROWSER=true",
6529
- "GEMINI_CLI_NONINTERACTIVE=false",
6530
- `OAUTH_CALLBACK_PORT=${geminiOauthCallbackPort}`,
6750
+ "GEMINI_CLI_NONINTERACTIVE=true",
6751
+ "GEMINI_CLI_TRUST_ALL=true",
6752
+ "GEMINI_DISABLE_UPDATE_CHECK=true",
6753
+ `OAUTH_CALLBACK_PORT=${port}`,
6531
6754
  "OAUTH_CALLBACK_HOST=0.0.0.0"
6532
6755
  ]
6533
6756
  });
@@ -6535,15 +6758,16 @@ var buildDockerGeminiAuthArgs = (spec) => {
6535
6758
  const base = [
6536
6759
  "run",
6537
6760
  "--rm",
6761
+ "--init",
6538
6762
  "-i",
6539
6763
  "-t",
6540
6764
  "-v",
6541
6765
  `${spec.hostPath}:${spec.containerPath}`,
6542
6766
  "-p",
6543
- `${spec.callbackPort}:${spec.callbackPort}`
6767
+ `${spec.callbackPort}:${spec.callbackPort}`,
6768
+ "-w",
6769
+ spec.containerPath
6544
6770
  ];
6545
- const dockerUser = resolveDefaultDockerUser();
6546
- if (dockerUser !== null) base.push("--user", dockerUser);
6547
6771
  for (const entry of spec.env) {
6548
6772
  const trimmed = entry.trim();
6549
6773
  if (trimmed.length === 0) continue;
@@ -6553,21 +6777,52 @@ var buildDockerGeminiAuthArgs = (spec) => {
6553
6777
  ...base,
6554
6778
  spec.image,
6555
6779
  "gemini",
6780
+ "mcp",
6781
+ "list",
6556
6782
  "--debug"
6557
6783
  ];
6558
6784
  };
6785
+ var cleanupExistingContainers = (port) => Effect.gen(function* (_) {
6786
+ const ids = (yield* _(runCommandCapture({
6787
+ cwd: process.cwd(),
6788
+ command: "docker",
6789
+ args: [
6790
+ "ps",
6791
+ "-q",
6792
+ "--filter",
6793
+ `publish=${port}`
6794
+ ]
6795
+ }, [0], () => /* @__PURE__ */ new Error("docker ps failed")).pipe(Effect.map((value) => value.trim()), Effect.orElseSucceed(() => "")))).split("\n").filter(Boolean);
6796
+ if (ids.length > 0) {
6797
+ yield* _(Effect.logInfo(`Cleaning up existing containers using port ${port}: ${ids.join(", ")}`));
6798
+ yield* _(runCommandExitCode({
6799
+ cwd: process.cwd(),
6800
+ command: "docker",
6801
+ args: [
6802
+ "rm",
6803
+ "-f",
6804
+ ...ids
6805
+ ]
6806
+ }).pipe(Effect.orElse(() => Effect.succeed(0))));
6807
+ }
6808
+ });
6559
6809
  var startDockerProcess = (executor, spec) => executor.start(pipe(Command.make("docker", ...buildDockerGeminiAuthArgs(spec)), Command.workingDirectory(spec.cwd), Command.stdin("inherit"), Command.stdout("pipe"), Command.stderr("pipe")));
6560
- var pumpDockerOutput = (source, fd, resultBox) => {
6810
+ var pumpDockerOutput = (source, fd, resultBox, authDeferred) => {
6561
6811
  const decoder = new TextDecoder("utf-8");
6562
6812
  let outputWindow = "";
6563
- return pipe(source, Stream.runForEach((chunk) => Effect.sync(() => {
6564
- writeChunkToFd(fd, chunk);
6813
+ return pipe(source, Stream.runForEach((chunk) => Effect.gen(function* (_) {
6814
+ yield* _(Effect.sync(() => {
6815
+ writeChunkToFd(fd, chunk);
6816
+ }));
6565
6817
  outputWindow += decoder.decode(chunk);
6566
6818
  if (outputWindow.length > outputWindowSize) outputWindow = outputWindow.slice(-outputWindowSize);
6567
6819
  if (resultBox.value !== "pending") return;
6568
6820
  const result = detectAuthResult(outputWindow);
6569
- if (result !== "pending") resultBox.value = result;
6570
- }).pipe(Effect.asVoid))).pipe(Effect.asVoid);
6821
+ if (result !== "pending") {
6822
+ resultBox.value = result;
6823
+ if (result === "success") yield* _(Deferred.succeed(authDeferred, void 0));
6824
+ }
6825
+ }))).pipe(Effect.asVoid);
6571
6826
  };
6572
6827
  var resolveGeminiLoginResult = (result, exitCode) => Effect.gen(function* (_) {
6573
6828
  if (result === "success") {
@@ -6581,7 +6836,7 @@ var resolveGeminiLoginResult = (result, exitCode) => Effect.gen(function* (_) {
6581
6836
  })));
6582
6837
  });
6583
6838
  var printOauthInstructions = () => Effect.sync(() => {
6584
- const port = geminiOauthCallbackPort;
6839
+ const port = defaultGeminiOauthCallbackPort;
6585
6840
  process.stderr.write("\n");
6586
6841
  process.stderr.write("╔═══════════════════════════════════════════════════════════════════════════╗\n");
6587
6842
  process.stderr.write("║ Gemini CLI OAuth Authentication ║\n");
@@ -6593,108 +6848,41 @@ var printOauthInstructions = () => Effect.sync(() => {
6593
6848
  process.stderr.write("╚═══════════════════════════════════════════════════════════════════════════╝\n");
6594
6849
  process.stderr.write("\n");
6595
6850
  });
6851
+ var fixGeminiAuthPermissions = (hostPath, containerPath) => runCommandExitCode({
6852
+ cwd: process.cwd(),
6853
+ command: "docker",
6854
+ args: [
6855
+ "run",
6856
+ "--rm",
6857
+ "-v",
6858
+ `${hostPath}:${containerPath}`,
6859
+ "alpine",
6860
+ "chmod",
6861
+ "-R",
6862
+ "777",
6863
+ containerPath
6864
+ ]
6865
+ }).pipe(Effect.tapError((err) => Effect.logWarning(`Failed to fix Gemini auth permissions: ${String(err)}`)), Effect.orElse(() => Effect.succeed(0)));
6596
6866
  var runGeminiOauthLoginWithPrompt = (cwd, accountPath, options) => Effect.scoped(Effect.gen(function* (_) {
6867
+ const port = defaultGeminiOauthCallbackPort;
6868
+ yield* _(cleanupExistingContainers(port));
6597
6869
  yield* _(printOauthInstructions());
6598
- const proc = yield* _(startDockerProcess(yield* _(CommandExecutor.CommandExecutor), buildDockerGeminiAuthSpec(cwd, yield* _(resolveDockerVolumeHostPath(cwd, accountPath)), options.image, options.containerPath)));
6870
+ const executor = yield* _(CommandExecutor.CommandExecutor);
6871
+ const hostPath = yield* _(resolveDockerVolumeHostPath(cwd, accountPath));
6872
+ const spec = buildDockerGeminiAuthSpec(cwd, hostPath, options.image, options.containerPath, port);
6873
+ const proc = yield* _(startDockerProcess(executor, spec));
6874
+ const authDeferred = yield* _(Deferred.make());
6599
6875
  const resultBox = { value: "pending" };
6600
- const stdoutFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stdout, 1, resultBox)));
6601
- const stderrFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stderr, 2, resultBox)));
6602
- const exitCode = yield* _(proc.exitCode.pipe(Effect.map(Number)));
6603
- yield* _(Fiber$1.join(stdoutFiber));
6604
- yield* _(Fiber$1.join(stderrFiber));
6876
+ const stdoutFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stdout, 1, resultBox, authDeferred)));
6877
+ const stderrFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stderr, 2, resultBox, authDeferred)));
6878
+ const exitCode = yield* _(Effect.race(proc.exitCode.pipe(Effect.map(Number)), pipe(Deferred.await(authDeferred), Effect.delay("500 millis"), Effect.flatMap(() => proc.kill()), Effect.map(() => 0))));
6879
+ yield* _(Fiber$1.interrupt(stdoutFiber));
6880
+ yield* _(Fiber$1.interrupt(stderrFiber));
6881
+ yield* _(fixGeminiAuthPermissions(hostPath, spec.containerPath));
6605
6882
  return yield* _(resolveGeminiLoginResult(resultBox.value, exitCode));
6606
6883
  }));
6607
6884
  //#endregion
6608
6885
  //#region ../lib/src/usecases/auth-gemini.ts
6609
- var geminiImageName = "docker-git-auth-gemini:latest";
6610
- var geminiImageDir = ".docker-git/.orch/auth/gemini/.image";
6611
- var geminiContainerHomeDir = "/gemini-home";
6612
- var geminiCredentialsDir$1 = ".gemini";
6613
- var geminiAuthRoot = ".docker-git/.orch/auth/gemini";
6614
- var geminiApiKeyFileName = ".api-key";
6615
- var geminiEnvFileName = ".env";
6616
- var geminiApiKeyPath = (accountPath) => `${accountPath}/${geminiApiKeyFileName}`;
6617
- var geminiEnvFilePath = (accountPath) => `${accountPath}/${geminiEnvFileName}`;
6618
- var geminiCredentialsPath = (accountPath) => `${accountPath}/${geminiCredentialsDir$1}`;
6619
- var renderGeminiDockerfile = () => String.raw`FROM ubuntu:24.04
6620
- ENV DEBIAN_FRONTEND=noninteractive
6621
- RUN apt-get update \
6622
- && apt-get install -y --no-install-recommends ca-certificates curl bsdutils \
6623
- && rm -rf /var/lib/apt/lists/*
6624
- RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
6625
- && apt-get install -y --no-install-recommends nodejs \
6626
- && node -v \
6627
- && npm -v \
6628
- && rm -rf /var/lib/apt/lists/*
6629
- RUN npm install -g @google/gemini-cli@latest
6630
- ENTRYPOINT ["/bin/bash", "-c"]
6631
- `;
6632
- var ensureGeminiOrchLayout = (cwd) => migrateLegacyOrchLayout(cwd, {
6633
- envGlobalPath: defaultTemplateConfig.envGlobalPath,
6634
- envProjectPath: defaultTemplateConfig.envProjectPath,
6635
- codexAuthPath: defaultTemplateConfig.codexAuthPath,
6636
- ghAuthPath: ".docker-git/.orch/auth/gh",
6637
- claudeAuthPath: ".docker-git/.orch/auth/claude",
6638
- geminiAuthPath: ".docker-git/.orch/auth/gemini"
6639
- });
6640
- var resolveGeminiAccountPath = (path, rootPath, label) => {
6641
- const accountLabel = normalizeAccountLabel(label, "default");
6642
- return {
6643
- accountLabel,
6644
- accountPath: path.join(rootPath, accountLabel)
6645
- };
6646
- };
6647
- var withGeminiAuth = (command, run, options = {}) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
6648
- yield* _(ensureGeminiOrchLayout(cwd));
6649
- const { accountLabel, accountPath } = resolveGeminiAccountPath(path, resolvePathFromCwd(path, cwd, command.geminiAuthPath), command.label);
6650
- yield* _(fs.makeDirectory(accountPath, { recursive: true }));
6651
- if (options.buildImage === true) yield* _(ensureDockerImage(fs, path, cwd, {
6652
- imageName: geminiImageName,
6653
- imageDir: geminiImageDir,
6654
- dockerfile: renderGeminiDockerfile(),
6655
- buildLabel: "gemini auth"
6656
- }));
6657
- return yield* _(run({
6658
- accountLabel,
6659
- accountPath,
6660
- cwd,
6661
- fs
6662
- }));
6663
- }));
6664
- var readApiKey = (fs, accountPath) => Effect.gen(function* (_) {
6665
- const apiKeyFilePath = geminiApiKeyPath(accountPath);
6666
- if (yield* _(isRegularFile$1(fs, apiKeyFilePath))) {
6667
- const trimmed = (yield* _(fs.readFileString(apiKeyFilePath), Effect.orElseSucceed(() => ""))).trim();
6668
- if (trimmed.length > 0) return trimmed;
6669
- }
6670
- const envFilePath = geminiEnvFilePath(accountPath);
6671
- if (yield* _(isRegularFile$1(fs, envFilePath))) {
6672
- const lines = (yield* _(fs.readFileString(envFilePath), Effect.orElseSucceed(() => ""))).split("\n");
6673
- for (const line of lines) {
6674
- const trimmed = line.trim();
6675
- if (trimmed.startsWith("GEMINI_API_KEY=")) {
6676
- const value = trimmed.slice(15).replaceAll(/^['"]|['"]$/g, "").trim();
6677
- if (value.length > 0) return value;
6678
- }
6679
- }
6680
- }
6681
- return null;
6682
- });
6683
- var hasOauthCredentials$1 = (fs, accountPath) => Effect.gen(function* (_) {
6684
- const credentialsDir = geminiCredentialsPath(accountPath);
6685
- if (!(yield* _(fs.exists(credentialsDir)))) return false;
6686
- const possibleFiles = [
6687
- `${credentialsDir}/oauth-tokens.json`,
6688
- `${credentialsDir}/credentials.json`,
6689
- `${credentialsDir}/application_default_credentials.json`
6690
- ];
6691
- for (const filePath of possibleFiles) if (yield* _(isRegularFile$1(fs, filePath))) return true;
6692
- return false;
6693
- });
6694
- var resolveGeminiAuthMethod = (fs, accountPath) => Effect.gen(function* (_) {
6695
- if ((yield* _(readApiKey(fs, accountPath))) !== null) return "api-key";
6696
- return (yield* _(hasOauthCredentials$1(fs, accountPath))) ? "oauth" : "none";
6697
- });
6698
6886
  var authGeminiLogin = (command, apiKey) => {
6699
6887
  const accountLabel = normalizeAccountLabel(command.label, "default");
6700
6888
  return withGeminiAuth(command, ({ accountPath, fs }) => Effect.gen(function* (_) {
@@ -6718,12 +6906,16 @@ var authGeminiLoginCli = (_command) => Effect.gen(function* (_) {
6718
6906
  var authGeminiLoginOauth = (command) => {
6719
6907
  const accountLabel = normalizeAccountLabel(command.label, "default");
6720
6908
  return withGeminiAuth(command, ({ accountPath, cwd, fs }) => Effect.gen(function* (_) {
6721
- const credentialsDir = geminiCredentialsPath(accountPath);
6722
- yield* _(fs.makeDirectory(credentialsDir, { recursive: true }));
6909
+ const settingsPath = yield* _(writeInitialSettings(yield* _(prepareGeminiCredentialsDir(cwd, accountPath, fs)), fs));
6723
6910
  yield* _(runGeminiOauthLoginWithPrompt(cwd, accountPath, {
6724
6911
  image: geminiImageName,
6725
6912
  containerPath: geminiContainerHomeDir
6726
6913
  }));
6914
+ yield* _(fs.writeFileString(settingsPath, JSON.stringify({ security: {
6915
+ folderTrust: { enabled: false },
6916
+ auth: { selectedType: "oauth-personal" },
6917
+ approvalPolicy: "never"
6918
+ } }, null, 2) + "\n"));
6727
6919
  }), { buildImage: true }).pipe(Effect.zipRight(autoSyncState(`chore(state): auth gemini oauth ${accountLabel}`)));
6728
6920
  };
6729
6921
  var authGeminiStatus = (command) => withGeminiAuth(command, ({ accountLabel, accountPath, fs }) => Effect.gen(function* (_) {
@@ -8098,7 +8290,8 @@ var buildClaudeCommand = (action, options) => Match.value(action).pipe(Match.whe
8098
8290
  var buildGeminiCommand = (action, options) => Match.value(action).pipe(Match.when("login", () => Either.right({
8099
8291
  _tag: "AuthGeminiLogin",
8100
8292
  label: options.label,
8101
- geminiAuthPath: options.geminiAuthPath
8293
+ geminiAuthPath: options.geminiAuthPath,
8294
+ isWeb: options.authWeb
8102
8295
  })), Match.when("status", () => Either.right({
8103
8296
  _tag: "AuthGeminiStatus",
8104
8297
  label: options.label,
@@ -9588,12 +9781,14 @@ var resolveClaudeLogoutEffect = (labelOption) => authClaudeLogout({
9588
9781
  var resolveGeminiOauthEffect = (labelOption) => authGeminiLoginOauth({
9589
9782
  _tag: "AuthGeminiLogin",
9590
9783
  label: labelOption,
9591
- geminiAuthPath: geminiAuthRoot
9784
+ geminiAuthPath: geminiAuthRoot,
9785
+ isWeb: false
9592
9786
  });
9593
9787
  var resolveGeminiApiKeyEffect = (labelOption, apiKey) => authGeminiLogin({
9594
9788
  _tag: "AuthGeminiLogin",
9595
9789
  label: labelOption,
9596
- geminiAuthPath: geminiAuthRoot
9790
+ geminiAuthPath: geminiAuthRoot,
9791
+ isWeb: false
9597
9792
  }, apiKey);
9598
9793
  var resolveGeminiLogoutEffect = (labelOption) => authGeminiLogout({
9599
9794
  _tag: "AuthGeminiLogout",
@@ -11378,7 +11573,7 @@ var setExitCode = (code) => Effect.sync(() => {
11378
11573
  });
11379
11574
  var logWarningAndExit = (error) => pipe(Effect.logWarning(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
11380
11575
  var logErrorAndExit = (error) => pipe(Effect.logError(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
11381
- var handleNonBaseCommand = (command) => Match.value(command).pipe(Match.when({ _tag: "StatePath" }, () => statePath), Match.when({ _tag: "StateInit" }, (cmd) => stateInit(cmd)), Match.when({ _tag: "StateStatus" }, () => stateStatus), Match.when({ _tag: "StatePull" }, () => statePull), Match.when({ _tag: "StateCommit" }, (cmd) => stateCommit(cmd.message)), Match.when({ _tag: "StatePush" }, () => statePush), Match.when({ _tag: "StateSync" }, (cmd) => stateSync(cmd.message)), Match.when({ _tag: "AuthGithubLogin" }, (cmd) => authGithubLogin(cmd)), Match.when({ _tag: "AuthGithubStatus" }, (cmd) => authGithubStatus(cmd)), Match.when({ _tag: "AuthGithubLogout" }, (cmd) => authGithubLogout(cmd)), Match.when({ _tag: "AuthCodexLogin" }, (cmd) => authCodexLogin(cmd)), Match.when({ _tag: "AuthCodexStatus" }, (cmd) => authCodexStatus(cmd)), Match.when({ _tag: "AuthCodexLogout" }, (cmd) => authCodexLogout(cmd)), Match.when({ _tag: "AuthClaudeLogin" }, (cmd) => authClaudeLogin(cmd)), Match.when({ _tag: "AuthClaudeStatus" }, (cmd) => authClaudeStatus(cmd)), Match.when({ _tag: "AuthClaudeLogout" }, (cmd) => authClaudeLogout(cmd)), Match.when({ _tag: "Attach" }, (cmd) => attachTmux(cmd)), Match.when({ _tag: "Panes" }, (cmd) => listTmuxPanes(cmd)), Match.when({ _tag: "SessionsList" }, (cmd) => listTerminalSessions(cmd))).pipe(Match.when({ _tag: "AuthGeminiLogin" }, (cmd) => authGeminiLoginCli(cmd)), Match.when({ _tag: "AuthGeminiStatus" }, (cmd) => authGeminiStatus(cmd)), Match.when({ _tag: "AuthGeminiLogout" }, (cmd) => authGeminiLogout(cmd)), Match.when({ _tag: "SessionsKill" }, (cmd) => killTerminalProcess(cmd)), Match.when({ _tag: "Apply" }, (cmd) => applyProjectConfig(cmd)), Match.when({ _tag: "SessionsLogs" }, (cmd) => tailTerminalLogs(cmd)), Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)), Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd)), Match.when({ _tag: "McpPlaywrightUp" }, (cmd) => mcpPlaywrightUp(cmd)), Match.exhaustive);
11576
+ var handleNonBaseCommand = (command) => Match.value(command).pipe(Match.when({ _tag: "StatePath" }, () => statePath), Match.when({ _tag: "StateInit" }, (cmd) => stateInit(cmd)), Match.when({ _tag: "StateStatus" }, () => stateStatus), Match.when({ _tag: "StatePull" }, () => statePull), Match.when({ _tag: "StateCommit" }, (cmd) => stateCommit(cmd.message)), Match.when({ _tag: "StatePush" }, () => statePush), Match.when({ _tag: "StateSync" }, (cmd) => stateSync(cmd.message)), Match.when({ _tag: "AuthGithubLogin" }, (cmd) => authGithubLogin(cmd)), Match.when({ _tag: "AuthGithubStatus" }, (cmd) => authGithubStatus(cmd)), Match.when({ _tag: "AuthGithubLogout" }, (cmd) => authGithubLogout(cmd)), Match.when({ _tag: "AuthCodexLogin" }, (cmd) => authCodexLogin(cmd)), Match.when({ _tag: "AuthCodexStatus" }, (cmd) => authCodexStatus(cmd)), Match.when({ _tag: "AuthCodexLogout" }, (cmd) => authCodexLogout(cmd)), Match.when({ _tag: "AuthClaudeLogin" }, (cmd) => authClaudeLogin(cmd)), Match.when({ _tag: "AuthClaudeStatus" }, (cmd) => authClaudeStatus(cmd)), Match.when({ _tag: "AuthClaudeLogout" }, (cmd) => authClaudeLogout(cmd)), Match.when({ _tag: "Attach" }, (cmd) => attachTmux(cmd)), Match.when({ _tag: "Panes" }, (cmd) => listTmuxPanes(cmd)), Match.when({ _tag: "SessionsList" }, (cmd) => listTerminalSessions(cmd))).pipe(Match.when({ _tag: "AuthGeminiLogin" }, (cmd) => cmd.isWeb ? authGeminiLoginOauth(cmd) : authGeminiLoginCli(cmd)), Match.when({ _tag: "AuthGeminiStatus" }, (cmd) => authGeminiStatus(cmd)), Match.when({ _tag: "AuthGeminiLogout" }, (cmd) => authGeminiLogout(cmd)), Match.when({ _tag: "SessionsKill" }, (cmd) => killTerminalProcess(cmd)), Match.when({ _tag: "Apply" }, (cmd) => applyProjectConfig(cmd)), Match.when({ _tag: "SessionsLogs" }, (cmd) => tailTerminalLogs(cmd)), Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)), Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd)), Match.when({ _tag: "McpPlaywrightUp" }, (cmd) => mcpPlaywrightUp(cmd)), Match.exhaustive);
11382
11577
  var program = pipe(readCommand, Effect.flatMap((command) => Match.value(command).pipe(Match.when({ _tag: "Help" }, ({ message }) => Effect.log(message)), Match.when({ _tag: "Create" }, (create) => createProject(create)), Match.when({ _tag: "Status" }, () => listProjectStatus), Match.when({ _tag: "DownAll" }, () => downAllDockerGitProjects), Match.when({ _tag: "Menu" }, () => runMenu), Match.orElse((cmd) => handleNonBaseCommand(cmd)))), Effect.catchTag("FileExistsError", (error) => pipe(Effect.logWarning(renderError(error)), Effect.asVoid)), Effect.catchTag("DockerAccessError", logWarningAndExit), Effect.catchTag("DockerCommandError", logWarningAndExit), Effect.catchTag("AuthError", logWarningAndExit), Effect.catchTag("AgentFailedError", logWarningAndExit), Effect.catchTag("CommandFailedError", logWarningAndExit), Effect.catchTag("ScrapArchiveNotFoundError", logErrorAndExit), Effect.catchTag("ScrapTargetDirUnsupportedError", logErrorAndExit), Effect.catchTag("ScrapWipeRefusedError", logErrorAndExit), Effect.matchEffect({
11383
11578
  onFailure: (error) => isParseError(error) ? logErrorAndExit(error) : pipe(Effect.logError(renderError(error)), Effect.flatMap(() => Effect.fail(error))),
11384
11579
  onSuccess: () => Effect.void