@prover-coder-ai/docker-git 1.0.43 → 1.0.44
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 +719 -151
- package/dist/src/docker-git/main.js.map +1 -1
- package/package.json +1 -1
|
@@ -269,6 +269,8 @@ var defaultTemplateConfig = {
|
|
|
269
269
|
codexAuthPath: "./.docker-git/.orch/auth/codex",
|
|
270
270
|
codexSharedAuthPath: "./.docker-git/.orch/auth/codex",
|
|
271
271
|
codexHome: "/home/dev/.codex",
|
|
272
|
+
geminiAuthPath: "./.docker-git/.orch/auth/gemini",
|
|
273
|
+
geminiHome: "/home/dev/.gemini",
|
|
272
274
|
cpuLimit: "30%",
|
|
273
275
|
ramLimit: "30%",
|
|
274
276
|
dockerNetworkMode: defaultDockerNetworkMode,
|
|
@@ -809,6 +811,10 @@ var buildDockerAuthSpec = (input) => ({
|
|
|
809
811
|
args: input.args,
|
|
810
812
|
interactive: input.interactive
|
|
811
813
|
});
|
|
814
|
+
var isRegularFile$1 = (fs, filePath) => Effect.gen(function* (_) {
|
|
815
|
+
if (!(yield* _(fs.exists(filePath)))) return false;
|
|
816
|
+
return (yield* _(fs.stat(filePath))).type === "File";
|
|
817
|
+
});
|
|
812
818
|
//#endregion
|
|
813
819
|
//#region ../lib/src/usecases/auth-sync-helpers.ts
|
|
814
820
|
var JsonValueSchema = Schema.suspend(() => Schema.Union(Schema.Null, Schema.Boolean, Schema.String, Schema.JsonNumber, Schema.Array(JsonValueSchema), Schema.Record({
|
|
@@ -876,7 +882,7 @@ var autoOptionError = (reason) => ({
|
|
|
876
882
|
option: "--auto",
|
|
877
883
|
reason
|
|
878
884
|
});
|
|
879
|
-
var isRegularFile
|
|
885
|
+
var isRegularFile = (fs, filePath) => Effect.gen(function* (_) {
|
|
880
886
|
if (!(yield* _(fs.exists(filePath)))) return false;
|
|
881
887
|
return (yield* _(fs.stat(filePath))).type === "File";
|
|
882
888
|
});
|
|
@@ -892,8 +898,8 @@ var resolveClaudeAccountPath$1 = (rootPath, label) => {
|
|
|
892
898
|
var hasClaudeAuth = (fs, rootPath, label) => Effect.gen(function* (_) {
|
|
893
899
|
for (const accountPath of resolveClaudeAccountPath$1(rootPath, label)) {
|
|
894
900
|
if (yield* _(hasNonEmptyFile(fs, `${accountPath}/.oauth-token`))) return true;
|
|
895
|
-
if (yield* _(isRegularFile
|
|
896
|
-
if (yield* _(isRegularFile
|
|
901
|
+
if (yield* _(isRegularFile(fs, `${accountPath}/.credentials.json`))) return true;
|
|
902
|
+
if (yield* _(isRegularFile(fs, `${accountPath}/.claude/.credentials.json`))) return true;
|
|
897
903
|
}
|
|
898
904
|
return false;
|
|
899
905
|
});
|
|
@@ -1357,6 +1363,9 @@ var TemplateConfigSchema = Schema.Struct({
|
|
|
1357
1363
|
codexAuthPath: Schema.String,
|
|
1358
1364
|
codexSharedAuthPath: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.codexSharedAuthPath }),
|
|
1359
1365
|
codexHome: Schema.String,
|
|
1366
|
+
geminiAuthLabel: Schema.optional(Schema.String),
|
|
1367
|
+
geminiAuthPath: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.geminiAuthPath }),
|
|
1368
|
+
geminiHome: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.geminiHome }),
|
|
1360
1369
|
cpuLimit: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.cpuLimit }),
|
|
1361
1370
|
ramLimit: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.ramLimit }),
|
|
1362
1371
|
dockerNetworkMode: Schema.optionalWith(Schema.Literal("shared", "project"), { default: () => defaultTemplateConfig.dockerNetworkMode }),
|
|
@@ -2969,6 +2978,143 @@ if [[ -f "$LEGACY_AGENTS_PATH" && -f "$AGENTS_PATH" ]]; then
|
|
|
2969
2978
|
fi`;
|
|
2970
2979
|
var renderEntrypointAgentsNotice = (config) => entrypointAgentsNoticeTemplate.replaceAll("__CODEX_HOME__", config.codexHome).replaceAll("__SSH_USER__", config.sshUser).replaceAll("__TARGET_DIR__", config.targetDir);
|
|
2971
2980
|
//#endregion
|
|
2981
|
+
//#region ../lib/src/core/templates-entrypoint/gemini.ts
|
|
2982
|
+
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
|
+
if [[ -z "$GEMINI_LABEL_RAW" ]]; then
|
|
2986
|
+
GEMINI_LABEL_RAW="default"
|
|
2987
|
+
fi
|
|
2988
|
+
|
|
2989
|
+
GEMINI_LABEL_NORM="$(printf "%s" "$GEMINI_LABEL_RAW" \
|
|
2990
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
2991
|
+
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//')"
|
|
2992
|
+
if [[ -z "$GEMINI_LABEL_NORM" ]]; then
|
|
2993
|
+
GEMINI_LABEL_NORM="default"
|
|
2994
|
+
fi
|
|
2995
|
+
|
|
2996
|
+
GEMINI_AUTH_ROOT="__GEMINI_AUTH_ROOT__"
|
|
2997
|
+
GEMINI_AUTH_DIR="$GEMINI_AUTH_ROOT/$GEMINI_LABEL_NORM"
|
|
2998
|
+
|
|
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
|
|
3008
|
+
GEMINI_HOME_DIR="__GEMINI_HOME_DIR__"
|
|
3009
|
+
mkdir -p "$GEMINI_HOME_DIR" || true
|
|
3010
|
+
|
|
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
|
+
docker_git_link_gemini_file() {
|
|
3016
|
+
local source_path="$1"
|
|
3017
|
+
local link_path="$2"
|
|
3018
|
+
|
|
3019
|
+
# Preserve user-created regular files and seed config dir once.
|
|
3020
|
+
if [[ -e "$link_path" && ! -L "$link_path" ]]; then
|
|
3021
|
+
if [[ -f "$link_path" && ! -e "$source_path" ]]; then
|
|
3022
|
+
cp "$link_path" "$source_path" || true
|
|
3023
|
+
chmod 0600 "$source_path" || true
|
|
3024
|
+
fi
|
|
3025
|
+
return 0
|
|
3026
|
+
fi
|
|
3027
|
+
|
|
3028
|
+
ln -sfn "$source_path" "$link_path" || true
|
|
3029
|
+
}
|
|
3030
|
+
|
|
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
|
+
}
|
|
3050
|
+
|
|
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
|
|
3057
|
+
fi
|
|
3058
|
+
|
|
3059
|
+
if ! command -v npm >/dev/null 2>&1; then
|
|
3060
|
+
return 0
|
|
3061
|
+
fi
|
|
3062
|
+
|
|
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
|
|
3068
|
+
fi
|
|
3069
|
+
|
|
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
|
|
3074
|
+
|
|
3075
|
+
if ! command -v npm >/dev/null 2>&1; then
|
|
3076
|
+
echo "gemini: npm is required but missing" >&2
|
|
3077
|
+
exit 127
|
|
3078
|
+
fi
|
|
3079
|
+
|
|
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
|
|
3085
|
+
fi
|
|
3086
|
+
|
|
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
|
|
3091
|
+
}
|
|
3092
|
+
|
|
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"
|
|
3096
|
+
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
|
|
3106
|
+
fi
|
|
3107
|
+
EOF
|
|
3108
|
+
chmod 0644 "$GEMINI_PROFILE" || true
|
|
3109
|
+
|
|
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:-}"`;
|
|
3112
|
+
var renderEntrypointGeminiConfig = (config) => [
|
|
3113
|
+
renderGeminiAuthConfig(config),
|
|
3114
|
+
renderGeminiCliInstall(),
|
|
3115
|
+
renderGeminiProfileSetup()
|
|
3116
|
+
].join("\n\n");
|
|
3117
|
+
//#endregion
|
|
2972
3118
|
//#region ../lib/src/core/templates-entrypoint/git.ts
|
|
2973
3119
|
var renderAuthLabelResolution = () => String.raw`# 2) Ensure GitHub auth vars are available for SSH sessions.
|
|
2974
3120
|
# Prefer a label-selected token (same selection model as clone/create) when present.
|
|
@@ -3871,6 +4017,7 @@ var renderEntrypoint = (config) => [
|
|
|
3871
4017
|
renderEntrypointDockerSocket(config),
|
|
3872
4018
|
renderEntrypointGitConfig(config),
|
|
3873
4019
|
renderEntrypointClaudeConfig(config),
|
|
4020
|
+
renderEntrypointGeminiConfig(config),
|
|
3874
4021
|
renderEntrypointGitHooks(),
|
|
3875
4022
|
renderEntrypointBackgroundTasks(config),
|
|
3876
4023
|
renderEntrypointBaseline(),
|
|
@@ -4427,7 +4574,7 @@ var resolveOriginPushTarget = (originUrl) => {
|
|
|
4427
4574
|
const trimmed = originUrl?.trim() ?? "";
|
|
4428
4575
|
return trimmed.length > 0 ? trimmed : "origin";
|
|
4429
4576
|
};
|
|
4430
|
-
var resolveSyncMessage = (value) => {
|
|
4577
|
+
var resolveSyncMessage$1 = (value) => {
|
|
4431
4578
|
const trimmed = value?.trim() ?? "";
|
|
4432
4579
|
return trimmed.length > 0 ? trimmed : defaultSyncMessage;
|
|
4433
4580
|
};
|
|
@@ -4508,7 +4655,7 @@ var runStateSyncOps = (root, originUrl, message, env, options) => Effect.gen(fun
|
|
|
4508
4655
|
yield* _(normalizeLegacyStateProjects(root));
|
|
4509
4656
|
const baseBranch = resolveBaseBranch(yield* _(getCurrentBranch(root, env)));
|
|
4510
4657
|
yield* _(pullRemoteAndRestoreLocal(root, baseBranch, env));
|
|
4511
|
-
yield* _(commitAllIfNeeded(root, resolveSyncMessage(message), env));
|
|
4658
|
+
yield* _(commitAllIfNeeded(root, resolveSyncMessage$1(message), env));
|
|
4512
4659
|
const pushExit = yield* _(gitExitCode(root, [
|
|
4513
4660
|
"push",
|
|
4514
4661
|
"--no-verify",
|
|
@@ -5921,12 +6068,7 @@ var applyProjectConfig = (command) => runApplyForProjectDir(command.projectDir,
|
|
|
5921
6068
|
return yield* _(runApplyForProjectDir(inferredProjectDir, command));
|
|
5922
6069
|
}) : Effect.fail(error)));
|
|
5923
6070
|
//#endregion
|
|
5924
|
-
//#region ../lib/src/
|
|
5925
|
-
var oauthTokenEnvKey = "DOCKER_GIT_CLAUDE_OAUTH_TOKEN";
|
|
5926
|
-
var tokenMarker = "Your OAuth token (valid for 1 year):";
|
|
5927
|
-
var tokenFooterMarker = "Store this token securely.";
|
|
5928
|
-
var outputWindowSize = 262144;
|
|
5929
|
-
var oauthTokenRegex = /([A-Za-z0-9][A-Za-z0-9._-]{20,})/u;
|
|
6071
|
+
//#region ../lib/src/shell/ansi-strip.ts
|
|
5930
6072
|
var ansiEscape = "\x1B";
|
|
5931
6073
|
var ansiBell = "\x07";
|
|
5932
6074
|
var isAnsiFinalByte = (codePoint) => codePoint !== void 0 && codePoint >= 64 && codePoint <= 126;
|
|
@@ -5970,6 +6112,20 @@ var stripAnsi = (raw) => {
|
|
|
5970
6112
|
}
|
|
5971
6113
|
return cleaned.join("");
|
|
5972
6114
|
};
|
|
6115
|
+
var writeChunkToFd = (fd, chunk) => {
|
|
6116
|
+
if (fd === 2) {
|
|
6117
|
+
process.stderr.write(chunk);
|
|
6118
|
+
return;
|
|
6119
|
+
}
|
|
6120
|
+
process.stdout.write(chunk);
|
|
6121
|
+
};
|
|
6122
|
+
//#endregion
|
|
6123
|
+
//#region ../lib/src/usecases/auth-claude-oauth.ts
|
|
6124
|
+
var oauthTokenEnvKey = "DOCKER_GIT_CLAUDE_OAUTH_TOKEN";
|
|
6125
|
+
var tokenMarker = "Your OAuth token (valid for 1 year):";
|
|
6126
|
+
var tokenFooterMarker = "Store this token securely.";
|
|
6127
|
+
var outputWindowSize$1 = 262144;
|
|
6128
|
+
var oauthTokenRegex = /([A-Za-z0-9][A-Za-z0-9._-]{20,})/u;
|
|
5973
6129
|
var extractOauthToken = (rawOutput) => {
|
|
5974
6130
|
const normalized = stripAnsi(rawOutput).replaceAll("\r", "\n");
|
|
5975
6131
|
const markerIndex = normalized.lastIndexOf(tokenMarker);
|
|
@@ -6024,21 +6180,14 @@ var buildDockerSetupTokenArgs = (spec) => {
|
|
|
6024
6180
|
...spec.args
|
|
6025
6181
|
];
|
|
6026
6182
|
};
|
|
6027
|
-
var startDockerProcess = (executor, spec) => executor.start(pipe(Command.make("docker", ...buildDockerSetupTokenArgs(spec)), Command.workingDirectory(spec.cwd), Command.stdin("inherit"), Command.stdout("pipe"), Command.stderr("pipe")));
|
|
6028
|
-
var
|
|
6029
|
-
if (fd === 2) {
|
|
6030
|
-
process.stderr.write(chunk);
|
|
6031
|
-
return;
|
|
6032
|
-
}
|
|
6033
|
-
process.stdout.write(chunk);
|
|
6034
|
-
};
|
|
6035
|
-
var pumpDockerOutput = (source, fd, tokenBox) => {
|
|
6183
|
+
var startDockerProcess$1 = (executor, spec) => executor.start(pipe(Command.make("docker", ...buildDockerSetupTokenArgs(spec)), Command.workingDirectory(spec.cwd), Command.stdin("inherit"), Command.stdout("pipe"), Command.stderr("pipe")));
|
|
6184
|
+
var pumpDockerOutput$1 = (source, fd, tokenBox) => {
|
|
6036
6185
|
const decoder = new TextDecoder("utf-8");
|
|
6037
6186
|
let outputWindow = "";
|
|
6038
6187
|
return pipe(source, Stream.runForEach((chunk) => Effect.sync(() => {
|
|
6039
6188
|
writeChunkToFd(fd, chunk);
|
|
6040
6189
|
outputWindow += decoder.decode(chunk);
|
|
6041
|
-
if (outputWindow.length > outputWindowSize) outputWindow = outputWindow.slice(-outputWindowSize);
|
|
6190
|
+
if (outputWindow.length > outputWindowSize$1) outputWindow = outputWindow.slice(-outputWindowSize$1);
|
|
6042
6191
|
if (tokenBox.value !== null) return;
|
|
6043
6192
|
const parsed = extractOauthToken(outputWindow);
|
|
6044
6193
|
if (parsed !== null) tokenBox.value = parsed;
|
|
@@ -6060,10 +6209,10 @@ var runClaudeOauthLoginWithPrompt = (cwd, accountPath, options) => {
|
|
|
6060
6209
|
const envToken = oauthTokenFromEnv();
|
|
6061
6210
|
if (envToken !== null) return ensureOauthToken(envToken);
|
|
6062
6211
|
return Effect.scoped(Effect.gen(function* (_) {
|
|
6063
|
-
const proc = yield* _(startDockerProcess(yield* _(CommandExecutor.CommandExecutor), buildDockerSetupTokenSpec(cwd, yield* _(resolveDockerVolumeHostPath(cwd, accountPath)), options.image, options.containerPath)));
|
|
6212
|
+
const proc = yield* _(startDockerProcess$1(yield* _(CommandExecutor.CommandExecutor), buildDockerSetupTokenSpec(cwd, yield* _(resolveDockerVolumeHostPath(cwd, accountPath)), options.image, options.containerPath)));
|
|
6064
6213
|
const tokenBox = { value: null };
|
|
6065
|
-
const stdoutFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stdout, 1, tokenBox)));
|
|
6066
|
-
const stderrFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stderr, 2, tokenBox)));
|
|
6214
|
+
const stdoutFiber = yield* _(Effect.forkScoped(pumpDockerOutput$1(proc.stdout, 1, tokenBox)));
|
|
6215
|
+
const stderrFiber = yield* _(Effect.forkScoped(pumpDockerOutput$1(proc.stderr, 2, tokenBox)));
|
|
6067
6216
|
const exitCode = yield* _(proc.exitCode.pipe(Effect.map(Number)));
|
|
6068
6217
|
yield* _(Fiber$1.join(stdoutFiber));
|
|
6069
6218
|
yield* _(Fiber$1.join(stderrFiber));
|
|
@@ -6084,19 +6233,15 @@ var claudeOauthTokenPath = (accountPath) => `${accountPath}/${claudeOauthTokenFi
|
|
|
6084
6233
|
var claudeConfigPath = (accountPath) => `${accountPath}/${claudeConfigFileName}`;
|
|
6085
6234
|
var claudeCredentialsPath = (accountPath) => `${accountPath}/${claudeCredentialsFileName}`;
|
|
6086
6235
|
var claudeNestedCredentialsPath = (accountPath) => `${accountPath}/${claudeCredentialsDirName}/${claudeCredentialsFileName}`;
|
|
6087
|
-
var isRegularFile = (fs, filePath) => Effect.gen(function* (_) {
|
|
6088
|
-
if (!(yield* _(fs.exists(filePath)))) return false;
|
|
6089
|
-
return (yield* _(fs.stat(filePath))).type === "File";
|
|
6090
|
-
});
|
|
6091
6236
|
var syncClaudeCredentialsFile = (fs, accountPath) => Effect.gen(function* (_) {
|
|
6092
6237
|
const nestedPath = claudeNestedCredentialsPath(accountPath);
|
|
6093
6238
|
const rootPath = claudeCredentialsPath(accountPath);
|
|
6094
|
-
if (yield* _(isRegularFile(fs, nestedPath))) {
|
|
6239
|
+
if (yield* _(isRegularFile$1(fs, nestedPath))) {
|
|
6095
6240
|
yield* _(fs.copyFile(nestedPath, rootPath));
|
|
6096
6241
|
yield* _(fs.chmod(rootPath, 384), Effect.orElseSucceed(() => void 0));
|
|
6097
6242
|
return;
|
|
6098
6243
|
}
|
|
6099
|
-
if (yield* _(isRegularFile(fs, rootPath))) {
|
|
6244
|
+
if (yield* _(isRegularFile$1(fs, rootPath))) {
|
|
6100
6245
|
const nestedDirPath = `${accountPath}/${claudeCredentialsDirName}`;
|
|
6101
6246
|
yield* _(fs.makeDirectory(nestedDirPath, { recursive: true }));
|
|
6102
6247
|
yield* _(fs.copyFile(rootPath, nestedPath));
|
|
@@ -6109,12 +6254,12 @@ var clearClaudeSessionCredentials = (fs, accountPath) => Effect.gen(function* (_
|
|
|
6109
6254
|
});
|
|
6110
6255
|
var hasNonEmptyOauthToken$1 = (fs, accountPath) => Effect.gen(function* (_) {
|
|
6111
6256
|
const tokenPath = claudeOauthTokenPath(accountPath);
|
|
6112
|
-
if (!(yield* _(isRegularFile(fs, tokenPath)))) return false;
|
|
6257
|
+
if (!(yield* _(isRegularFile$1(fs, tokenPath)))) return false;
|
|
6113
6258
|
return (yield* _(fs.readFileString(tokenPath), Effect.orElseSucceed(() => ""))).trim().length > 0;
|
|
6114
6259
|
});
|
|
6115
6260
|
var readOauthToken = (fs, accountPath) => Effect.gen(function* (_) {
|
|
6116
6261
|
const tokenPath = claudeOauthTokenPath(accountPath);
|
|
6117
|
-
if (!(yield* _(isRegularFile(fs, tokenPath)))) return null;
|
|
6262
|
+
if (!(yield* _(isRegularFile$1(fs, tokenPath)))) return null;
|
|
6118
6263
|
const token = (yield* _(fs.readFileString(tokenPath), Effect.orElseSucceed(() => ""))).trim();
|
|
6119
6264
|
return token.length > 0 ? token : null;
|
|
6120
6265
|
});
|
|
@@ -6124,7 +6269,7 @@ var resolveClaudeAuthMethod = (fs, accountPath) => Effect.gen(function* (_) {
|
|
|
6124
6269
|
return "oauth-token";
|
|
6125
6270
|
}
|
|
6126
6271
|
yield* _(syncClaudeCredentialsFile(fs, accountPath));
|
|
6127
|
-
return (yield* _(isRegularFile(fs, claudeCredentialsPath(accountPath)))) ? "claude-ai-session" : "none";
|
|
6272
|
+
return (yield* _(isRegularFile$1(fs, claudeCredentialsPath(accountPath)))) ? "claude-ai-session" : "none";
|
|
6128
6273
|
});
|
|
6129
6274
|
var buildClaudeAuthEnv = (interactive, oauthToken = null) => [...interactive ? [
|
|
6130
6275
|
`HOME=${claudeContainerHomeDir}`,
|
|
@@ -6349,6 +6494,259 @@ var authCodexStatus = (command) => withCodexAuth(command, ({ accountPath, cwd })
|
|
|
6349
6494
|
}));
|
|
6350
6495
|
var authCodexLogout = (command) => withCodexAuth(command, ({ accountPath, cwd }) => runCodexLogout(cwd, accountPath)).pipe(Effect.zipRight(autoSyncState(`chore(state): auth codex logout ${normalizeAccountLabel(command.label, "default")}`)));
|
|
6351
6496
|
//#endregion
|
|
6497
|
+
//#region ../lib/src/usecases/auth-gemini-oauth.ts
|
|
6498
|
+
var outputWindowSize = 262144;
|
|
6499
|
+
var authSuccessPatterns = [
|
|
6500
|
+
"Authentication succeeded",
|
|
6501
|
+
"Authentication successful",
|
|
6502
|
+
"Successfully authenticated",
|
|
6503
|
+
"Logged in as",
|
|
6504
|
+
"You are now logged in"
|
|
6505
|
+
];
|
|
6506
|
+
var authFailurePatterns = [
|
|
6507
|
+
"Authentication failed",
|
|
6508
|
+
"Failed to authenticate",
|
|
6509
|
+
"Authorization failed",
|
|
6510
|
+
"Authentication timed out",
|
|
6511
|
+
"Authentication cancelled"
|
|
6512
|
+
];
|
|
6513
|
+
var detectAuthResult = (output) => {
|
|
6514
|
+
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";
|
|
6517
|
+
return "pending";
|
|
6518
|
+
};
|
|
6519
|
+
var geminiOauthCallbackPort = 38751;
|
|
6520
|
+
var buildDockerGeminiAuthSpec = (cwd, accountPath, image, containerPath) => ({
|
|
6521
|
+
cwd,
|
|
6522
|
+
image,
|
|
6523
|
+
hostPath: accountPath,
|
|
6524
|
+
containerPath,
|
|
6525
|
+
callbackPort: geminiOauthCallbackPort,
|
|
6526
|
+
env: [
|
|
6527
|
+
`HOME=${containerPath}`,
|
|
6528
|
+
"NO_BROWSER=true",
|
|
6529
|
+
"GEMINI_CLI_NONINTERACTIVE=false",
|
|
6530
|
+
`OAUTH_CALLBACK_PORT=${geminiOauthCallbackPort}`,
|
|
6531
|
+
"OAUTH_CALLBACK_HOST=0.0.0.0"
|
|
6532
|
+
]
|
|
6533
|
+
});
|
|
6534
|
+
var buildDockerGeminiAuthArgs = (spec) => {
|
|
6535
|
+
const base = [
|
|
6536
|
+
"run",
|
|
6537
|
+
"--rm",
|
|
6538
|
+
"-i",
|
|
6539
|
+
"-t",
|
|
6540
|
+
"-v",
|
|
6541
|
+
`${spec.hostPath}:${spec.containerPath}`,
|
|
6542
|
+
"-p",
|
|
6543
|
+
`${spec.callbackPort}:${spec.callbackPort}`
|
|
6544
|
+
];
|
|
6545
|
+
const dockerUser = resolveDefaultDockerUser();
|
|
6546
|
+
if (dockerUser !== null) base.push("--user", dockerUser);
|
|
6547
|
+
for (const entry of spec.env) {
|
|
6548
|
+
const trimmed = entry.trim();
|
|
6549
|
+
if (trimmed.length === 0) continue;
|
|
6550
|
+
base.push("-e", trimmed);
|
|
6551
|
+
}
|
|
6552
|
+
return [
|
|
6553
|
+
...base,
|
|
6554
|
+
spec.image,
|
|
6555
|
+
"gemini",
|
|
6556
|
+
"--debug"
|
|
6557
|
+
];
|
|
6558
|
+
};
|
|
6559
|
+
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) => {
|
|
6561
|
+
const decoder = new TextDecoder("utf-8");
|
|
6562
|
+
let outputWindow = "";
|
|
6563
|
+
return pipe(source, Stream.runForEach((chunk) => Effect.sync(() => {
|
|
6564
|
+
writeChunkToFd(fd, chunk);
|
|
6565
|
+
outputWindow += decoder.decode(chunk);
|
|
6566
|
+
if (outputWindow.length > outputWindowSize) outputWindow = outputWindow.slice(-outputWindowSize);
|
|
6567
|
+
if (resultBox.value !== "pending") return;
|
|
6568
|
+
const result = detectAuthResult(outputWindow);
|
|
6569
|
+
if (result !== "pending") resultBox.value = result;
|
|
6570
|
+
}).pipe(Effect.asVoid))).pipe(Effect.asVoid);
|
|
6571
|
+
};
|
|
6572
|
+
var resolveGeminiLoginResult = (result, exitCode) => Effect.gen(function* (_) {
|
|
6573
|
+
if (result === "success") {
|
|
6574
|
+
if (exitCode !== 0) yield* _(Effect.logWarning(`Gemini CLI returned exit=${exitCode}, but authentication appears successful; continuing.`));
|
|
6575
|
+
return;
|
|
6576
|
+
}
|
|
6577
|
+
if (result === "failure") yield* _(Effect.fail(new AuthError({ message: "Gemini CLI OAuth authentication failed. Please try again." })));
|
|
6578
|
+
if (exitCode !== 0) yield* _(Effect.fail(new CommandFailedError({
|
|
6579
|
+
command: "gemini",
|
|
6580
|
+
exitCode
|
|
6581
|
+
})));
|
|
6582
|
+
});
|
|
6583
|
+
var printOauthInstructions = () => Effect.sync(() => {
|
|
6584
|
+
const port = geminiOauthCallbackPort;
|
|
6585
|
+
process.stderr.write("\n");
|
|
6586
|
+
process.stderr.write("╔═══════════════════════════════════════════════════════════════════════════╗\n");
|
|
6587
|
+
process.stderr.write("║ Gemini CLI OAuth Authentication ║\n");
|
|
6588
|
+
process.stderr.write("╠═══════════════════════════════════════════════════════════════════════════╣\n");
|
|
6589
|
+
process.stderr.write("║ 1. Copy the auth URL shown below and open it in your browser ║\n");
|
|
6590
|
+
process.stderr.write("║ 2. Sign in with your Google account ║\n");
|
|
6591
|
+
process.stderr.write(`║ 3. After authentication, the browser will redirect to localhost:${port} ║\n`);
|
|
6592
|
+
process.stderr.write("║ 4. The callback will be captured automatically (port is forwarded) ║\n");
|
|
6593
|
+
process.stderr.write("╚═══════════════════════════════════════════════════════════════════════════╝\n");
|
|
6594
|
+
process.stderr.write("\n");
|
|
6595
|
+
});
|
|
6596
|
+
var runGeminiOauthLoginWithPrompt = (cwd, accountPath, options) => Effect.scoped(Effect.gen(function* (_) {
|
|
6597
|
+
yield* _(printOauthInstructions());
|
|
6598
|
+
const proc = yield* _(startDockerProcess(yield* _(CommandExecutor.CommandExecutor), buildDockerGeminiAuthSpec(cwd, yield* _(resolveDockerVolumeHostPath(cwd, accountPath)), options.image, options.containerPath)));
|
|
6599
|
+
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));
|
|
6605
|
+
return yield* _(resolveGeminiLoginResult(resultBox.value, exitCode));
|
|
6606
|
+
}));
|
|
6607
|
+
//#endregion
|
|
6608
|
+
//#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
|
+
var authGeminiLogin = (command, apiKey) => {
|
|
6699
|
+
const accountLabel = normalizeAccountLabel(command.label, "default");
|
|
6700
|
+
return withGeminiAuth(command, ({ accountPath, fs }) => Effect.gen(function* (_) {
|
|
6701
|
+
const apiKeyFilePath = geminiApiKeyPath(accountPath);
|
|
6702
|
+
yield* _(fs.writeFileString(apiKeyFilePath, `${apiKey.trim()}\n`));
|
|
6703
|
+
yield* _(fs.chmod(apiKeyFilePath, 384), Effect.orElseSucceed(() => void 0));
|
|
6704
|
+
})).pipe(Effect.zipRight(autoSyncState(`chore(state): auth gemini ${accountLabel}`)));
|
|
6705
|
+
};
|
|
6706
|
+
var authGeminiLoginCli = (_command) => Effect.gen(function* (_) {
|
|
6707
|
+
yield* _(Effect.log("Gemini CLI supports two authentication methods:"));
|
|
6708
|
+
yield* _(Effect.log(""));
|
|
6709
|
+
yield* _(Effect.log("1. API Key (recommended for simplicity):"));
|
|
6710
|
+
yield* _(Effect.log(" - Go to https://ai.google.dev/aistudio"));
|
|
6711
|
+
yield* _(Effect.log(" - Create or retrieve your API key"));
|
|
6712
|
+
yield* _(Effect.log(" - Use: docker-git menu -> Auth profiles -> Gemini CLI: set API key"));
|
|
6713
|
+
yield* _(Effect.log(""));
|
|
6714
|
+
yield* _(Effect.log("2. OAuth (Sign in with Google):"));
|
|
6715
|
+
yield* _(Effect.log(" - Use: docker-git menu -> Auth profiles -> Gemini CLI: login via OAuth"));
|
|
6716
|
+
yield* _(Effect.log(" - Follow the prompts to authenticate with your Google account"));
|
|
6717
|
+
});
|
|
6718
|
+
var authGeminiLoginOauth = (command) => {
|
|
6719
|
+
const accountLabel = normalizeAccountLabel(command.label, "default");
|
|
6720
|
+
return withGeminiAuth(command, ({ accountPath, cwd, fs }) => Effect.gen(function* (_) {
|
|
6721
|
+
const credentialsDir = geminiCredentialsPath(accountPath);
|
|
6722
|
+
yield* _(fs.makeDirectory(credentialsDir, { recursive: true }));
|
|
6723
|
+
yield* _(runGeminiOauthLoginWithPrompt(cwd, accountPath, {
|
|
6724
|
+
image: geminiImageName,
|
|
6725
|
+
containerPath: geminiContainerHomeDir
|
|
6726
|
+
}));
|
|
6727
|
+
}), { buildImage: true }).pipe(Effect.zipRight(autoSyncState(`chore(state): auth gemini oauth ${accountLabel}`)));
|
|
6728
|
+
};
|
|
6729
|
+
var authGeminiStatus = (command) => withGeminiAuth(command, ({ accountLabel, accountPath, fs }) => Effect.gen(function* (_) {
|
|
6730
|
+
const authMethod = yield* _(resolveGeminiAuthMethod(fs, accountPath));
|
|
6731
|
+
if (authMethod === "none") {
|
|
6732
|
+
yield* _(Effect.log(`Gemini not connected (${accountLabel}).`));
|
|
6733
|
+
return;
|
|
6734
|
+
}
|
|
6735
|
+
yield* _(Effect.log(`Gemini connected (${accountLabel}, ${authMethod}).`));
|
|
6736
|
+
}));
|
|
6737
|
+
var authGeminiLogout = (command) => Effect.gen(function* (_) {
|
|
6738
|
+
const accountLabel = normalizeAccountLabel(command.label, "default");
|
|
6739
|
+
yield* _(withGeminiAuth(command, ({ accountPath, fs }) => Effect.gen(function* (_) {
|
|
6740
|
+
yield* _(fs.remove(geminiApiKeyPath(accountPath), { force: true }));
|
|
6741
|
+
yield* _(fs.remove(geminiEnvFilePath(accountPath), { force: true }));
|
|
6742
|
+
yield* _(fs.remove(geminiCredentialsPath(accountPath), {
|
|
6743
|
+
recursive: true,
|
|
6744
|
+
force: true
|
|
6745
|
+
}));
|
|
6746
|
+
})));
|
|
6747
|
+
yield* _(autoSyncState(`chore(state): auth gemini logout ${accountLabel}`));
|
|
6748
|
+
}).pipe(Effect.asVoid);
|
|
6749
|
+
//#endregion
|
|
6352
6750
|
//#region ../lib/src/usecases/state-repo-github.ts
|
|
6353
6751
|
var dotDockerGitRepoName = ".docker-git";
|
|
6354
6752
|
var defaultStateRef = "main";
|
|
@@ -7646,10 +8044,12 @@ var normalizeLabel$1 = (value) => {
|
|
|
7646
8044
|
var defaultEnvGlobalPath = ".docker-git/.orch/env/global.env";
|
|
7647
8045
|
var defaultCodexAuthPath = ".docker-git/.orch/auth/codex";
|
|
7648
8046
|
var defaultClaudeAuthPath = ".docker-git/.orch/auth/claude";
|
|
8047
|
+
var defaultGeminiAuthPath = ".docker-git/.orch/auth/gemini";
|
|
7649
8048
|
var resolveAuthOptions = (raw) => ({
|
|
7650
8049
|
envGlobalPath: raw.envGlobalPath ?? defaultEnvGlobalPath,
|
|
7651
8050
|
codexAuthPath: raw.codexAuthPath ?? defaultCodexAuthPath,
|
|
7652
8051
|
claudeAuthPath: defaultClaudeAuthPath,
|
|
8052
|
+
geminiAuthPath: defaultGeminiAuthPath,
|
|
7653
8053
|
label: normalizeLabel$1(raw.label),
|
|
7654
8054
|
token: normalizeLabel$1(raw.token),
|
|
7655
8055
|
scopes: normalizeLabel$1(raw.scopes),
|
|
@@ -7695,7 +8095,20 @@ var buildClaudeCommand = (action, options) => Match.value(action).pipe(Match.whe
|
|
|
7695
8095
|
label: options.label,
|
|
7696
8096
|
claudeAuthPath: options.claudeAuthPath
|
|
7697
8097
|
})), Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`))));
|
|
7698
|
-
var
|
|
8098
|
+
var buildGeminiCommand = (action, options) => Match.value(action).pipe(Match.when("login", () => Either.right({
|
|
8099
|
+
_tag: "AuthGeminiLogin",
|
|
8100
|
+
label: options.label,
|
|
8101
|
+
geminiAuthPath: options.geminiAuthPath
|
|
8102
|
+
})), Match.when("status", () => Either.right({
|
|
8103
|
+
_tag: "AuthGeminiStatus",
|
|
8104
|
+
label: options.label,
|
|
8105
|
+
geminiAuthPath: options.geminiAuthPath
|
|
8106
|
+
})), Match.when("logout", () => Either.right({
|
|
8107
|
+
_tag: "AuthGeminiLogout",
|
|
8108
|
+
label: options.label,
|
|
8109
|
+
geminiAuthPath: options.geminiAuthPath
|
|
8110
|
+
})), Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`))));
|
|
8111
|
+
var buildAuthCommand = (provider, action, options) => Match.value(provider).pipe(Match.when("github", () => buildGithubCommand(action, options)), Match.when("gh", () => buildGithubCommand(action, options)), Match.when("codex", () => buildCodexCommand(action, options)), Match.when("claude", () => buildClaudeCommand(action, options)), Match.when("cc", () => buildClaudeCommand(action, options)), Match.when("gemini", () => buildGeminiCommand(action, options)), Match.orElse(() => Either.left(invalidArgument("auth provider", `unknown provider '${provider}'`))));
|
|
7699
8112
|
var parseAuth = (args) => {
|
|
7700
8113
|
if (args.length < 2) return Either.left(missingArgument(args.length === 0 ? "auth provider" : "auth action"));
|
|
7701
8114
|
const provider = args[0] ?? "";
|
|
@@ -7802,13 +8215,15 @@ var buildDefaultPathConfig = (normalizedSecretsRoot) => normalizedSecretsRoot ==
|
|
|
7802
8215
|
authorizedKeysPath: defaultTemplateConfig.authorizedKeysPath,
|
|
7803
8216
|
envGlobalPath: defaultTemplateConfig.envGlobalPath,
|
|
7804
8217
|
envProjectPath: defaultTemplateConfig.envProjectPath,
|
|
7805
|
-
codexAuthPath: defaultTemplateConfig.codexAuthPath
|
|
8218
|
+
codexAuthPath: defaultTemplateConfig.codexAuthPath,
|
|
8219
|
+
geminiAuthPath: defaultTemplateConfig.geminiAuthPath
|
|
7806
8220
|
} : {
|
|
7807
8221
|
dockerGitPath: defaultTemplateConfig.dockerGitPath,
|
|
7808
8222
|
authorizedKeysPath: defaultTemplateConfig.authorizedKeysPath,
|
|
7809
8223
|
envGlobalPath: `${normalizedSecretsRoot}/global.env`,
|
|
7810
8224
|
envProjectPath: defaultTemplateConfig.envProjectPath,
|
|
7811
|
-
codexAuthPath: `${normalizedSecretsRoot}/codex
|
|
8225
|
+
codexAuthPath: `${normalizedSecretsRoot}/codex`,
|
|
8226
|
+
geminiAuthPath: `${normalizedSecretsRoot}/gemini`
|
|
7812
8227
|
};
|
|
7813
8228
|
var resolvePaths = (raw, repoPath) => Either.gen(function* (_) {
|
|
7814
8229
|
const defaults = buildDefaultPathConfig(resolveNormalizedSecretsRoot(raw.secretsRoot));
|
|
@@ -7825,6 +8240,8 @@ var resolvePaths = (raw, repoPath) => Either.gen(function* (_) {
|
|
|
7825
8240
|
codexAuthPath,
|
|
7826
8241
|
codexSharedAuthPath: codexAuthPath,
|
|
7827
8242
|
codexHome: yield* _(nonEmpty("--codex-home", raw.codexHome, defaultTemplateConfig.codexHome)),
|
|
8243
|
+
geminiAuthPath: defaults.geminiAuthPath,
|
|
8244
|
+
geminiHome: defaultTemplateConfig.geminiHome,
|
|
7828
8245
|
outDir: yield* _(nonEmpty("--out-dir", raw.outDir, `.docker-git/${repoPath}`))
|
|
7829
8246
|
};
|
|
7830
8247
|
});
|
|
@@ -7854,6 +8271,8 @@ var buildTemplateConfig = ({ agentAuto, agentMode, claudeAuthLabel, codexAuthLab
|
|
|
7854
8271
|
codexAuthPath: paths.codexAuthPath,
|
|
7855
8272
|
codexSharedAuthPath: paths.codexSharedAuthPath,
|
|
7856
8273
|
codexHome: paths.codexHome,
|
|
8274
|
+
geminiAuthPath: paths.geminiAuthPath,
|
|
8275
|
+
geminiHome: paths.geminiHome,
|
|
7857
8276
|
cpuLimit,
|
|
7858
8277
|
ramLimit,
|
|
7859
8278
|
dockerNetworkMode,
|
|
@@ -8935,6 +9354,12 @@ var countAuthAccountDirectories = (fs, path, root) => Effect.gen(function* (_) {
|
|
|
8935
9354
|
return count;
|
|
8936
9355
|
});
|
|
8937
9356
|
//#endregion
|
|
9357
|
+
//#region src/docker-git/menu-auth-snapshot-builder.ts
|
|
9358
|
+
var countAuthAccountEntries = (fs, path, claudeAuthPath, geminiAuthPath) => pipe(Effect.all({
|
|
9359
|
+
claudeAuthEntries: countAuthAccountDirectories(fs, path, claudeAuthPath),
|
|
9360
|
+
geminiAuthEntries: countAuthAccountDirectories(fs, path, geminiAuthPath)
|
|
9361
|
+
}));
|
|
9362
|
+
//#endregion
|
|
8938
9363
|
//#region src/docker-git/menu-labeled-env.ts
|
|
8939
9364
|
var normalizeLabel = (value) => {
|
|
8940
9365
|
const trimmed = value.trim();
|
|
@@ -8983,6 +9408,18 @@ var authMenuItems = [
|
|
|
8983
9408
|
action: "ClaudeLogout",
|
|
8984
9409
|
label: "Claude Code: logout (clear cache)"
|
|
8985
9410
|
},
|
|
9411
|
+
{
|
|
9412
|
+
action: "GeminiOauth",
|
|
9413
|
+
label: "Gemini CLI: login via OAuth (Google account)"
|
|
9414
|
+
},
|
|
9415
|
+
{
|
|
9416
|
+
action: "GeminiApiKey",
|
|
9417
|
+
label: "Gemini CLI: set API key"
|
|
9418
|
+
},
|
|
9419
|
+
{
|
|
9420
|
+
action: "GeminiLogout",
|
|
9421
|
+
label: "Gemini CLI: logout (clear credentials)"
|
|
9422
|
+
},
|
|
8986
9423
|
{
|
|
8987
9424
|
action: "Refresh",
|
|
8988
9425
|
label: "Refresh snapshot"
|
|
@@ -9042,34 +9479,62 @@ var flowSteps$1 = {
|
|
|
9042
9479
|
label: "Label to logout (empty = default)",
|
|
9043
9480
|
required: false,
|
|
9044
9481
|
secret: false
|
|
9482
|
+
}],
|
|
9483
|
+
GeminiOauth: [{
|
|
9484
|
+
key: "label",
|
|
9485
|
+
label: "Label (empty = default)",
|
|
9486
|
+
required: false,
|
|
9487
|
+
secret: false
|
|
9488
|
+
}],
|
|
9489
|
+
GeminiApiKey: [{
|
|
9490
|
+
key: "label",
|
|
9491
|
+
label: "Label (empty = default)",
|
|
9492
|
+
required: false,
|
|
9493
|
+
secret: false
|
|
9494
|
+
}, {
|
|
9495
|
+
key: "apiKey",
|
|
9496
|
+
label: "Gemini API key (from ai.google.dev)",
|
|
9497
|
+
required: true,
|
|
9498
|
+
secret: true
|
|
9499
|
+
}],
|
|
9500
|
+
GeminiLogout: [{
|
|
9501
|
+
key: "label",
|
|
9502
|
+
label: "Label to logout (empty = default)",
|
|
9503
|
+
required: false,
|
|
9504
|
+
secret: false
|
|
9045
9505
|
}]
|
|
9046
9506
|
};
|
|
9047
|
-
var flowTitle = (flow) => Match.value(flow).pipe(Match.when("GithubOauth", () => "GitHub OAuth"), Match.when("GithubRemove", () => "GitHub remove"), Match.when("GitSet", () => "Git credentials"), Match.when("GitRemove", () => "Git remove"), Match.when("ClaudeOauth", () => "Claude Code OAuth"), Match.when("ClaudeLogout", () => "Claude Code logout"), Match.exhaustive);
|
|
9048
|
-
var successMessage$1 = (flow, label) => Match.value(flow).pipe(Match.when("GithubOauth", () => `Saved GitHub token (${label}).`), Match.when("GithubRemove", () => `Removed GitHub token (${label}).`), Match.when("GitSet", () => `Saved Git credentials (${label}).`), Match.when("GitRemove", () => `Removed Git credentials (${label}).`), Match.when("ClaudeOauth", () => `Saved Claude Code login (${label}).`), Match.when("ClaudeLogout", () => `Logged out Claude Code (${label}).`), Match.exhaustive);
|
|
9507
|
+
var flowTitle = (flow) => Match.value(flow).pipe(Match.when("GithubOauth", () => "GitHub OAuth"), Match.when("GithubRemove", () => "GitHub remove"), Match.when("GitSet", () => "Git credentials"), Match.when("GitRemove", () => "Git remove"), Match.when("ClaudeOauth", () => "Claude Code OAuth"), Match.when("ClaudeLogout", () => "Claude Code logout"), Match.when("GeminiOauth", () => "Gemini CLI OAuth"), Match.when("GeminiApiKey", () => "Gemini CLI API key"), Match.when("GeminiLogout", () => "Gemini CLI logout"), Match.exhaustive);
|
|
9508
|
+
var successMessage$1 = (flow, label) => Match.value(flow).pipe(Match.when("GithubOauth", () => `Saved GitHub token (${label}).`), Match.when("GithubRemove", () => `Removed GitHub token (${label}).`), Match.when("GitSet", () => `Saved Git credentials (${label}).`), Match.when("GitRemove", () => `Removed Git credentials (${label}).`), Match.when("ClaudeOauth", () => `Saved Claude Code login (${label}).`), Match.when("ClaudeLogout", () => `Logged out Claude Code (${label}).`), Match.when("GeminiOauth", () => `Saved Gemini CLI OAuth login (${label}).`), Match.when("GeminiApiKey", () => `Saved Gemini API key (${label}).`), Match.when("GeminiLogout", () => `Logged out Gemini CLI (${label}).`), Match.exhaustive);
|
|
9049
9509
|
var buildGlobalEnvPath$1 = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/env/global.env`;
|
|
9050
9510
|
var buildClaudeAuthPath$1 = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/claude`;
|
|
9511
|
+
var buildGeminiAuthPath$1 = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/gemini`;
|
|
9051
9512
|
var loadAuthEnvText = (cwd) => Effect.gen(function* (_) {
|
|
9052
9513
|
const fs = yield* _(FileSystem.FileSystem);
|
|
9053
9514
|
const path = yield* _(Path.Path);
|
|
9054
9515
|
const globalEnvPath = buildGlobalEnvPath$1(cwd);
|
|
9055
9516
|
const claudeAuthPath = buildClaudeAuthPath$1(cwd);
|
|
9517
|
+
const geminiAuthPath = buildGeminiAuthPath$1(cwd);
|
|
9056
9518
|
yield* _(ensureEnvFile$1(fs, path, globalEnvPath));
|
|
9057
9519
|
return {
|
|
9058
9520
|
fs,
|
|
9059
9521
|
path,
|
|
9060
9522
|
globalEnvPath,
|
|
9061
9523
|
claudeAuthPath,
|
|
9524
|
+
geminiAuthPath,
|
|
9062
9525
|
envText: yield* _(readEnvText(fs, globalEnvPath))
|
|
9063
9526
|
};
|
|
9064
9527
|
});
|
|
9065
|
-
var readAuthSnapshot = (cwd) => pipe(loadAuthEnvText(cwd), Effect.flatMap(({ claudeAuthPath, envText, fs, globalEnvPath, path }) =>
|
|
9528
|
+
var readAuthSnapshot = (cwd) => pipe(loadAuthEnvText(cwd), Effect.flatMap(({ claudeAuthPath, envText, fs, geminiAuthPath, globalEnvPath, path }) => countAuthAccountEntries(fs, path, claudeAuthPath, geminiAuthPath).pipe(Effect.map(({ claudeAuthEntries, geminiAuthEntries }) => ({
|
|
9066
9529
|
globalEnvPath,
|
|
9067
9530
|
claudeAuthPath,
|
|
9531
|
+
geminiAuthPath,
|
|
9068
9532
|
totalEntries: parseEnvEntries(envText).filter((entry) => entry.value.trim().length > 0).length,
|
|
9069
9533
|
githubTokenEntries: countKeyEntries(envText, "GITHUB_TOKEN"),
|
|
9070
9534
|
gitTokenEntries: countKeyEntries(envText, "GIT_AUTH_TOKEN"),
|
|
9071
9535
|
gitUserEntries: countKeyEntries(envText, "GIT_AUTH_USER"),
|
|
9072
|
-
claudeAuthEntries
|
|
9536
|
+
claudeAuthEntries,
|
|
9537
|
+
geminiAuthEntries
|
|
9073
9538
|
})))));
|
|
9074
9539
|
var writeAuthFlow = (cwd, flow, values) => pipe(loadAuthEnvText(cwd), Effect.flatMap(({ envText, fs, globalEnvPath }) => {
|
|
9075
9540
|
const label = values["label"] ?? "";
|
|
@@ -9098,6 +9563,67 @@ var authMenuActionByIndex = (index) => {
|
|
|
9098
9563
|
};
|
|
9099
9564
|
var authMenuSize = () => authMenuItems.length;
|
|
9100
9565
|
//#endregion
|
|
9566
|
+
//#region src/docker-git/menu-auth-effects.ts
|
|
9567
|
+
var resolveLabelOption = (values) => {
|
|
9568
|
+
const labelValue = (values["label"] ?? "").trim();
|
|
9569
|
+
return labelValue.length > 0 ? labelValue : null;
|
|
9570
|
+
};
|
|
9571
|
+
var resolveGithubOauthEffect = (labelOption, globalEnvPath) => authGithubLogin({
|
|
9572
|
+
_tag: "AuthGithubLogin",
|
|
9573
|
+
label: labelOption,
|
|
9574
|
+
token: null,
|
|
9575
|
+
scopes: null,
|
|
9576
|
+
envGlobalPath: globalEnvPath
|
|
9577
|
+
});
|
|
9578
|
+
var resolveClaudeOauthEffect = (labelOption) => authClaudeLogin({
|
|
9579
|
+
_tag: "AuthClaudeLogin",
|
|
9580
|
+
label: labelOption,
|
|
9581
|
+
claudeAuthPath: claudeAuthRoot
|
|
9582
|
+
});
|
|
9583
|
+
var resolveClaudeLogoutEffect = (labelOption) => authClaudeLogout({
|
|
9584
|
+
_tag: "AuthClaudeLogout",
|
|
9585
|
+
label: labelOption,
|
|
9586
|
+
claudeAuthPath: claudeAuthRoot
|
|
9587
|
+
});
|
|
9588
|
+
var resolveGeminiOauthEffect = (labelOption) => authGeminiLoginOauth({
|
|
9589
|
+
_tag: "AuthGeminiLogin",
|
|
9590
|
+
label: labelOption,
|
|
9591
|
+
geminiAuthPath: geminiAuthRoot
|
|
9592
|
+
});
|
|
9593
|
+
var resolveGeminiApiKeyEffect = (labelOption, apiKey) => authGeminiLogin({
|
|
9594
|
+
_tag: "AuthGeminiLogin",
|
|
9595
|
+
label: labelOption,
|
|
9596
|
+
geminiAuthPath: geminiAuthRoot
|
|
9597
|
+
}, apiKey);
|
|
9598
|
+
var resolveGeminiLogoutEffect = (labelOption) => authGeminiLogout({
|
|
9599
|
+
_tag: "AuthGeminiLogout",
|
|
9600
|
+
label: labelOption,
|
|
9601
|
+
geminiAuthPath: geminiAuthRoot
|
|
9602
|
+
});
|
|
9603
|
+
var resolveAuthPromptEffect = (view, cwd, values) => {
|
|
9604
|
+
const labelOption = resolveLabelOption(values);
|
|
9605
|
+
return Match.value(view.flow).pipe(Match.when("GithubOauth", () => resolveGithubOauthEffect(labelOption, view.snapshot.globalEnvPath)), Match.when("ClaudeOauth", () => resolveClaudeOauthEffect(labelOption)), Match.when("ClaudeLogout", () => resolveClaudeLogoutEffect(labelOption)), Match.when("GeminiOauth", () => resolveGeminiOauthEffect(labelOption)), Match.when("GeminiApiKey", () => resolveGeminiApiKeyEffect(labelOption, (values["apiKey"] ?? "").trim())), Match.when("GeminiLogout", () => resolveGeminiLogoutEffect(labelOption)), Match.when("GithubRemove", (flow) => writeAuthFlow(cwd, flow, values)), Match.when("GitSet", (flow) => writeAuthFlow(cwd, flow, values)), Match.when("GitRemove", (flow) => writeAuthFlow(cwd, flow, values)), Match.exhaustive);
|
|
9606
|
+
};
|
|
9607
|
+
var startAuthMenuWithSnapshot = (snapshot, context) => {
|
|
9608
|
+
context.setView({
|
|
9609
|
+
_tag: "AuthMenu",
|
|
9610
|
+
selected: 0,
|
|
9611
|
+
snapshot
|
|
9612
|
+
});
|
|
9613
|
+
context.setMessage(null);
|
|
9614
|
+
};
|
|
9615
|
+
var runAuthPromptEffect = (effect, view, label, context, options) => {
|
|
9616
|
+
const withOptionalSuspension = options.suspendTui ? withSuspendedTui(effect, {
|
|
9617
|
+
onError: pauseOnError(renderError),
|
|
9618
|
+
onResume: resumeSshWithSkipInputs(context)
|
|
9619
|
+
}) : effect;
|
|
9620
|
+
context.setSshActive(options.suspendTui);
|
|
9621
|
+
context.runner.runEffect(pipe(withOptionalSuspension, Effect.zipRight(readAuthSnapshot(context.cwd)), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9622
|
+
startAuthMenuWithSnapshot(snapshot, context);
|
|
9623
|
+
context.setMessage(successMessage$1(view.flow, label));
|
|
9624
|
+
})), Effect.asVoid));
|
|
9625
|
+
};
|
|
9626
|
+
//#endregion
|
|
9101
9627
|
//#region src/docker-git/menu-input-utils.ts
|
|
9102
9628
|
var parseMenuIndex = (input) => {
|
|
9103
9629
|
const trimmed = input.trim();
|
|
@@ -9154,14 +9680,6 @@ var defaultLabel = (value) => {
|
|
|
9154
9680
|
const trimmed = value.trim();
|
|
9155
9681
|
return trimmed.length > 0 ? trimmed : "default";
|
|
9156
9682
|
};
|
|
9157
|
-
var startAuthMenuWithSnapshot = (snapshot, context) => {
|
|
9158
|
-
context.setView({
|
|
9159
|
-
_tag: "AuthMenu",
|
|
9160
|
-
selected: 0,
|
|
9161
|
-
snapshot
|
|
9162
|
-
});
|
|
9163
|
-
context.setMessage(null);
|
|
9164
|
-
};
|
|
9165
9683
|
var startAuthPrompt = (snapshot, flow, context) => {
|
|
9166
9684
|
context.setView({
|
|
9167
9685
|
_tag: "AuthPrompt",
|
|
@@ -9173,39 +9691,6 @@ var startAuthPrompt = (snapshot, flow, context) => {
|
|
|
9173
9691
|
});
|
|
9174
9692
|
context.setMessage(null);
|
|
9175
9693
|
};
|
|
9176
|
-
var resolveLabelOption = (values) => {
|
|
9177
|
-
const labelValue = (values["label"] ?? "").trim();
|
|
9178
|
-
return labelValue.length > 0 ? labelValue : null;
|
|
9179
|
-
};
|
|
9180
|
-
var resolveAuthPromptEffect = (view, cwd, values) => {
|
|
9181
|
-
const labelOption = resolveLabelOption(values);
|
|
9182
|
-
return Match.value(view.flow).pipe(Match.when("GithubOauth", () => authGithubLogin({
|
|
9183
|
-
_tag: "AuthGithubLogin",
|
|
9184
|
-
label: labelOption,
|
|
9185
|
-
token: null,
|
|
9186
|
-
scopes: null,
|
|
9187
|
-
envGlobalPath: view.snapshot.globalEnvPath
|
|
9188
|
-
})), Match.when("ClaudeOauth", () => authClaudeLogin({
|
|
9189
|
-
_tag: "AuthClaudeLogin",
|
|
9190
|
-
label: labelOption,
|
|
9191
|
-
claudeAuthPath: claudeAuthRoot
|
|
9192
|
-
})), Match.when("ClaudeLogout", () => authClaudeLogout({
|
|
9193
|
-
_tag: "AuthClaudeLogout",
|
|
9194
|
-
label: labelOption,
|
|
9195
|
-
claudeAuthPath: claudeAuthRoot
|
|
9196
|
-
})), Match.when("GithubRemove", (flow) => writeAuthFlow(cwd, flow, values)), Match.when("GitSet", (flow) => writeAuthFlow(cwd, flow, values)), Match.when("GitRemove", (flow) => writeAuthFlow(cwd, flow, values)), Match.exhaustive);
|
|
9197
|
-
};
|
|
9198
|
-
var runAuthPromptEffect = (effect, view, label, context, options) => {
|
|
9199
|
-
const withOptionalSuspension = options.suspendTui ? withSuspendedTui(effect, {
|
|
9200
|
-
onError: pauseOnError(renderError),
|
|
9201
|
-
onResume: resumeSshWithSkipInputs(context)
|
|
9202
|
-
}) : effect;
|
|
9203
|
-
context.setSshActive(options.suspendTui);
|
|
9204
|
-
context.runner.runEffect(pipe(withOptionalSuspension, Effect.zipRight(readAuthSnapshot(context.state.cwd)), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9205
|
-
startAuthMenuWithSnapshot(snapshot, context);
|
|
9206
|
-
context.setMessage(successMessage$1(view.flow, label));
|
|
9207
|
-
})), Effect.asVoid));
|
|
9208
|
-
};
|
|
9209
9694
|
var loadAuthMenuView = (cwd, context) => pipe(readAuthSnapshot(cwd), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9210
9695
|
startAuthMenuWithSnapshot(snapshot, context);
|
|
9211
9696
|
})), Effect.asVoid);
|
|
@@ -9225,7 +9710,10 @@ var submitAuthPrompt = (view, context) => {
|
|
|
9225
9710
|
startAuthMenuWithSnapshot(view.snapshot, context);
|
|
9226
9711
|
}, (nextValues) => {
|
|
9227
9712
|
const label = defaultLabel(nextValues["label"] ?? "");
|
|
9228
|
-
runAuthPromptEffect(resolveAuthPromptEffect(view, context.state.cwd, nextValues), view, label,
|
|
9713
|
+
runAuthPromptEffect(resolveAuthPromptEffect(view, context.state.cwd, nextValues), view, label, {
|
|
9714
|
+
...context,
|
|
9715
|
+
cwd: context.state.cwd
|
|
9716
|
+
}, { suspendTui: view.flow === "GithubOauth" || view.flow === "ClaudeOauth" || view.flow === "ClaudeLogout" || view.flow === "GeminiOauth" });
|
|
9229
9717
|
});
|
|
9230
9718
|
};
|
|
9231
9719
|
var setAuthMenuSelection = (view, selected, context) => {
|
|
@@ -9267,6 +9755,15 @@ var handleAuthMenuInput = (input, key, view, context) => {
|
|
|
9267
9755
|
}
|
|
9268
9756
|
handleAuthMenuNumberInput(input, view, context);
|
|
9269
9757
|
};
|
|
9758
|
+
var setAuthPromptBuffer = (args) => {
|
|
9759
|
+
const { context, input, key, view } = args;
|
|
9760
|
+
const nextBuffer = nextBufferValue(input, key, view.buffer);
|
|
9761
|
+
if (nextBuffer === null) return;
|
|
9762
|
+
context.setView({
|
|
9763
|
+
...view,
|
|
9764
|
+
buffer: nextBuffer
|
|
9765
|
+
});
|
|
9766
|
+
};
|
|
9270
9767
|
var handleAuthPromptInput = (input, key, view, context) => {
|
|
9271
9768
|
if (key.escape) {
|
|
9272
9769
|
startAuthMenuWithSnapshot(view.snapshot, context);
|
|
@@ -9283,15 +9780,6 @@ var handleAuthPromptInput = (input, key, view, context) => {
|
|
|
9283
9780
|
context
|
|
9284
9781
|
});
|
|
9285
9782
|
};
|
|
9286
|
-
var setAuthPromptBuffer = (args) => {
|
|
9287
|
-
const { context, input, key, view } = args;
|
|
9288
|
-
const nextBuffer = nextBufferValue(input, key, view.buffer);
|
|
9289
|
-
if (nextBuffer === null) return;
|
|
9290
|
-
context.setView({
|
|
9291
|
-
...view,
|
|
9292
|
-
buffer: nextBuffer
|
|
9293
|
-
});
|
|
9294
|
-
};
|
|
9295
9783
|
var openAuthMenu = (context) => {
|
|
9296
9784
|
context.setMessage("Loading auth profiles...");
|
|
9297
9785
|
context.runner.runEffect(loadAuthMenuView(context.state.cwd, context));
|
|
@@ -9384,15 +9872,17 @@ var loadRuntimeByProject = (items) => pipe(runDockerPsNames(process.cwd()), Effe
|
|
|
9384
9872
|
}));
|
|
9385
9873
|
var runtimeForSelection = (view, selected) => view.runtimeByProject[selected.projectDir] ?? stoppedRuntime$1();
|
|
9386
9874
|
//#endregion
|
|
9875
|
+
//#region src/docker-git/menu-project-auth-helpers.ts
|
|
9876
|
+
var hasFileAtPath = (fs, filePath) => Effect.gen(function* (_) {
|
|
9877
|
+
if (!(yield* _(fs.exists(filePath)))) return false;
|
|
9878
|
+
return (yield* _(fs.stat(filePath))).type === "File";
|
|
9879
|
+
});
|
|
9880
|
+
//#endregion
|
|
9387
9881
|
//#region src/docker-git/menu-project-auth-claude.ts
|
|
9388
9882
|
var oauthTokenFileName = ".oauth-token";
|
|
9389
9883
|
var legacyConfigFileName = ".config.json";
|
|
9390
9884
|
var credentialsFileName = ".credentials.json";
|
|
9391
9885
|
var nestedCredentialsFileName = ".claude/.credentials.json";
|
|
9392
|
-
var hasFileAtPath = (fs, filePath) => Effect.gen(function* (_) {
|
|
9393
|
-
if (!(yield* _(fs.exists(filePath)))) return false;
|
|
9394
|
-
return (yield* _(fs.stat(filePath))).type === "File";
|
|
9395
|
-
});
|
|
9396
9886
|
var hasNonEmptyOauthToken = (fs, tokenPath) => Effect.gen(function* (_) {
|
|
9397
9887
|
if (!(yield* _(hasFileAtPath(fs, tokenPath)))) return false;
|
|
9398
9888
|
return (yield* _(fs.readFileString(tokenPath), Effect.orElseSucceed(() => ""))).trim().length > 0;
|
|
@@ -9419,6 +9909,107 @@ var hasClaudeAccountCredentials = (fs, accountPath) => hasFileAtPath(fs, `${acco
|
|
|
9419
9909
|
}));
|
|
9420
9910
|
}));
|
|
9421
9911
|
//#endregion
|
|
9912
|
+
//#region src/docker-git/menu-project-auth-gemini.ts
|
|
9913
|
+
var apiKeyFileName = ".api-key";
|
|
9914
|
+
var envFileName = ".env";
|
|
9915
|
+
var geminiCredentialsDir = ".gemini";
|
|
9916
|
+
var hasNonEmptyApiKey = (fs, apiKeyPath) => Effect.gen(function* (_) {
|
|
9917
|
+
if (!(yield* _(hasFileAtPath(fs, apiKeyPath)))) return false;
|
|
9918
|
+
return (yield* _(fs.readFileString(apiKeyPath), Effect.orElseSucceed(() => ""))).trim().length > 0;
|
|
9919
|
+
});
|
|
9920
|
+
var hasApiKeyInEnvFile = (fs, envFilePath) => Effect.gen(function* (_) {
|
|
9921
|
+
if (!(yield* _(hasFileAtPath(fs, envFilePath)))) return false;
|
|
9922
|
+
const lines = (yield* _(fs.readFileString(envFilePath), Effect.orElseSucceed(() => ""))).split("\n");
|
|
9923
|
+
for (const line of lines) {
|
|
9924
|
+
const trimmed = line.trim();
|
|
9925
|
+
if (trimmed.startsWith("GEMINI_API_KEY=")) {
|
|
9926
|
+
if (trimmed.slice(15).replaceAll(/^['"]|['"]$/g, "").trim().length > 0) return true;
|
|
9927
|
+
}
|
|
9928
|
+
}
|
|
9929
|
+
return false;
|
|
9930
|
+
});
|
|
9931
|
+
var geminiOauthCredentialFiles = [
|
|
9932
|
+
"oauth-tokens.json",
|
|
9933
|
+
"credentials.json",
|
|
9934
|
+
"application_default_credentials.json"
|
|
9935
|
+
];
|
|
9936
|
+
var checkAnyFileExists = (fs, basePath, fileNames) => {
|
|
9937
|
+
const [first, ...rest] = fileNames;
|
|
9938
|
+
if (first === void 0) return Effect.succeed(false);
|
|
9939
|
+
return hasFileAtPath(fs, `${basePath}/${first}`).pipe(Effect.flatMap((exists) => exists ? Effect.succeed(true) : checkAnyFileExists(fs, basePath, rest)));
|
|
9940
|
+
};
|
|
9941
|
+
var hasOauthCredentials = (fs, accountPath) => {
|
|
9942
|
+
const credentialsDir = `${accountPath}/${geminiCredentialsDir}`;
|
|
9943
|
+
return hasFileAtPath(fs, credentialsDir).pipe(Effect.flatMap((dirExists) => dirExists ? checkAnyFileExists(fs, credentialsDir, geminiOauthCredentialFiles) : Effect.succeed(false)));
|
|
9944
|
+
};
|
|
9945
|
+
var hasGeminiAccountCredentials = (fs, accountPath) => hasNonEmptyApiKey(fs, `${accountPath}/${apiKeyFileName}`).pipe(Effect.flatMap((hasApiKey) => {
|
|
9946
|
+
if (hasApiKey) return Effect.succeed(true);
|
|
9947
|
+
return hasApiKeyInEnvFile(fs, `${accountPath}/${envFileName}`).pipe(Effect.flatMap((hasEnvApiKey) => {
|
|
9948
|
+
if (hasEnvApiKey) return Effect.succeed(true);
|
|
9949
|
+
return hasOauthCredentials(fs, accountPath);
|
|
9950
|
+
}));
|
|
9951
|
+
}));
|
|
9952
|
+
//#endregion
|
|
9953
|
+
//#region src/docker-git/menu-project-auth-flows.ts
|
|
9954
|
+
var githubTokenBaseKey$1 = "GITHUB_TOKEN";
|
|
9955
|
+
var gitTokenBaseKey$1 = "GIT_AUTH_TOKEN";
|
|
9956
|
+
var gitUserBaseKey = "GIT_AUTH_USER";
|
|
9957
|
+
var projectGithubLabelKey$1 = "GITHUB_AUTH_LABEL";
|
|
9958
|
+
var projectGitLabelKey$1 = "GIT_AUTH_LABEL";
|
|
9959
|
+
var projectClaudeLabelKey$1 = "CLAUDE_AUTH_LABEL";
|
|
9960
|
+
var projectGeminiLabelKey$1 = "GEMINI_AUTH_LABEL";
|
|
9961
|
+
var defaultGitUser = "x-access-token";
|
|
9962
|
+
var missingSecret = (provider, label, envPath) => new AuthError({ message: `${provider} not connected: label '${label}' not found in ${envPath}` });
|
|
9963
|
+
var clearProjectGitLabels = (envText) => {
|
|
9964
|
+
return upsertEnvKey(upsertEnvKey(upsertEnvKey(envText, "GH_TOKEN", ""), projectGitLabelKey$1, ""), projectGithubLabelKey$1, "");
|
|
9965
|
+
};
|
|
9966
|
+
var updateProjectGithubConnect = (spec) => {
|
|
9967
|
+
const key = buildLabeledEnvKey(githubTokenBaseKey$1, spec.rawLabel);
|
|
9968
|
+
const token = findEnvValue(spec.globalEnvText, key);
|
|
9969
|
+
if (token === null) return Effect.fail(missingSecret("GitHub token", spec.canonicalLabel, spec.globalEnvPath));
|
|
9970
|
+
const withoutGitLabel = upsertEnvKey(upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", token), "GH_TOKEN", token), projectGitLabelKey$1, "");
|
|
9971
|
+
return Effect.succeed(upsertEnvKey(withoutGitLabel, projectGithubLabelKey$1, spec.canonicalLabel));
|
|
9972
|
+
};
|
|
9973
|
+
var updateProjectGithubDisconnect = (spec) => {
|
|
9974
|
+
const withoutGitToken = upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", "");
|
|
9975
|
+
return Effect.succeed(clearProjectGitLabels(withoutGitToken));
|
|
9976
|
+
};
|
|
9977
|
+
var updateProjectGitConnect = (spec) => {
|
|
9978
|
+
const tokenKey = buildLabeledEnvKey(gitTokenBaseKey$1, spec.rawLabel);
|
|
9979
|
+
const userKey = buildLabeledEnvKey(gitUserBaseKey, spec.rawLabel);
|
|
9980
|
+
const token = findEnvValue(spec.globalEnvText, tokenKey);
|
|
9981
|
+
if (token === null) return Effect.fail(missingSecret("Git credentials", spec.canonicalLabel, spec.globalEnvPath));
|
|
9982
|
+
const defaultUser = findEnvValue(spec.globalEnvText, gitUserBaseKey) ?? defaultGitUser;
|
|
9983
|
+
const user = findEnvValue(spec.globalEnvText, userKey) ?? defaultUser;
|
|
9984
|
+
const withGitLabel = upsertEnvKey(upsertEnvKey(upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", token), "GIT_AUTH_USER", user), "GH_TOKEN", token), projectGitLabelKey$1, spec.canonicalLabel);
|
|
9985
|
+
return Effect.succeed(upsertEnvKey(withGitLabel, projectGithubLabelKey$1, spec.canonicalLabel));
|
|
9986
|
+
};
|
|
9987
|
+
var updateProjectGitDisconnect = (spec) => {
|
|
9988
|
+
const withoutUser = upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", ""), "GIT_AUTH_USER", "");
|
|
9989
|
+
return Effect.succeed(clearProjectGitLabels(withoutUser));
|
|
9990
|
+
};
|
|
9991
|
+
var resolveAccountCandidates = (authPath, accountLabel) => accountLabel === "default" ? [`${authPath}/default`, authPath] : [`${authPath}/${accountLabel}`];
|
|
9992
|
+
var findFirstCredentialsMatch = (fs, candidates, hasCredentials) => Effect.gen(function* (_) {
|
|
9993
|
+
for (const accountPath of candidates) {
|
|
9994
|
+
if (!(yield* _(fs.exists(accountPath)))) continue;
|
|
9995
|
+
if (yield* _(hasCredentials(fs, accountPath), Effect.orElseSucceed(() => false))) return accountPath;
|
|
9996
|
+
}
|
|
9997
|
+
return null;
|
|
9998
|
+
});
|
|
9999
|
+
var updateProjectClaudeConnect = (spec) => {
|
|
10000
|
+
const accountLabel = normalizeAccountLabel(spec.rawLabel, "default");
|
|
10001
|
+
const accountCandidates = resolveAccountCandidates(spec.claudeAuthPath, accountLabel);
|
|
10002
|
+
return findFirstCredentialsMatch(spec.fs, accountCandidates, hasClaudeAccountCredentials).pipe(Effect.flatMap((matched) => matched === null ? Effect.fail(missingSecret("Claude Code login", spec.canonicalLabel, spec.claudeAuthPath)) : Effect.succeed(upsertEnvKey(spec.projectEnvText, projectClaudeLabelKey$1, spec.canonicalLabel))));
|
|
10003
|
+
};
|
|
10004
|
+
var updateProjectClaudeDisconnect = (spec) => Effect.succeed(upsertEnvKey(spec.projectEnvText, projectClaudeLabelKey$1, ""));
|
|
10005
|
+
var updateProjectGeminiConnect = (spec) => {
|
|
10006
|
+
const accountLabel = normalizeAccountLabel(spec.rawLabel, "default");
|
|
10007
|
+
const accountCandidates = resolveAccountCandidates(spec.geminiAuthPath, accountLabel);
|
|
10008
|
+
return findFirstCredentialsMatch(spec.fs, accountCandidates, hasGeminiAccountCredentials).pipe(Effect.flatMap((matched) => matched === null ? Effect.fail(missingSecret("Gemini CLI API key", spec.canonicalLabel, spec.geminiAuthPath)) : Effect.succeed(upsertEnvKey(spec.projectEnvText, projectGeminiLabelKey$1, spec.canonicalLabel))));
|
|
10009
|
+
};
|
|
10010
|
+
var updateProjectGeminiDisconnect = (spec) => Effect.succeed(upsertEnvKey(spec.projectEnvText, projectGeminiLabelKey$1, ""));
|
|
10011
|
+
var resolveProjectEnvUpdate = (flow, spec) => Match.value(flow).pipe(Match.when("ProjectGithubConnect", () => updateProjectGithubConnect(spec)), Match.when("ProjectGithubDisconnect", () => updateProjectGithubDisconnect(spec)), Match.when("ProjectGitConnect", () => updateProjectGitConnect(spec)), Match.when("ProjectGitDisconnect", () => updateProjectGitDisconnect(spec)), Match.when("ProjectClaudeConnect", () => updateProjectClaudeConnect(spec)), Match.when("ProjectClaudeDisconnect", () => updateProjectClaudeDisconnect(spec)), Match.when("ProjectGeminiConnect", () => updateProjectGeminiConnect(spec)), Match.when("ProjectGeminiDisconnect", () => updateProjectGeminiDisconnect(spec)), Match.exhaustive);
|
|
10012
|
+
//#endregion
|
|
9422
10013
|
//#region src/docker-git/menu-project-auth-data.ts
|
|
9423
10014
|
var projectAuthMenuItems = [
|
|
9424
10015
|
{
|
|
@@ -9445,6 +10036,14 @@ var projectAuthMenuItems = [
|
|
|
9445
10036
|
action: "ProjectClaudeDisconnect",
|
|
9446
10037
|
label: "Project: Claude disconnect"
|
|
9447
10038
|
},
|
|
10039
|
+
{
|
|
10040
|
+
action: "ProjectGeminiConnect",
|
|
10041
|
+
label: "Project: Gemini connect label"
|
|
10042
|
+
},
|
|
10043
|
+
{
|
|
10044
|
+
action: "ProjectGeminiDisconnect",
|
|
10045
|
+
label: "Project: Gemini disconnect"
|
|
10046
|
+
},
|
|
9448
10047
|
{
|
|
9449
10048
|
action: "Refresh",
|
|
9450
10049
|
label: "Refresh snapshot"
|
|
@@ -9475,7 +10074,14 @@ var flowSteps = {
|
|
|
9475
10074
|
required: false,
|
|
9476
10075
|
secret: false
|
|
9477
10076
|
}],
|
|
9478
|
-
ProjectClaudeDisconnect: []
|
|
10077
|
+
ProjectClaudeDisconnect: [],
|
|
10078
|
+
ProjectGeminiConnect: [{
|
|
10079
|
+
key: "label",
|
|
10080
|
+
label: "Label (empty = default)",
|
|
10081
|
+
required: false,
|
|
10082
|
+
secret: false
|
|
10083
|
+
}],
|
|
10084
|
+
ProjectGeminiDisconnect: []
|
|
9479
10085
|
};
|
|
9480
10086
|
var resolveCanonicalLabel = (value) => {
|
|
9481
10087
|
const normalized = normalizeLabel(value);
|
|
@@ -9483,18 +10089,19 @@ var resolveCanonicalLabel = (value) => {
|
|
|
9483
10089
|
};
|
|
9484
10090
|
var githubTokenBaseKey = "GITHUB_TOKEN";
|
|
9485
10091
|
var gitTokenBaseKey = "GIT_AUTH_TOKEN";
|
|
9486
|
-
var gitUserBaseKey = "GIT_AUTH_USER";
|
|
9487
10092
|
var projectGithubLabelKey = "GITHUB_AUTH_LABEL";
|
|
9488
10093
|
var projectGitLabelKey = "GIT_AUTH_LABEL";
|
|
9489
10094
|
var projectClaudeLabelKey = "CLAUDE_AUTH_LABEL";
|
|
9490
|
-
var
|
|
10095
|
+
var projectGeminiLabelKey = "GEMINI_AUTH_LABEL";
|
|
9491
10096
|
var buildGlobalEnvPath = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/env/global.env`;
|
|
9492
10097
|
var buildClaudeAuthPath = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/claude`;
|
|
10098
|
+
var buildGeminiAuthPath = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/gemini`;
|
|
9493
10099
|
var loadProjectAuthEnvText = (project) => Effect.gen(function* (_) {
|
|
9494
10100
|
const fs = yield* _(FileSystem.FileSystem);
|
|
9495
10101
|
const path = yield* _(Path.Path);
|
|
9496
10102
|
const globalEnvPath = buildGlobalEnvPath(process.cwd());
|
|
9497
10103
|
const claudeAuthPath = buildClaudeAuthPath(process.cwd());
|
|
10104
|
+
const geminiAuthPath = buildGeminiAuthPath(process.cwd());
|
|
9498
10105
|
yield* _(ensureEnvFile$1(fs, path, globalEnvPath));
|
|
9499
10106
|
yield* _(ensureEnvFile$1(fs, path, project.envProjectPath));
|
|
9500
10107
|
const globalEnvText = yield* _(readEnvText(fs, globalEnvPath));
|
|
@@ -9505,69 +10112,29 @@ var loadProjectAuthEnvText = (project) => Effect.gen(function* (_) {
|
|
|
9505
10112
|
globalEnvPath,
|
|
9506
10113
|
projectEnvPath: project.envProjectPath,
|
|
9507
10114
|
claudeAuthPath,
|
|
10115
|
+
geminiAuthPath,
|
|
9508
10116
|
globalEnvText,
|
|
9509
10117
|
projectEnvText
|
|
9510
10118
|
};
|
|
9511
10119
|
});
|
|
9512
|
-
var readProjectAuthSnapshot = (project) => pipe(loadProjectAuthEnvText(project), Effect.flatMap(({ claudeAuthPath, fs, globalEnvPath, globalEnvText, path, projectEnvPath, projectEnvText }) =>
|
|
10120
|
+
var readProjectAuthSnapshot = (project) => pipe(loadProjectAuthEnvText(project), Effect.flatMap(({ claudeAuthPath, fs, geminiAuthPath, globalEnvPath, globalEnvText, path, projectEnvPath, projectEnvText }) => countAuthAccountEntries(fs, path, claudeAuthPath, geminiAuthPath).pipe(Effect.map(({ claudeAuthEntries, geminiAuthEntries }) => ({
|
|
9513
10121
|
projectDir: project.projectDir,
|
|
9514
10122
|
projectName: project.displayName,
|
|
9515
10123
|
envGlobalPath: globalEnvPath,
|
|
9516
10124
|
envProjectPath: projectEnvPath,
|
|
9517
10125
|
claudeAuthPath,
|
|
10126
|
+
geminiAuthPath,
|
|
9518
10127
|
githubTokenEntries: countKeyEntries(globalEnvText, githubTokenBaseKey),
|
|
9519
10128
|
gitTokenEntries: countKeyEntries(globalEnvText, gitTokenBaseKey),
|
|
9520
10129
|
claudeAuthEntries,
|
|
10130
|
+
geminiAuthEntries,
|
|
9521
10131
|
activeGithubLabel: findEnvValue(projectEnvText, projectGithubLabelKey),
|
|
9522
10132
|
activeGitLabel: findEnvValue(projectEnvText, projectGitLabelKey),
|
|
9523
|
-
activeClaudeLabel: findEnvValue(projectEnvText, projectClaudeLabelKey)
|
|
10133
|
+
activeClaudeLabel: findEnvValue(projectEnvText, projectClaudeLabelKey),
|
|
10134
|
+
activeGeminiLabel: findEnvValue(projectEnvText, projectGeminiLabelKey)
|
|
9524
10135
|
})))));
|
|
9525
|
-
var
|
|
9526
|
-
var
|
|
9527
|
-
const key = buildLabeledEnvKey(githubTokenBaseKey, spec.rawLabel);
|
|
9528
|
-
const token = findEnvValue(spec.globalEnvText, key);
|
|
9529
|
-
if (token === null) return Effect.fail(missingSecret("GitHub token", spec.canonicalLabel, spec.globalEnvPath));
|
|
9530
|
-
const withoutGitLabel = upsertEnvKey(upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", token), "GH_TOKEN", token), projectGitLabelKey, "");
|
|
9531
|
-
return Effect.succeed(upsertEnvKey(withoutGitLabel, projectGithubLabelKey, spec.canonicalLabel));
|
|
9532
|
-
};
|
|
9533
|
-
var clearProjectGitLabels = (envText) => {
|
|
9534
|
-
return upsertEnvKey(upsertEnvKey(upsertEnvKey(envText, "GH_TOKEN", ""), projectGitLabelKey, ""), projectGithubLabelKey, "");
|
|
9535
|
-
};
|
|
9536
|
-
var updateProjectGithubDisconnect = (spec) => {
|
|
9537
|
-
const withoutGitToken = upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", "");
|
|
9538
|
-
return Effect.succeed(clearProjectGitLabels(withoutGitToken));
|
|
9539
|
-
};
|
|
9540
|
-
var updateProjectGitConnect = (spec) => {
|
|
9541
|
-
const tokenKey = buildLabeledEnvKey(gitTokenBaseKey, spec.rawLabel);
|
|
9542
|
-
const userKey = buildLabeledEnvKey(gitUserBaseKey, spec.rawLabel);
|
|
9543
|
-
const token = findEnvValue(spec.globalEnvText, tokenKey);
|
|
9544
|
-
if (token === null) return Effect.fail(missingSecret("Git credentials", spec.canonicalLabel, spec.globalEnvPath));
|
|
9545
|
-
const defaultUser = findEnvValue(spec.globalEnvText, gitUserBaseKey) ?? defaultGitUser;
|
|
9546
|
-
const user = findEnvValue(spec.globalEnvText, userKey) ?? defaultUser;
|
|
9547
|
-
const withGitLabel = upsertEnvKey(upsertEnvKey(upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", token), "GIT_AUTH_USER", user), "GH_TOKEN", token), projectGitLabelKey, spec.canonicalLabel);
|
|
9548
|
-
return Effect.succeed(upsertEnvKey(withGitLabel, projectGithubLabelKey, spec.canonicalLabel));
|
|
9549
|
-
};
|
|
9550
|
-
var updateProjectGitDisconnect = (spec) => {
|
|
9551
|
-
const withoutUser = upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", ""), "GIT_AUTH_USER", "");
|
|
9552
|
-
return Effect.succeed(clearProjectGitLabels(withoutUser));
|
|
9553
|
-
};
|
|
9554
|
-
var resolveClaudeAccountCandidates = (claudeAuthPath, accountLabel) => accountLabel === "default" ? [`${claudeAuthPath}/default`, claudeAuthPath] : [`${claudeAuthPath}/${accountLabel}`];
|
|
9555
|
-
var updateProjectClaudeConnect = (spec) => {
|
|
9556
|
-
const accountLabel = normalizeAccountLabel(spec.rawLabel, "default");
|
|
9557
|
-
const accountCandidates = resolveClaudeAccountCandidates(spec.claudeAuthPath, accountLabel);
|
|
9558
|
-
return Effect.gen(function* (_) {
|
|
9559
|
-
for (const accountPath of accountCandidates) {
|
|
9560
|
-
if (!(yield* _(spec.fs.exists(accountPath)))) continue;
|
|
9561
|
-
if (yield* _(hasClaudeAccountCredentials(spec.fs, accountPath), Effect.orElseSucceed(() => false))) return upsertEnvKey(spec.projectEnvText, projectClaudeLabelKey, spec.canonicalLabel);
|
|
9562
|
-
}
|
|
9563
|
-
return yield* _(Effect.fail(missingSecret("Claude Code login", spec.canonicalLabel, spec.claudeAuthPath)));
|
|
9564
|
-
});
|
|
9565
|
-
};
|
|
9566
|
-
var updateProjectClaudeDisconnect = (spec) => {
|
|
9567
|
-
return Effect.succeed(upsertEnvKey(spec.projectEnvText, projectClaudeLabelKey, ""));
|
|
9568
|
-
};
|
|
9569
|
-
var resolveProjectEnvUpdate = (flow, spec) => Match.value(flow).pipe(Match.when("ProjectGithubConnect", () => updateProjectGithubConnect(spec)), Match.when("ProjectGithubDisconnect", () => updateProjectGithubDisconnect(spec)), Match.when("ProjectGitConnect", () => updateProjectGitConnect(spec)), Match.when("ProjectGitDisconnect", () => updateProjectGitDisconnect(spec)), Match.when("ProjectClaudeConnect", () => updateProjectClaudeConnect(spec)), Match.when("ProjectClaudeDisconnect", () => updateProjectClaudeDisconnect(spec)), Match.exhaustive);
|
|
9570
|
-
var writeProjectAuthFlow = (project, flow, values) => pipe(loadProjectAuthEnvText(project), Effect.flatMap(({ claudeAuthPath, fs, globalEnvPath, globalEnvText, projectEnvPath, projectEnvText }) => {
|
|
10136
|
+
var resolveSyncMessage = (flow, canonicalLabel, displayName) => Match.value(flow).pipe(Match.when("ProjectGithubConnect", () => `chore(state): project auth gh ${canonicalLabel} ${displayName}`), Match.when("ProjectGithubDisconnect", () => `chore(state): project auth gh logout ${displayName}`), Match.when("ProjectGitConnect", () => `chore(state): project auth git ${canonicalLabel} ${displayName}`), Match.when("ProjectGitDisconnect", () => `chore(state): project auth git logout ${displayName}`), Match.when("ProjectClaudeConnect", () => `chore(state): project auth claude ${canonicalLabel} ${displayName}`), Match.when("ProjectClaudeDisconnect", () => `chore(state): project auth claude logout ${displayName}`), Match.when("ProjectGeminiConnect", () => `chore(state): project auth gemini ${canonicalLabel} ${displayName}`), Match.when("ProjectGeminiDisconnect", () => `chore(state): project auth gemini logout ${displayName}`), Match.exhaustive);
|
|
10137
|
+
var writeProjectAuthFlow = (project, flow, values) => pipe(loadProjectAuthEnvText(project), Effect.flatMap(({ claudeAuthPath, fs, geminiAuthPath, globalEnvPath, globalEnvText, projectEnvPath, projectEnvText }) => {
|
|
9571
10138
|
const rawLabel = values["label"] ?? "";
|
|
9572
10139
|
const canonicalLabel = resolveCanonicalLabel(rawLabel);
|
|
9573
10140
|
const nextProjectEnv = resolveProjectEnvUpdate(flow, {
|
|
@@ -9577,9 +10144,10 @@ var writeProjectAuthFlow = (project, flow, values) => pipe(loadProjectAuthEnvTex
|
|
|
9577
10144
|
globalEnvPath,
|
|
9578
10145
|
globalEnvText,
|
|
9579
10146
|
projectEnvText,
|
|
9580
|
-
claudeAuthPath
|
|
10147
|
+
claudeAuthPath,
|
|
10148
|
+
geminiAuthPath
|
|
9581
10149
|
});
|
|
9582
|
-
const syncMessage =
|
|
10150
|
+
const syncMessage = resolveSyncMessage(flow, canonicalLabel, project.displayName);
|
|
9583
10151
|
return pipe(nextProjectEnv, Effect.flatMap((nextText) => fs.writeFileString(projectEnvPath, nextText)), Effect.zipRight(autoSyncState(syncMessage)));
|
|
9584
10152
|
}), Effect.asVoid);
|
|
9585
10153
|
var projectAuthViewSteps = (flow) => flowSteps[flow];
|
|
@@ -9615,7 +10183,7 @@ var startProjectAuthPrompt = (project, snapshot, flow, context) => {
|
|
|
9615
10183
|
var loadProjectAuthMenuView = (project, context) => pipe(readProjectAuthSnapshot(project), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9616
10184
|
startProjectAuthMenu(project, snapshot, context);
|
|
9617
10185
|
})), Effect.asVoid);
|
|
9618
|
-
var successMessage = (flow, label) => Match.value(flow).pipe(Match.when("ProjectGithubConnect", () => `Connected GitHub label (${label}) to project.`), Match.when("ProjectGithubDisconnect", () => "Disconnected GitHub from project."), Match.when("ProjectGitConnect", () => `Connected Git label (${label}) to project.`), Match.when("ProjectGitDisconnect", () => "Disconnected Git from project."), Match.when("ProjectClaudeConnect", () => `Connected Claude label (${label}) to project.`), Match.when("ProjectClaudeDisconnect", () => "Disconnected Claude from project."), Match.exhaustive);
|
|
10186
|
+
var successMessage = (flow, label) => Match.value(flow).pipe(Match.when("ProjectGithubConnect", () => `Connected GitHub label (${label}) to project.`), Match.when("ProjectGithubDisconnect", () => "Disconnected GitHub from project."), Match.when("ProjectGitConnect", () => `Connected Git label (${label}) to project.`), Match.when("ProjectGitDisconnect", () => "Disconnected Git from project."), Match.when("ProjectClaudeConnect", () => `Connected Claude label (${label}) to project.`), Match.when("ProjectClaudeDisconnect", () => "Disconnected Claude from project."), Match.when("ProjectGeminiConnect", () => `Connected Gemini label (${label}) to project.`), Match.when("ProjectGeminiDisconnect", () => "Disconnected Gemini from project."), Match.exhaustive);
|
|
9619
10187
|
var runProjectAuthEffect = (project, flow, values, label, context) => {
|
|
9620
10188
|
context.runner.runEffect(pipe(writeProjectAuthFlow(project, flow, values), Effect.zipRight(readProjectAuthSnapshot(project)), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9621
10189
|
startProjectAuthMenu(project, snapshot, context);
|
|
@@ -9640,7 +10208,7 @@ var runProjectAuthAction$1 = (action, view, context) => {
|
|
|
9640
10208
|
context.runner.runEffect(loadProjectAuthMenuView(view.project, context));
|
|
9641
10209
|
return;
|
|
9642
10210
|
}
|
|
9643
|
-
if (action === "ProjectGithubDisconnect" || action === "ProjectGitDisconnect" || action === "ProjectClaudeDisconnect") {
|
|
10211
|
+
if (action === "ProjectGithubDisconnect" || action === "ProjectGitDisconnect" || action === "ProjectClaudeDisconnect" || action === "ProjectGeminiDisconnect") {
|
|
9644
10212
|
runProjectAuthEffect(view.project, action, {}, "default", context);
|
|
9645
10213
|
return;
|
|
9646
10214
|
}
|
|
@@ -10810,7 +11378,7 @@ var setExitCode = (code) => Effect.sync(() => {
|
|
|
10810
11378
|
});
|
|
10811
11379
|
var logWarningAndExit = (error) => pipe(Effect.logWarning(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
|
|
10812
11380
|
var logErrorAndExit = (error) => pipe(Effect.logError(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
|
|
10813
|
-
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)), Match.when({ _tag: "
|
|
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);
|
|
10814
11382
|
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({
|
|
10815
11383
|
onFailure: (error) => isParseError(error) ? logErrorAndExit(error) : pipe(Effect.logError(renderError(error)), Effect.flatMap(() => Effect.fail(error))),
|
|
10816
11384
|
onSuccess: () => Effect.void
|