@prover-coder-ai/docker-git 1.0.44 → 1.0.45
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.
- package/dist/src/docker-git/main.js +279 -111
- package/dist/src/docker-git/main.js.map +1 -1
- package/package.json +1 -1
|
@@ -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";
|
|
@@ -2178,6 +2178,7 @@ elif [[ "$TARGET_DIR" == "~/"* ]]; then
|
|
|
2178
2178
|
fi
|
|
2179
2179
|
CLAUDE_AUTH_LABEL="\${CLAUDE_AUTH_LABEL:-}"
|
|
2180
2180
|
CODEX_AUTH_LABEL="\${CODEX_AUTH_LABEL:-}"
|
|
2181
|
+
GEMINI_AUTH_LABEL="\${GEMINI_AUTH_LABEL:-}"
|
|
2181
2182
|
GIT_AUTH_USER="\${GIT_AUTH_USER:-\${GITHUB_USER:-x-access-token}}"
|
|
2182
2183
|
GIT_AUTH_TOKEN="\${GIT_AUTH_TOKEN:-\${GITHUB_TOKEN:-\${GH_TOKEN:-}}}"
|
|
2183
2184
|
GH_TOKEN="\${GH_TOKEN:-\${GIT_AUTH_TOKEN:-}}"
|
|
@@ -2980,8 +2981,8 @@ var renderEntrypointAgentsNotice = (config) => entrypointAgentsNoticeTemplate.re
|
|
|
2980
2981
|
//#endregion
|
|
2981
2982
|
//#region ../lib/src/core/templates-entrypoint/gemini.ts
|
|
2982
2983
|
var geminiAuthRootContainerPath = (sshUser) => `/home/${sshUser}/.docker-git/.orch/auth/gemini`;
|
|
2983
|
-
var geminiAuthConfigTemplate = String.raw`# Gemini CLI: expose
|
|
2984
|
-
GEMINI_LABEL_RAW="$
|
|
2984
|
+
var geminiAuthConfigTemplate = String.raw`# Gemini CLI: expose GEMINI_HOME for sessions (OAuth cache lives under ~/.docker-git/.orch/auth/gemini)
|
|
2985
|
+
GEMINI_LABEL_RAW="$GEMINI_AUTH_LABEL"
|
|
2985
2986
|
if [[ -z "$GEMINI_LABEL_RAW" ]]; then
|
|
2986
2987
|
GEMINI_LABEL_RAW="default"
|
|
2987
2988
|
fi
|
|
@@ -2994,29 +2995,16 @@ if [[ -z "$GEMINI_LABEL_NORM" ]]; then
|
|
|
2994
2995
|
fi
|
|
2995
2996
|
|
|
2996
2997
|
GEMINI_AUTH_ROOT="__GEMINI_AUTH_ROOT__"
|
|
2997
|
-
|
|
2998
|
+
export GEMINI_CONFIG_DIR="$GEMINI_AUTH_ROOT/$GEMINI_LABEL_NORM"
|
|
2998
2999
|
|
|
2999
|
-
|
|
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
|
|
3000
|
+
mkdir -p "$GEMINI_CONFIG_DIR" || true
|
|
3008
3001
|
GEMINI_HOME_DIR="__GEMINI_HOME_DIR__"
|
|
3009
3002
|
mkdir -p "$GEMINI_HOME_DIR" || true
|
|
3010
3003
|
|
|
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
3004
|
docker_git_link_gemini_file() {
|
|
3016
3005
|
local source_path="$1"
|
|
3017
3006
|
local link_path="$2"
|
|
3018
3007
|
|
|
3019
|
-
# Preserve user-created regular files and seed config dir once.
|
|
3020
3008
|
if [[ -e "$link_path" && ! -L "$link_path" ]]; then
|
|
3021
3009
|
if [[ -f "$link_path" && ! -e "$source_path" ]]; then
|
|
3022
3010
|
cp "$link_path" "$source_path" || true
|
|
@@ -3028,91 +3016,170 @@ docker_git_link_gemini_file() {
|
|
|
3028
3016
|
ln -sfn "$source_path" "$link_path" || true
|
|
3029
3017
|
}
|
|
3030
3018
|
|
|
3031
|
-
# Link
|
|
3032
|
-
docker_git_link_gemini_file "$
|
|
3019
|
+
# Link .api-key and .env from central auth storage to container home
|
|
3020
|
+
docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.api-key" "$GEMINI_HOME_DIR/.api-key"
|
|
3021
|
+
docker_git_link_gemini_file "$GEMINI_CONFIG_DIR/.env" "$GEMINI_HOME_DIR/.env"
|
|
3033
3022
|
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
unset GEMINI_API_KEY || true
|
|
3023
|
+
# Ensure gemini YOLO wrapper exists
|
|
3024
|
+
GEMINI_REAL_BIN="$(command -v gemini || echo "/usr/local/bin/gemini")"
|
|
3025
|
+
GEMINI_WRAPPER_BIN="/usr/local/bin/gemini-wrapper"
|
|
3026
|
+
if [[ -f "$GEMINI_REAL_BIN" && "$GEMINI_REAL_BIN" != "$GEMINI_WRAPPER_BIN" ]]; then
|
|
3027
|
+
if [[ ! -f "$GEMINI_WRAPPER_BIN" ]]; then
|
|
3028
|
+
cat <<'EOF' > "$GEMINI_WRAPPER_BIN"
|
|
3029
|
+
#!/usr/bin/env bash
|
|
3030
|
+
GEMINI_ORIGINAL_BIN="__GEMINI_REAL_BIN__"
|
|
3031
|
+
exec "$GEMINI_ORIGINAL_BIN" --yolo "$@"
|
|
3032
|
+
EOF
|
|
3033
|
+
sed -i "s#__GEMINI_REAL_BIN__#$GEMINI_REAL_BIN#g" "$GEMINI_WRAPPER_BIN" || true
|
|
3034
|
+
chmod 0755 "$GEMINI_WRAPPER_BIN" || true
|
|
3035
|
+
# Create an alias or symlink if needed, but here we just ensure it exists
|
|
3048
3036
|
fi
|
|
3049
|
-
|
|
3037
|
+
fi
|
|
3050
3038
|
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3039
|
+
# Special case for .gemini folder: we want the folder itself to be the link if it doesn't exist
|
|
3040
|
+
# or its content to be linked if we want to manage it.
|
|
3041
|
+
if [[ -d "$GEMINI_CONFIG_DIR/.gemini" ]]; then
|
|
3042
|
+
if [[ -L "$GEMINI_HOME_DIR" ]]; then
|
|
3043
|
+
rm -f "$GEMINI_HOME_DIR"
|
|
3044
|
+
elif [[ -d "$GEMINI_HOME_DIR" ]]; then
|
|
3045
|
+
# If it's a real directory, move it aside if it's empty or just has our managed files
|
|
3046
|
+
mv "$GEMINI_HOME_DIR" "$GEMINI_HOME_DIR.bak-$(date +%s)" || true
|
|
3057
3047
|
fi
|
|
3048
|
+
ln -sfn "$GEMINI_CONFIG_DIR/.gemini" "$GEMINI_HOME_DIR"
|
|
3049
|
+
fi
|
|
3058
3050
|
|
|
3059
|
-
|
|
3060
|
-
|
|
3051
|
+
docker_git_refresh_gemini_env() {
|
|
3052
|
+
# If .api-key exists, export it as GEMINI_API_KEY
|
|
3053
|
+
if [[ -f "$GEMINI_HOME_DIR/.api-key" ]]; then
|
|
3054
|
+
export GEMINI_API_KEY="$(cat "$GEMINI_HOME_DIR/.api-key" | tr -d '\r\n')"
|
|
3055
|
+
elif [[ -f "$GEMINI_HOME_DIR/.env" ]]; then
|
|
3056
|
+
# Parse GEMINI_API_KEY from .env
|
|
3057
|
+
API_KEY="$(grep "^GEMINI_API_KEY=" "$GEMINI_HOME_DIR/.env" | cut -d'=' -f2- | sed "s/^['\"]//;s/['\"]$//")"
|
|
3058
|
+
if [[ -n "$API_KEY" ]]; then
|
|
3059
|
+
export GEMINI_API_KEY="$API_KEY"
|
|
3060
|
+
fi
|
|
3061
3061
|
fi
|
|
3062
|
+
}
|
|
3062
3063
|
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3064
|
+
docker_git_refresh_gemini_env`;
|
|
3065
|
+
var renderGeminiAuthConfig = (config) => geminiAuthConfigTemplate.replaceAll("__GEMINI_AUTH_ROOT__", geminiAuthRootContainerPath(config.sshUser)).replaceAll("__GEMINI_HOME_DIR__", config.geminiHome);
|
|
3066
|
+
var renderGeminiPermissionSettingsConfig = (config) => String.raw`# Gemini CLI: keep trust settings in sync with docker-git defaults
|
|
3067
|
+
GEMINI_SETTINGS_DIR="${config.geminiHome}"
|
|
3068
|
+
GEMINI_TRUST_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/trustedFolders.json"
|
|
3069
|
+
GEMINI_CONFIG_SETTINGS_FILE="$GEMINI_SETTINGS_DIR/settings.json"
|
|
3069
3070
|
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
#!/usr/bin/env bash
|
|
3073
|
-
set -euo pipefail
|
|
3071
|
+
# Wait for symlink to be established by the auth config step
|
|
3072
|
+
mkdir -p "$GEMINI_SETTINGS_DIR" || true
|
|
3074
3073
|
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3074
|
+
# Disable folder trust prompt and enable auto-approval in settings.json
|
|
3075
|
+
if [[ ! -f "$GEMINI_CONFIG_SETTINGS_FILE" ]]; then
|
|
3076
|
+
cat <<'EOF' > "$GEMINI_CONFIG_SETTINGS_FILE"
|
|
3077
|
+
{
|
|
3078
|
+
"security": {
|
|
3079
|
+
"folderTrust": {
|
|
3080
|
+
"enabled": false
|
|
3081
|
+
},
|
|
3082
|
+
"approvalPolicy": "never"
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
EOF
|
|
3085
3086
|
fi
|
|
3086
3087
|
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3088
|
+
# Pre-trust important directories in trustedFolders.json
|
|
3089
|
+
# Use flat mapping as required by recent Gemini CLI versions
|
|
3090
|
+
cat <<'EOF' > "$GEMINI_TRUST_SETTINGS_FILE"
|
|
3091
|
+
{
|
|
3092
|
+
"/": "TRUST_FOLDER",
|
|
3093
|
+
"${config.geminiHome}": "TRUST_FOLDER",
|
|
3094
|
+
"${config.targetDir}": "TRUST_FOLDER"
|
|
3091
3095
|
}
|
|
3096
|
+
EOF
|
|
3092
3097
|
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3098
|
+
chown -R 1000:1000 "$GEMINI_SETTINGS_DIR" || true
|
|
3099
|
+
chmod 0600 "$GEMINI_TRUST_SETTINGS_FILE" "$GEMINI_CONFIG_SETTINGS_FILE" 2>/dev/null || true`;
|
|
3100
|
+
var renderGeminiSudoConfig = (config) => String.raw`# Gemini CLI: allow passwordless sudo for agent tasks
|
|
3101
|
+
if [[ -d /etc/sudoers.d ]]; then
|
|
3102
|
+
echo "${config.sshUser} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/gemini-agent
|
|
3103
|
+
chmod 0440 /etc/sudoers.d/gemini-agent
|
|
3104
|
+
fi`;
|
|
3105
|
+
var renderGeminiMcpPlaywrightConfig = (_config) => String.raw`# Gemini CLI: keep Playwright MCP config in sync (TODO: Gemini CLI MCP integration format)
|
|
3106
|
+
# For now, Gemini CLI uses MCP via ~/.gemini/settings.json or command line.
|
|
3107
|
+
# We'll ensure it has the same Playwright capability as Claude/Codex once format is confirmed.`;
|
|
3108
|
+
var renderGeminiProfileSetup = (config) => String.raw`GEMINI_PROFILE="/etc/profile.d/gemini-config.sh"
|
|
3109
|
+
printf "export GEMINI_AUTH_LABEL=%q\n" "$GEMINI_AUTH_LABEL" > "$GEMINI_PROFILE"
|
|
3110
|
+
printf "export GEMINI_HOME=%q\n" "${config.geminiHome}" >> "$GEMINI_PROFILE"
|
|
3111
|
+
printf "export GEMINI_CLI_DISABLE_UPDATE_CHECK=true\n" >> "$GEMINI_PROFILE"
|
|
3112
|
+
printf "export GEMINI_CLI_NONINTERACTIVE=true\n" >> "$GEMINI_PROFILE"
|
|
3113
|
+
printf "export GEMINI_CLI_APPROVAL_MODE=yolo\n" >> "$GEMINI_PROFILE"
|
|
3114
|
+
printf "alias gemini='/usr/local/bin/gemini-wrapper'\n" >> "$GEMINI_PROFILE"
|
|
3096
3115
|
cat <<'EOF' >> "$GEMINI_PROFILE"
|
|
3097
|
-
|
|
3098
|
-
|
|
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
|
|
3116
|
+
if [[ -f "$GEMINI_HOME/.api-key" ]]; then
|
|
3117
|
+
export GEMINI_API_KEY="$(cat "$GEMINI_HOME/.api-key" | tr -d '\r\n')"
|
|
3106
3118
|
fi
|
|
3107
3119
|
EOF
|
|
3108
3120
|
chmod 0644 "$GEMINI_PROFILE" || true
|
|
3109
3121
|
|
|
3110
|
-
docker_git_upsert_ssh_env "GEMINI_AUTH_LABEL" "$
|
|
3111
|
-
docker_git_upsert_ssh_env "GEMINI_API_KEY" "
|
|
3122
|
+
docker_git_upsert_ssh_env "GEMINI_AUTH_LABEL" "$GEMINI_AUTH_LABEL"
|
|
3123
|
+
docker_git_upsert_ssh_env "GEMINI_API_KEY" "\${GEMINI_API_KEY:-}"
|
|
3124
|
+
docker_git_upsert_ssh_env "GEMINI_CLI_DISABLE_UPDATE_CHECK" "true"
|
|
3125
|
+
docker_git_upsert_ssh_env "GEMINI_CLI_NONINTERACTIVE" "true"
|
|
3126
|
+
docker_git_upsert_ssh_env "GEMINI_CLI_APPROVAL_MODE" "yolo"`;
|
|
3127
|
+
var entrypointGeminiNoticeTemplate = String.raw`# Ensure global GEMINI.md exists for container context
|
|
3128
|
+
GEMINI_MD_PATH="__GEMINI_HOME__/GEMINI.md"
|
|
3129
|
+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: repository"
|
|
3130
|
+
if [[ "$REPO_REF" == issue-* ]]; then
|
|
3131
|
+
ISSUE_ID="$(printf "%s" "$REPO_REF" | sed -E 's#^issue-##')"
|
|
3132
|
+
ISSUE_URL=""
|
|
3133
|
+
if [[ "$REPO_URL" == https://github.com/* ]]; then
|
|
3134
|
+
ISSUE_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')"
|
|
3135
|
+
if [[ -n "$ISSUE_REPO" ]]; then
|
|
3136
|
+
ISSUE_URL="https://github.com/$ISSUE_REPO/issues/$ISSUE_ID"
|
|
3137
|
+
fi
|
|
3138
|
+
fi
|
|
3139
|
+
if [[ -n "$ISSUE_URL" ]]; then
|
|
3140
|
+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID ($ISSUE_URL)"
|
|
3141
|
+
else
|
|
3142
|
+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: issue #$ISSUE_ID"
|
|
3143
|
+
fi
|
|
3144
|
+
elif [[ "$REPO_REF" == refs/pull/*/head ]]; then
|
|
3145
|
+
PR_ID="$(printf "%s" "$REPO_REF" | sed -nE 's#^refs/pull/([0-9]+)/head$#\1#p')"
|
|
3146
|
+
PR_URL=""
|
|
3147
|
+
if [[ "$REPO_URL" == https://github.com/* && -n "$PR_ID" ]]; then
|
|
3148
|
+
PR_REPO="$(printf "%s" "$REPO_URL" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')"
|
|
3149
|
+
if [[ -n "$PR_REPO" ]]; then
|
|
3150
|
+
PR_URL="https://github.com/$PR_REPO/pull/$PR_ID"
|
|
3151
|
+
fi
|
|
3152
|
+
fi
|
|
3153
|
+
if [[ -n "$PR_ID" && -n "$PR_URL" ]]; then
|
|
3154
|
+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID ($PR_URL)"
|
|
3155
|
+
elif [[ -n "$PR_ID" ]]; then
|
|
3156
|
+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: PR #$PR_ID"
|
|
3157
|
+
else
|
|
3158
|
+
GEMINI_WORKSPACE_CONTEXT="Контекст workspace: pull request ($REPO_REF)"
|
|
3159
|
+
fi
|
|
3160
|
+
fi
|
|
3161
|
+
|
|
3162
|
+
cat <<EOF > "$GEMINI_MD_PATH"
|
|
3163
|
+
<!-- docker-git-managed:gemini-md -->
|
|
3164
|
+
Ты автономный агент, который имеет полностью все права управления контейнером. У тебя есть доступ к командам sudo, gh, codex, gemini, claude, opencode, oh-my-opencode, sshpass, git, node, pnpm и всем остальным другим. Проекты с которыми идёт работа лежат по пути ~
|
|
3165
|
+
Рабочая папка проекта (git clone): __TARGET_DIR__
|
|
3166
|
+
Доступные workspace пути: __TARGET_DIR__
|
|
3167
|
+
$GEMINI_WORKSPACE_CONTEXT
|
|
3168
|
+
Фокус задачи: работай только в workspace, который запрашивает пользователь. Текущий workspace: __TARGET_DIR__
|
|
3169
|
+
Доступ к интернету: есть. Если чего-то не знаешь — ищи в интернете или по кодовой базе.
|
|
3170
|
+
Для решения задач обязательно используй subagents. Сам агент обязан выполнять финальную проверку, интеграцию и валидацию результата перед ответом пользователю.
|
|
3171
|
+
Если ты видишь файлы AGENTS.md, GEMINI.md или CLAUDE.md внутри проекта, ты обязан их читать и соблюдать инструкции.
|
|
3172
|
+
<!-- /docker-git-managed:gemini-md -->
|
|
3173
|
+
EOF
|
|
3174
|
+
chown 1000:1000 "$GEMINI_MD_PATH" || true`;
|
|
3175
|
+
var renderEntrypointGeminiNotice = (config) => entrypointGeminiNoticeTemplate.replaceAll("__GEMINI_HOME__", config.geminiHome).replaceAll("__TARGET_DIR__", config.targetDir);
|
|
3112
3176
|
var renderEntrypointGeminiConfig = (config) => [
|
|
3113
3177
|
renderGeminiAuthConfig(config),
|
|
3114
|
-
|
|
3115
|
-
|
|
3178
|
+
renderGeminiPermissionSettingsConfig(config),
|
|
3179
|
+
renderGeminiMcpPlaywrightConfig(config),
|
|
3180
|
+
renderGeminiSudoConfig(config),
|
|
3181
|
+
renderGeminiProfileSetup(config),
|
|
3182
|
+
renderEntrypointGeminiNotice(config)
|
|
3116
3183
|
].join("\n\n");
|
|
3117
3184
|
//#endregion
|
|
3118
3185
|
//#region ../lib/src/core/templates-entrypoint/git.ts
|
|
@@ -3680,6 +3747,7 @@ AGENT_ENV_FILE="/run/docker-git/agent-env.sh"
|
|
|
3680
3747
|
{
|
|
3681
3748
|
[[ -f /etc/profile.d/gh-token.sh ]] && cat /etc/profile.d/gh-token.sh
|
|
3682
3749
|
[[ -f /etc/profile.d/claude-config.sh ]] && cat /etc/profile.d/claude-config.sh
|
|
3750
|
+
[[ -f /etc/profile.d/gemini-config.sh ]] && cat /etc/profile.d/gemini-config.sh
|
|
3683
3751
|
} > "$AGENT_ENV_FILE" 2>/dev/null || true
|
|
3684
3752
|
chmod 644 "$AGENT_ENV_FILE"`,
|
|
3685
3753
|
renderAgentPrompt(),
|
|
@@ -3689,7 +3757,7 @@ if [[ -n "$AGENT_PROMPT" ]]; then
|
|
|
3689
3757
|
chmod 644 "$AGENT_PROMPT_FILE"
|
|
3690
3758
|
fi`
|
|
3691
3759
|
].join("\n\n");
|
|
3692
|
-
var renderAgentPromptCommand = (mode) => mode
|
|
3760
|
+
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
3761
|
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
3762
|
var renderAgentModeBlock = (config, mode) => {
|
|
3695
3763
|
const startMessage = `[agent] starting ${mode}...`;
|
|
@@ -3710,6 +3778,7 @@ var renderAgentModeCase = (config) => [
|
|
|
3710
3778
|
String.raw`case "$AGENT_MODE" in`,
|
|
3711
3779
|
indentBlock(renderAgentModeBlock(config, "claude")),
|
|
3712
3780
|
indentBlock(renderAgentModeBlock(config, "codex")),
|
|
3781
|
+
indentBlock(renderAgentModeBlock(config, "gemini")),
|
|
3713
3782
|
indentBlock(String.raw`*)
|
|
3714
3783
|
echo "[agent] unknown agent mode: $AGENT_MODE"
|
|
3715
3784
|
;;`),
|
|
@@ -4182,7 +4251,9 @@ RUN set -eu; \
|
|
|
4182
4251
|
npm install -g oh-my-opencode@latest "oh-my-opencode-linux-\${OH_MY_OPENCODE_ARCH}@latest"
|
|
4183
4252
|
RUN oh-my-opencode --version
|
|
4184
4253
|
RUN npm install -g @anthropic-ai/claude-code@latest
|
|
4185
|
-
RUN claude --version
|
|
4254
|
+
RUN claude --version
|
|
4255
|
+
RUN npm install -g @google/gemini-cli@latest --force
|
|
4256
|
+
RUN gemini --version`;
|
|
4186
4257
|
var renderDockerfileOpenCode = () => `# Tooling: OpenCode (binary)
|
|
4187
4258
|
RUN set -eu; \
|
|
4188
4259
|
for attempt in 1 2 3 4 5; do \
|
|
@@ -6501,7 +6572,8 @@ var authSuccessPatterns = [
|
|
|
6501
6572
|
"Authentication successful",
|
|
6502
6573
|
"Successfully authenticated",
|
|
6503
6574
|
"Logged in as",
|
|
6504
|
-
"You are now logged in"
|
|
6575
|
+
"You are now logged in",
|
|
6576
|
+
"Logged in with Google"
|
|
6505
6577
|
];
|
|
6506
6578
|
var authFailurePatterns = [
|
|
6507
6579
|
"Authentication failed",
|
|
@@ -6512,22 +6584,28 @@ var authFailurePatterns = [
|
|
|
6512
6584
|
];
|
|
6513
6585
|
var detectAuthResult = (output) => {
|
|
6514
6586
|
const normalized = stripAnsi(output).toLowerCase();
|
|
6515
|
-
|
|
6516
|
-
|
|
6587
|
+
const authInitiated = [
|
|
6588
|
+
"please visit the following url",
|
|
6589
|
+
"enter the authorization code",
|
|
6590
|
+
"authorized the application"
|
|
6591
|
+
].some((m) => normalized.includes(m));
|
|
6592
|
+
if (authSuccessPatterns.some((pattern) => normalized.includes(pattern.toLowerCase()) && (authInitiated || normalized.includes("logged in with google")))) return "success";
|
|
6593
|
+
if (authFailurePatterns.some((pattern) => normalized.includes(pattern.toLowerCase()))) return "failure";
|
|
6517
6594
|
return "pending";
|
|
6518
6595
|
};
|
|
6519
|
-
var
|
|
6520
|
-
var buildDockerGeminiAuthSpec = (cwd, accountPath, image, containerPath) => ({
|
|
6596
|
+
var defaultGeminiOauthCallbackPort = 38751;
|
|
6597
|
+
var buildDockerGeminiAuthSpec = (cwd, accountPath, image, containerPath, port) => ({
|
|
6521
6598
|
cwd,
|
|
6522
6599
|
image,
|
|
6523
6600
|
hostPath: accountPath,
|
|
6524
6601
|
containerPath,
|
|
6525
|
-
callbackPort:
|
|
6602
|
+
callbackPort: port,
|
|
6526
6603
|
env: [
|
|
6527
6604
|
`HOME=${containerPath}`,
|
|
6528
6605
|
"NO_BROWSER=true",
|
|
6529
|
-
"GEMINI_CLI_NONINTERACTIVE=
|
|
6530
|
-
|
|
6606
|
+
"GEMINI_CLI_NONINTERACTIVE=true",
|
|
6607
|
+
"GEMINI_CLI_TRUST_ALL=true",
|
|
6608
|
+
`OAUTH_CALLBACK_PORT=${port}`,
|
|
6531
6609
|
"OAUTH_CALLBACK_HOST=0.0.0.0"
|
|
6532
6610
|
]
|
|
6533
6611
|
});
|
|
@@ -6542,8 +6620,6 @@ var buildDockerGeminiAuthArgs = (spec) => {
|
|
|
6542
6620
|
"-p",
|
|
6543
6621
|
`${spec.callbackPort}:${spec.callbackPort}`
|
|
6544
6622
|
];
|
|
6545
|
-
const dockerUser = resolveDefaultDockerUser();
|
|
6546
|
-
if (dockerUser !== null) base.push("--user", dockerUser);
|
|
6547
6623
|
for (const entry of spec.env) {
|
|
6548
6624
|
const trimmed = entry.trim();
|
|
6549
6625
|
if (trimmed.length === 0) continue;
|
|
@@ -6553,21 +6629,51 @@ var buildDockerGeminiAuthArgs = (spec) => {
|
|
|
6553
6629
|
...base,
|
|
6554
6630
|
spec.image,
|
|
6555
6631
|
"gemini",
|
|
6632
|
+
"login",
|
|
6556
6633
|
"--debug"
|
|
6557
6634
|
];
|
|
6558
6635
|
};
|
|
6636
|
+
var cleanupExistingContainers = (port) => Effect.gen(function* (_) {
|
|
6637
|
+
const ids = (yield* _(runCommandCapture({
|
|
6638
|
+
cwd: process.cwd(),
|
|
6639
|
+
command: "docker",
|
|
6640
|
+
args: [
|
|
6641
|
+
"ps",
|
|
6642
|
+
"-q",
|
|
6643
|
+
"--filter",
|
|
6644
|
+
`publish=${port}`
|
|
6645
|
+
]
|
|
6646
|
+
}, [0], () => /* @__PURE__ */ new Error("docker ps failed")).pipe(Effect.map((value) => value.trim()), Effect.orElseSucceed(() => "")))).split("\n").filter(Boolean);
|
|
6647
|
+
if (ids.length > 0) {
|
|
6648
|
+
yield* _(Effect.logInfo(`Cleaning up existing containers using port ${port}: ${ids.join(", ")}`));
|
|
6649
|
+
yield* _(runCommandExitCode({
|
|
6650
|
+
cwd: process.cwd(),
|
|
6651
|
+
command: "docker",
|
|
6652
|
+
args: [
|
|
6653
|
+
"rm",
|
|
6654
|
+
"-f",
|
|
6655
|
+
...ids
|
|
6656
|
+
]
|
|
6657
|
+
}).pipe(Effect.orElse(() => Effect.succeed(0))));
|
|
6658
|
+
}
|
|
6659
|
+
});
|
|
6559
6660
|
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) => {
|
|
6661
|
+
var pumpDockerOutput = (source, fd, resultBox, authDeferred) => {
|
|
6561
6662
|
const decoder = new TextDecoder("utf-8");
|
|
6562
6663
|
let outputWindow = "";
|
|
6563
|
-
return pipe(source, Stream.runForEach((chunk) => Effect.
|
|
6564
|
-
|
|
6664
|
+
return pipe(source, Stream.runForEach((chunk) => Effect.gen(function* (_) {
|
|
6665
|
+
yield* _(Effect.sync(() => {
|
|
6666
|
+
writeChunkToFd(fd, chunk);
|
|
6667
|
+
}));
|
|
6565
6668
|
outputWindow += decoder.decode(chunk);
|
|
6566
6669
|
if (outputWindow.length > outputWindowSize) outputWindow = outputWindow.slice(-outputWindowSize);
|
|
6567
6670
|
if (resultBox.value !== "pending") return;
|
|
6568
6671
|
const result = detectAuthResult(outputWindow);
|
|
6569
|
-
if (result !== "pending")
|
|
6570
|
-
|
|
6672
|
+
if (result !== "pending") {
|
|
6673
|
+
resultBox.value = result;
|
|
6674
|
+
if (result === "success") yield* _(Deferred.succeed(authDeferred, void 0));
|
|
6675
|
+
}
|
|
6676
|
+
}))).pipe(Effect.asVoid);
|
|
6571
6677
|
};
|
|
6572
6678
|
var resolveGeminiLoginResult = (result, exitCode) => Effect.gen(function* (_) {
|
|
6573
6679
|
if (result === "success") {
|
|
@@ -6581,7 +6687,7 @@ var resolveGeminiLoginResult = (result, exitCode) => Effect.gen(function* (_) {
|
|
|
6581
6687
|
})));
|
|
6582
6688
|
});
|
|
6583
6689
|
var printOauthInstructions = () => Effect.sync(() => {
|
|
6584
|
-
const port =
|
|
6690
|
+
const port = defaultGeminiOauthCallbackPort;
|
|
6585
6691
|
process.stderr.write("\n");
|
|
6586
6692
|
process.stderr.write("╔═══════════════════════════════════════════════════════════════════════════╗\n");
|
|
6587
6693
|
process.stderr.write("║ Gemini CLI OAuth Authentication ║\n");
|
|
@@ -6593,15 +6699,37 @@ var printOauthInstructions = () => Effect.sync(() => {
|
|
|
6593
6699
|
process.stderr.write("╚═══════════════════════════════════════════════════════════════════════════╝\n");
|
|
6594
6700
|
process.stderr.write("\n");
|
|
6595
6701
|
});
|
|
6702
|
+
var fixGeminiAuthPermissions = (hostPath, containerPath) => runCommandExitCode({
|
|
6703
|
+
cwd: process.cwd(),
|
|
6704
|
+
command: "docker",
|
|
6705
|
+
args: [
|
|
6706
|
+
"run",
|
|
6707
|
+
"--rm",
|
|
6708
|
+
"-v",
|
|
6709
|
+
`${hostPath}:${containerPath}`,
|
|
6710
|
+
"alpine",
|
|
6711
|
+
"chmod",
|
|
6712
|
+
"-R",
|
|
6713
|
+
"777",
|
|
6714
|
+
containerPath
|
|
6715
|
+
]
|
|
6716
|
+
}).pipe(Effect.tapError((err) => Effect.logWarning(`Failed to fix Gemini auth permissions: ${String(err)}`)), Effect.orElse(() => Effect.succeed(0)));
|
|
6596
6717
|
var runGeminiOauthLoginWithPrompt = (cwd, accountPath, options) => Effect.scoped(Effect.gen(function* (_) {
|
|
6718
|
+
const port = defaultGeminiOauthCallbackPort;
|
|
6719
|
+
yield* _(cleanupExistingContainers(port));
|
|
6597
6720
|
yield* _(printOauthInstructions());
|
|
6598
|
-
const
|
|
6721
|
+
const executor = yield* _(CommandExecutor.CommandExecutor);
|
|
6722
|
+
const hostPath = yield* _(resolveDockerVolumeHostPath(cwd, accountPath));
|
|
6723
|
+
const spec = buildDockerGeminiAuthSpec(cwd, hostPath, options.image, options.containerPath, port);
|
|
6724
|
+
const proc = yield* _(startDockerProcess(executor, spec));
|
|
6725
|
+
const authDeferred = yield* _(Deferred.make());
|
|
6599
6726
|
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)));
|
|
6727
|
+
const stdoutFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stdout, 1, resultBox, authDeferred)));
|
|
6728
|
+
const stderrFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stderr, 2, resultBox, authDeferred)));
|
|
6729
|
+
const exitCode = yield* _(Effect.race(proc.exitCode.pipe(Effect.map(Number)), pipe(Deferred.await(authDeferred), Effect.flatMap(() => proc.kill()), Effect.map(() => 0))));
|
|
6603
6730
|
yield* _(Fiber$1.join(stdoutFiber));
|
|
6604
6731
|
yield* _(Fiber$1.join(stderrFiber));
|
|
6732
|
+
yield* _(fixGeminiAuthPermissions(hostPath, spec.containerPath));
|
|
6605
6733
|
return yield* _(resolveGeminiLoginResult(resultBox.value, exitCode));
|
|
6606
6734
|
}));
|
|
6607
6735
|
//#endregion
|
|
@@ -6715,15 +6843,52 @@ var authGeminiLoginCli = (_command) => Effect.gen(function* (_) {
|
|
|
6715
6843
|
yield* _(Effect.log(" - Use: docker-git menu -> Auth profiles -> Gemini CLI: login via OAuth"));
|
|
6716
6844
|
yield* _(Effect.log(" - Follow the prompts to authenticate with your Google account"));
|
|
6717
6845
|
});
|
|
6846
|
+
var prepareGeminiCredentialsDir = (cwd, accountPath, fs) => Effect.gen(function* (_) {
|
|
6847
|
+
const credentialsDir = geminiCredentialsPath(accountPath);
|
|
6848
|
+
const removeFallback = pipe(runCommandExitCode({
|
|
6849
|
+
cwd,
|
|
6850
|
+
command: "docker",
|
|
6851
|
+
args: [
|
|
6852
|
+
"run",
|
|
6853
|
+
"--rm",
|
|
6854
|
+
"-v",
|
|
6855
|
+
`${accountPath}:/target`,
|
|
6856
|
+
"alpine",
|
|
6857
|
+
"rm",
|
|
6858
|
+
"-rf",
|
|
6859
|
+
"/target/.gemini"
|
|
6860
|
+
]
|
|
6861
|
+
}), Effect.asVoid, Effect.orElse(() => Effect.void));
|
|
6862
|
+
yield* _(fs.remove(credentialsDir, {
|
|
6863
|
+
recursive: true,
|
|
6864
|
+
force: true
|
|
6865
|
+
}).pipe(Effect.orElse(() => removeFallback)));
|
|
6866
|
+
yield* _(fs.makeDirectory(credentialsDir, { recursive: true }));
|
|
6867
|
+
return credentialsDir;
|
|
6868
|
+
});
|
|
6869
|
+
var writeInitialSettings = (credentialsDir, fs) => Effect.gen(function* (_) {
|
|
6870
|
+
const settingsPath = `${credentialsDir}/settings.json`;
|
|
6871
|
+
yield* _(fs.writeFileString(settingsPath, JSON.stringify({ security: { folderTrust: { enabled: false } } })));
|
|
6872
|
+
const trustedFoldersPath = `${credentialsDir}/trustedFolders.json`;
|
|
6873
|
+
yield* _(fs.writeFileString(trustedFoldersPath, JSON.stringify({
|
|
6874
|
+
"/": "TRUST_FOLDER",
|
|
6875
|
+
[geminiContainerHomeDir]: "TRUST_FOLDER"
|
|
6876
|
+
})));
|
|
6877
|
+
return settingsPath;
|
|
6878
|
+
});
|
|
6718
6879
|
var authGeminiLoginOauth = (command) => {
|
|
6719
6880
|
const accountLabel = normalizeAccountLabel(command.label, "default");
|
|
6720
6881
|
return withGeminiAuth(command, ({ accountPath, cwd, fs }) => Effect.gen(function* (_) {
|
|
6721
|
-
const
|
|
6722
|
-
yield* _(fs.makeDirectory(credentialsDir, { recursive: true }));
|
|
6882
|
+
const settingsPath = yield* _(writeInitialSettings(yield* _(prepareGeminiCredentialsDir(cwd, accountPath, fs)), fs));
|
|
6723
6883
|
yield* _(runGeminiOauthLoginWithPrompt(cwd, accountPath, {
|
|
6724
6884
|
image: geminiImageName,
|
|
6725
6885
|
containerPath: geminiContainerHomeDir
|
|
6726
6886
|
}));
|
|
6887
|
+
yield* _(fs.writeFileString(settingsPath, JSON.stringify({ security: {
|
|
6888
|
+
folderTrust: { enabled: false },
|
|
6889
|
+
auth: { selectedType: "oauth-personal" },
|
|
6890
|
+
approvalPolicy: "never"
|
|
6891
|
+
} }, null, 2) + "\n"));
|
|
6727
6892
|
}), { buildImage: true }).pipe(Effect.zipRight(autoSyncState(`chore(state): auth gemini oauth ${accountLabel}`)));
|
|
6728
6893
|
};
|
|
6729
6894
|
var authGeminiStatus = (command) => withGeminiAuth(command, ({ accountLabel, accountPath, fs }) => Effect.gen(function* (_) {
|
|
@@ -8098,7 +8263,8 @@ var buildClaudeCommand = (action, options) => Match.value(action).pipe(Match.whe
|
|
|
8098
8263
|
var buildGeminiCommand = (action, options) => Match.value(action).pipe(Match.when("login", () => Either.right({
|
|
8099
8264
|
_tag: "AuthGeminiLogin",
|
|
8100
8265
|
label: options.label,
|
|
8101
|
-
geminiAuthPath: options.geminiAuthPath
|
|
8266
|
+
geminiAuthPath: options.geminiAuthPath,
|
|
8267
|
+
isWeb: options.authWeb
|
|
8102
8268
|
})), Match.when("status", () => Either.right({
|
|
8103
8269
|
_tag: "AuthGeminiStatus",
|
|
8104
8270
|
label: options.label,
|
|
@@ -9588,12 +9754,14 @@ var resolveClaudeLogoutEffect = (labelOption) => authClaudeLogout({
|
|
|
9588
9754
|
var resolveGeminiOauthEffect = (labelOption) => authGeminiLoginOauth({
|
|
9589
9755
|
_tag: "AuthGeminiLogin",
|
|
9590
9756
|
label: labelOption,
|
|
9591
|
-
geminiAuthPath: geminiAuthRoot
|
|
9757
|
+
geminiAuthPath: geminiAuthRoot,
|
|
9758
|
+
isWeb: false
|
|
9592
9759
|
});
|
|
9593
9760
|
var resolveGeminiApiKeyEffect = (labelOption, apiKey) => authGeminiLogin({
|
|
9594
9761
|
_tag: "AuthGeminiLogin",
|
|
9595
9762
|
label: labelOption,
|
|
9596
|
-
geminiAuthPath: geminiAuthRoot
|
|
9763
|
+
geminiAuthPath: geminiAuthRoot,
|
|
9764
|
+
isWeb: false
|
|
9597
9765
|
}, apiKey);
|
|
9598
9766
|
var resolveGeminiLogoutEffect = (labelOption) => authGeminiLogout({
|
|
9599
9767
|
_tag: "AuthGeminiLogout",
|
|
@@ -11378,7 +11546,7 @@ var setExitCode = (code) => Effect.sync(() => {
|
|
|
11378
11546
|
});
|
|
11379
11547
|
var logWarningAndExit = (error) => pipe(Effect.logWarning(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
|
|
11380
11548
|
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);
|
|
11549
|
+
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
11550
|
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
11551
|
onFailure: (error) => isParseError(error) ? logErrorAndExit(error) : pipe(Effect.logError(renderError(error)), Effect.flatMap(() => Effect.fail(error))),
|
|
11384
11552
|
onSuccess: () => Effect.void
|