@prover-coder-ai/docker-git 1.0.42 → 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 +1002 -268
- 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
|
});
|
|
@@ -1211,6 +1217,38 @@ var ensureGhAuthImage = (fs, path, cwd, buildLabel) => ensureDockerImage(fs, pat
|
|
|
1211
1217
|
buildLabel
|
|
1212
1218
|
});
|
|
1213
1219
|
//#endregion
|
|
1220
|
+
//#region ../lib/src/usecases/github-api-helpers.ts
|
|
1221
|
+
/**
|
|
1222
|
+
* Run `gh api <args>` inside the auth Docker container and return trimmed stdout.
|
|
1223
|
+
*
|
|
1224
|
+
* @pure false
|
|
1225
|
+
* @effect CommandExecutor (Docker)
|
|
1226
|
+
* @invariant exits with CommandFailedError on non-zero exit code
|
|
1227
|
+
* @complexity O(1)
|
|
1228
|
+
*/
|
|
1229
|
+
var runGhApiCapture = (cwd, hostPath, token, args) => runDockerAuthCapture(buildDockerAuthSpec({
|
|
1230
|
+
cwd,
|
|
1231
|
+
image: ghImageName,
|
|
1232
|
+
hostPath,
|
|
1233
|
+
containerPath: ghAuthDir,
|
|
1234
|
+
env: `GH_TOKEN=${token}`,
|
|
1235
|
+
args: ["api", ...args],
|
|
1236
|
+
interactive: false
|
|
1237
|
+
}), [0], (exitCode) => new CommandFailedError({
|
|
1238
|
+
command: `gh api ${args.join(" ")}`,
|
|
1239
|
+
exitCode
|
|
1240
|
+
})).pipe(Effect.map((raw) => raw.trim()));
|
|
1241
|
+
/**
|
|
1242
|
+
* Like `runGhApiCapture` but returns `null` instead of failing on API errors
|
|
1243
|
+
* (e.g. HTTP 404 / non-zero exit code).
|
|
1244
|
+
*
|
|
1245
|
+
* @pure false
|
|
1246
|
+
* @effect CommandExecutor (Docker)
|
|
1247
|
+
* @invariant never fails — errors become null
|
|
1248
|
+
* @complexity O(1)
|
|
1249
|
+
*/
|
|
1250
|
+
var runGhApiNullable = (cwd, hostPath, token, args) => runGhApiCapture(cwd, hostPath, token, args).pipe(Effect.catchTag("CommandFailedError", () => Effect.succeed("")), Effect.map((raw) => raw.length === 0 ? null : raw));
|
|
1251
|
+
//#endregion
|
|
1214
1252
|
//#region ../lib/src/usecases/runtime.ts
|
|
1215
1253
|
var withFsPathContext = (run) => Effect.gen(function* (_) {
|
|
1216
1254
|
return yield* _(run({
|
|
@@ -1228,20 +1266,7 @@ var resolveGithubToken$1 = (envText) => {
|
|
|
1228
1266
|
const labeled = entries.find((entry) => entry.key.startsWith("GITHUB_TOKEN__"));
|
|
1229
1267
|
return labeled && labeled.value.trim().length > 0 ? labeled.value.trim() : null;
|
|
1230
1268
|
};
|
|
1231
|
-
var
|
|
1232
|
-
cwd,
|
|
1233
|
-
image: ghImageName,
|
|
1234
|
-
hostPath,
|
|
1235
|
-
containerPath: ghAuthDir,
|
|
1236
|
-
env: `GH_TOKEN=${token}`,
|
|
1237
|
-
args: ["api", ...args],
|
|
1238
|
-
interactive: false
|
|
1239
|
-
}), [0], (exitCode) => new CommandFailedError({
|
|
1240
|
-
command: `gh api ${args.join(" ")}`,
|
|
1241
|
-
exitCode
|
|
1242
|
-
})).pipe(Effect.map((raw) => raw.trim()));
|
|
1243
|
-
var runGhApiCloneUrl = (cwd, hostPath, token, args) => runGhApiCapture(cwd, hostPath, token, args).pipe(Effect.catchTag("CommandFailedError", () => Effect.succeed("")), Effect.map((raw) => raw.length === 0 ? null : raw));
|
|
1244
|
-
var resolveViewerLogin = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
1269
|
+
var resolveViewerLogin$1 = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
1245
1270
|
const command = "gh api /user --jq .login";
|
|
1246
1271
|
const raw = yield* _(runGhApiCapture(cwd, hostPath, token, [
|
|
1247
1272
|
"/user",
|
|
@@ -1254,12 +1279,12 @@ var resolveViewerLogin = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
|
1254
1279
|
})));
|
|
1255
1280
|
return raw;
|
|
1256
1281
|
});
|
|
1257
|
-
var resolveRepoCloneUrl = (cwd, hostPath, token, fullName) =>
|
|
1282
|
+
var resolveRepoCloneUrl = (cwd, hostPath, token, fullName) => runGhApiNullable(cwd, hostPath, token, [
|
|
1258
1283
|
`/repos/${fullName}`,
|
|
1259
1284
|
"--jq",
|
|
1260
1285
|
".clone_url"
|
|
1261
1286
|
]);
|
|
1262
|
-
var createFork = (cwd, hostPath, token, owner, repo) =>
|
|
1287
|
+
var createFork = (cwd, hostPath, token, owner, repo) => runGhApiNullable(cwd, hostPath, token, [
|
|
1263
1288
|
"-X",
|
|
1264
1289
|
"POST",
|
|
1265
1290
|
`/repos/${owner}/${repo}/forks`,
|
|
@@ -1277,7 +1302,7 @@ var resolveGithubForkUrl = (repoUrl, envGlobalPath) => withFsPathContext(({ cwd,
|
|
|
1277
1302
|
const ghRoot = resolvePathFromCwd(path, cwd, ghAuthRoot);
|
|
1278
1303
|
yield* _(fs.makeDirectory(ghRoot, { recursive: true }));
|
|
1279
1304
|
yield* _(ensureGhAuthImage(fs, path, cwd, "gh api"));
|
|
1280
|
-
const viewer = yield* _(resolveViewerLogin(cwd, ghRoot, token));
|
|
1305
|
+
const viewer = yield* _(resolveViewerLogin$1(cwd, ghRoot, token));
|
|
1281
1306
|
if (viewer.toLowerCase() === repo.owner.toLowerCase()) return null;
|
|
1282
1307
|
const existingFork = yield* _(resolveRepoCloneUrl(cwd, ghRoot, token, `${viewer}/${repo.repo}`));
|
|
1283
1308
|
if (existingFork !== null) return existingFork;
|
|
@@ -1338,6 +1363,9 @@ var TemplateConfigSchema = Schema.Struct({
|
|
|
1338
1363
|
codexAuthPath: Schema.String,
|
|
1339
1364
|
codexSharedAuthPath: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.codexSharedAuthPath }),
|
|
1340
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 }),
|
|
1341
1369
|
cpuLimit: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.cpuLimit }),
|
|
1342
1370
|
ramLimit: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.ramLimit }),
|
|
1343
1371
|
dockerNetworkMode: Schema.optionalWith(Schema.Literal("shared", "project"), { default: () => defaultTemplateConfig.dockerNetworkMode }),
|
|
@@ -1588,28 +1616,15 @@ var resolveComposeResourceLimits = (template, hostResources) => {
|
|
|
1588
1616
|
//#region ../lib/src/usecases/resource-limits.ts
|
|
1589
1617
|
var resolveTemplateResourceLimits = (template) => Effect.succeed(withDefaultResourceLimitIntent(template));
|
|
1590
1618
|
//#endregion
|
|
1591
|
-
//#region ../lib/src/usecases/state-repo/env.ts
|
|
1592
|
-
var isTruthyEnv = (value) => {
|
|
1593
|
-
const normalized = value.trim().toLowerCase();
|
|
1594
|
-
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
1595
|
-
};
|
|
1596
|
-
var isFalsyEnv = (value) => {
|
|
1597
|
-
const normalized = value.trim().toLowerCase();
|
|
1598
|
-
return normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off";
|
|
1599
|
-
};
|
|
1600
|
-
var autoSyncStrictEnvKey = "DOCKER_GIT_STATE_AUTO_SYNC_STRICT";
|
|
1601
|
-
var defaultSyncMessage = "chore(state): sync";
|
|
1602
|
-
var isAutoSyncEnabled = (envValue, hasRemote) => {
|
|
1603
|
-
if (envValue === void 0) return hasRemote;
|
|
1604
|
-
if (envValue.trim().length === 0) return hasRemote;
|
|
1605
|
-
if (isFalsyEnv(envValue)) return false;
|
|
1606
|
-
if (isTruthyEnv(envValue)) return true;
|
|
1607
|
-
return true;
|
|
1608
|
-
};
|
|
1609
|
-
//#endregion
|
|
1610
1619
|
//#region ../lib/src/usecases/state-repo/git-commands.ts
|
|
1611
1620
|
var successExitCode = Number(ExitCode(0));
|
|
1612
|
-
var gitBaseEnv$1 = {
|
|
1621
|
+
var gitBaseEnv$1 = {
|
|
1622
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1623
|
+
GIT_AUTHOR_NAME: "docker-git",
|
|
1624
|
+
GIT_AUTHOR_EMAIL: "docker-git@users.noreply.github.com",
|
|
1625
|
+
GIT_COMMITTER_NAME: "docker-git",
|
|
1626
|
+
GIT_COMMITTER_EMAIL: "docker-git@users.noreply.github.com"
|
|
1627
|
+
};
|
|
1613
1628
|
var git = (cwd, args, env = gitBaseEnv$1) => runCommandWithExitCodes({
|
|
1614
1629
|
cwd,
|
|
1615
1630
|
command: "git",
|
|
@@ -1641,6 +1656,68 @@ var hasOriginRemote = (root) => Effect.map(gitExitCode(root, [
|
|
|
1641
1656
|
"origin"
|
|
1642
1657
|
]), (exit) => exit === successExitCode);
|
|
1643
1658
|
//#endregion
|
|
1659
|
+
//#region ../lib/src/usecases/state-repo/adopt-remote.ts
|
|
1660
|
+
var adoptRemoteHistoryIfOrphan = (root, repoRef, env) => Effect.gen(function* (_) {
|
|
1661
|
+
const fetchExit = yield* _(gitExitCode(root, [
|
|
1662
|
+
"fetch",
|
|
1663
|
+
"origin",
|
|
1664
|
+
repoRef
|
|
1665
|
+
], env));
|
|
1666
|
+
if (fetchExit !== successExitCode) {
|
|
1667
|
+
yield* _(Effect.logWarning(`git fetch origin ${repoRef} failed (exit ${fetchExit}); starting fresh history`));
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
const remoteRef = `origin/${repoRef}`;
|
|
1671
|
+
if ((yield* _(gitExitCode(root, [
|
|
1672
|
+
"show-ref",
|
|
1673
|
+
"--verify",
|
|
1674
|
+
"--quiet",
|
|
1675
|
+
`refs/remotes/${remoteRef}`
|
|
1676
|
+
], env))) !== successExitCode) return;
|
|
1677
|
+
if ((yield* _(gitExitCode(root, ["rev-parse", "HEAD"], env))) !== successExitCode) {
|
|
1678
|
+
yield* _(git(root, ["reset", remoteRef], env));
|
|
1679
|
+
yield* _(gitExitCode(root, ["checkout-index", "--all"], env));
|
|
1680
|
+
yield* _(Effect.log(`Adopted remote history from ${remoteRef}`));
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
if ((yield* _(gitExitCode(root, [
|
|
1684
|
+
"merge-base",
|
|
1685
|
+
"HEAD",
|
|
1686
|
+
remoteRef
|
|
1687
|
+
], env))) === successExitCode) return;
|
|
1688
|
+
yield* _(Effect.logWarning(`Local history has no common ancestor with ${remoteRef}; merging unrelated histories`));
|
|
1689
|
+
if ((yield* _(gitExitCode(root, [
|
|
1690
|
+
"merge",
|
|
1691
|
+
"--allow-unrelated-histories",
|
|
1692
|
+
"--no-edit",
|
|
1693
|
+
remoteRef
|
|
1694
|
+
], env))) === successExitCode) {
|
|
1695
|
+
yield* _(Effect.log(`Merged unrelated histories from ${remoteRef}`));
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
yield* _(gitExitCode(root, ["merge", "--abort"], env));
|
|
1699
|
+
yield* _(Effect.logWarning(`Merge conflict with ${remoteRef}; sync will open a PR for manual resolution`));
|
|
1700
|
+
});
|
|
1701
|
+
//#endregion
|
|
1702
|
+
//#region ../lib/src/usecases/state-repo/env.ts
|
|
1703
|
+
var isTruthyEnv = (value) => {
|
|
1704
|
+
const normalized = value.trim().toLowerCase();
|
|
1705
|
+
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
1706
|
+
};
|
|
1707
|
+
var isFalsyEnv = (value) => {
|
|
1708
|
+
const normalized = value.trim().toLowerCase();
|
|
1709
|
+
return normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off";
|
|
1710
|
+
};
|
|
1711
|
+
var autoSyncStrictEnvKey = "DOCKER_GIT_STATE_AUTO_SYNC_STRICT";
|
|
1712
|
+
var defaultSyncMessage = "chore(state): sync";
|
|
1713
|
+
var isAutoSyncEnabled = (envValue, hasRemote) => {
|
|
1714
|
+
if (envValue === void 0) return hasRemote;
|
|
1715
|
+
if (envValue.trim().length === 0) return hasRemote;
|
|
1716
|
+
if (isFalsyEnv(envValue)) return false;
|
|
1717
|
+
if (isTruthyEnv(envValue)) return true;
|
|
1718
|
+
return true;
|
|
1719
|
+
};
|
|
1720
|
+
//#endregion
|
|
1644
1721
|
//#region ../lib/src/usecases/state-repo/github-auth.ts
|
|
1645
1722
|
var githubTokenKey = "GITHUB_TOKEN";
|
|
1646
1723
|
var githubHttpsRemoteRe = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/;
|
|
@@ -2901,6 +2978,143 @@ if [[ -f "$LEGACY_AGENTS_PATH" && -f "$AGENTS_PATH" ]]; then
|
|
|
2901
2978
|
fi`;
|
|
2902
2979
|
var renderEntrypointAgentsNotice = (config) => entrypointAgentsNoticeTemplate.replaceAll("__CODEX_HOME__", config.codexHome).replaceAll("__SSH_USER__", config.sshUser).replaceAll("__TARGET_DIR__", config.targetDir);
|
|
2903
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
|
|
2904
3118
|
//#region ../lib/src/core/templates-entrypoint/git.ts
|
|
2905
3119
|
var renderAuthLabelResolution = () => String.raw`# 2) Ensure GitHub auth vars are available for SSH sessions.
|
|
2906
3120
|
# Prefer a label-selected token (same selection model as clone/create) when present.
|
|
@@ -3803,6 +4017,7 @@ var renderEntrypoint = (config) => [
|
|
|
3803
4017
|
renderEntrypointDockerSocket(config),
|
|
3804
4018
|
renderEntrypointGitConfig(config),
|
|
3805
4019
|
renderEntrypointClaudeConfig(config),
|
|
4020
|
+
renderEntrypointGeminiConfig(config),
|
|
3806
4021
|
renderEntrypointGitHooks(),
|
|
3807
4022
|
renderEntrypointBackgroundTasks(config),
|
|
3808
4023
|
renderEntrypointBaseline(),
|
|
@@ -4359,7 +4574,7 @@ var resolveOriginPushTarget = (originUrl) => {
|
|
|
4359
4574
|
const trimmed = originUrl?.trim() ?? "";
|
|
4360
4575
|
return trimmed.length > 0 ? trimmed : "origin";
|
|
4361
4576
|
};
|
|
4362
|
-
var resolveSyncMessage = (value) => {
|
|
4577
|
+
var resolveSyncMessage$1 = (value) => {
|
|
4363
4578
|
const trimmed = value?.trim() ?? "";
|
|
4364
4579
|
return trimmed.length > 0 ? trimmed : defaultSyncMessage;
|
|
4365
4580
|
};
|
|
@@ -4378,7 +4593,7 @@ var commitAllIfNeeded = (root, message, env) => Effect.gen(function* (_) {
|
|
|
4378
4593
|
], env));
|
|
4379
4594
|
});
|
|
4380
4595
|
var sanitizeBranchComponent = (value) => value.trim().replaceAll(" ", "-").replaceAll(":", "-").replaceAll("..", "-").replaceAll("@{", "-").replaceAll("\\", "-").replaceAll("^", "-").replaceAll("~", "-");
|
|
4381
|
-
var
|
|
4596
|
+
var pullRemoteAndRestoreLocal = (root, baseBranch, env) => Effect.gen(function* (_) {
|
|
4382
4597
|
const fetchExit = yield* _(gitExitCode(root, [
|
|
4383
4598
|
"fetch",
|
|
4384
4599
|
"origin",
|
|
@@ -4393,10 +4608,26 @@ var rebaseOntoOriginIfPossible = (root, baseBranch, env) => Effect.gen(function*
|
|
|
4393
4608
|
"--verify",
|
|
4394
4609
|
"--quiet",
|
|
4395
4610
|
`refs/remotes/origin/${baseBranch}`
|
|
4396
|
-
], env))) !== successExitCode) return
|
|
4397
|
-
|
|
4398
|
-
yield* _(gitExitCode(root, ["
|
|
4399
|
-
|
|
4611
|
+
], env))) !== successExitCode) return;
|
|
4612
|
+
yield* _(git(root, ["add", "-A"], env));
|
|
4613
|
+
const stashExit = yield* _(gitExitCode(root, ["stash", "--include-untracked"], env));
|
|
4614
|
+
yield* _(git(root, [
|
|
4615
|
+
"reset",
|
|
4616
|
+
"--hard",
|
|
4617
|
+
`origin/${baseBranch}`
|
|
4618
|
+
], env));
|
|
4619
|
+
if (stashExit === successExitCode) {
|
|
4620
|
+
if ((yield* _(gitExitCode(root, ["stash", "pop"], env))) !== successExitCode) {
|
|
4621
|
+
yield* _(gitExitCode(root, [
|
|
4622
|
+
"checkout",
|
|
4623
|
+
"--theirs",
|
|
4624
|
+
"--",
|
|
4625
|
+
"."
|
|
4626
|
+
], env));
|
|
4627
|
+
yield* _(git(root, ["add", "-A"], env));
|
|
4628
|
+
yield* _(gitExitCode(root, ["stash", "drop"], env));
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4400
4631
|
});
|
|
4401
4632
|
var pushToNewBranch = (root, baseBranch, originPushTarget, env) => Effect.gen(function* (_) {
|
|
4402
4633
|
const headShort = yield* _(gitCapture$1(root, [
|
|
@@ -4422,15 +4653,9 @@ var getCurrentBranch = (root, env) => gitCapture$1(root, [
|
|
|
4422
4653
|
var runStateSyncOps = (root, originUrl, message, env, options) => Effect.gen(function* (_) {
|
|
4423
4654
|
const originPushTarget = resolveOriginPushTarget(options?.originPushUrlOverride ?? null);
|
|
4424
4655
|
yield* _(normalizeLegacyStateProjects(root));
|
|
4425
|
-
yield* _(commitAllIfNeeded(root, resolveSyncMessage(message), env));
|
|
4426
4656
|
const baseBranch = resolveBaseBranch(yield* _(getCurrentBranch(root, env)));
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
const compareUrl = tryBuildGithubCompareUrl(originUrl, baseBranch, prBranch);
|
|
4430
|
-
yield* _(Effect.logWarning(`State sync needs manual merge: pushed changes to branch '${prBranch}'.`));
|
|
4431
|
-
yield* _(logOpenPr(originUrl, baseBranch, prBranch, compareUrl));
|
|
4432
|
-
return;
|
|
4433
|
-
}
|
|
4657
|
+
yield* _(pullRemoteAndRestoreLocal(root, baseBranch, env));
|
|
4658
|
+
yield* _(commitAllIfNeeded(root, resolveSyncMessage$1(message), env));
|
|
4434
4659
|
const pushExit = yield* _(gitExitCode(root, [
|
|
4435
4660
|
"push",
|
|
4436
4661
|
"--no-verify",
|
|
@@ -4517,7 +4742,7 @@ var autoSyncState = (message) => Effect.gen(function* (_) {
|
|
|
4517
4742
|
onFailure: (error) => Effect.logWarning(`State auto-sync failed: ${String(error)}`),
|
|
4518
4743
|
onSuccess: () => Effect.void
|
|
4519
4744
|
}), Effect.asVoid);
|
|
4520
|
-
var cloneStateRepo = (root, input) => Effect.gen(function* (_) {
|
|
4745
|
+
var cloneStateRepo = (root, input, env) => Effect.gen(function* (_) {
|
|
4521
4746
|
const cloneBranchExit = yield* _(runCommandExitCode({
|
|
4522
4747
|
cwd: root,
|
|
4523
4748
|
command: "git",
|
|
@@ -4528,7 +4753,7 @@ var cloneStateRepo = (root, input) => Effect.gen(function* (_) {
|
|
|
4528
4753
|
input.repoUrl,
|
|
4529
4754
|
root
|
|
4530
4755
|
],
|
|
4531
|
-
env
|
|
4756
|
+
env
|
|
4532
4757
|
}));
|
|
4533
4758
|
if (cloneBranchExit === successExitCode) return;
|
|
4534
4759
|
yield* _(Effect.logWarning(`git clone --branch ${input.repoRef} failed (exit ${cloneBranchExit}); retrying without --branch`));
|
|
@@ -4540,58 +4765,63 @@ var cloneStateRepo = (root, input) => Effect.gen(function* (_) {
|
|
|
4540
4765
|
input.repoUrl,
|
|
4541
4766
|
root
|
|
4542
4767
|
],
|
|
4543
|
-
env
|
|
4768
|
+
env
|
|
4544
4769
|
}));
|
|
4545
4770
|
if (cloneDefaultExit !== successExitCode) return yield* _(Effect.fail(new CommandFailedError({
|
|
4546
4771
|
command: "git clone",
|
|
4547
4772
|
exitCode: cloneDefaultExit
|
|
4548
4773
|
})));
|
|
4549
4774
|
}).pipe(Effect.asVoid);
|
|
4550
|
-
var initRepoIfNeeded = (fs, path, root, input) => Effect.gen(function* (_) {
|
|
4775
|
+
var initRepoIfNeeded = (fs, path, root, input, env) => Effect.gen(function* (_) {
|
|
4551
4776
|
yield* _(fs.makeDirectory(root, { recursive: true }));
|
|
4552
4777
|
const gitDir = path.join(root, ".git");
|
|
4553
4778
|
if (yield* _(fs.exists(gitDir))) return;
|
|
4554
4779
|
if ((yield* _(fs.readDirectory(root))).length === 0) {
|
|
4555
|
-
yield* _(cloneStateRepo(root, input));
|
|
4780
|
+
yield* _(cloneStateRepo(root, input, env));
|
|
4556
4781
|
yield* _(Effect.log(`State dir cloned: ${root}`));
|
|
4557
4782
|
return;
|
|
4558
4783
|
}
|
|
4559
|
-
yield* _(git(root, ["init"],
|
|
4784
|
+
yield* _(git(root, ["init", "--initial-branch=main"], env));
|
|
4560
4785
|
}).pipe(Effect.asVoid);
|
|
4561
|
-
var ensureOriginRemote = (root, repoUrl) => Effect.gen(function* (_) {
|
|
4786
|
+
var ensureOriginRemote = (root, repoUrl, env) => Effect.gen(function* (_) {
|
|
4562
4787
|
if ((yield* _(gitExitCode(root, [
|
|
4563
4788
|
"remote",
|
|
4564
4789
|
"set-url",
|
|
4565
4790
|
"origin",
|
|
4566
4791
|
repoUrl
|
|
4567
|
-
],
|
|
4792
|
+
], env))) === successExitCode) return;
|
|
4568
4793
|
yield* _(git(root, [
|
|
4569
4794
|
"remote",
|
|
4570
4795
|
"add",
|
|
4571
4796
|
"origin",
|
|
4572
4797
|
repoUrl
|
|
4573
|
-
],
|
|
4798
|
+
], env));
|
|
4574
4799
|
});
|
|
4575
|
-
var checkoutBranchBestEffort = (root, repoRef) => Effect.gen(function* (_) {
|
|
4800
|
+
var checkoutBranchBestEffort = (root, repoRef, env) => Effect.gen(function* (_) {
|
|
4576
4801
|
const checkoutExit = yield* _(gitExitCode(root, [
|
|
4577
4802
|
"checkout",
|
|
4578
4803
|
"-B",
|
|
4579
4804
|
repoRef
|
|
4580
|
-
],
|
|
4805
|
+
], env));
|
|
4581
4806
|
if (checkoutExit === successExitCode) return;
|
|
4582
4807
|
yield* _(Effect.logWarning(`git checkout -B ${repoRef} failed (exit ${checkoutExit})`));
|
|
4583
4808
|
});
|
|
4584
|
-
var stateInit = (input) =>
|
|
4585
|
-
const
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4809
|
+
var stateInit = (input) => {
|
|
4810
|
+
const doInit = (env) => Effect.gen(function* (_) {
|
|
4811
|
+
const fs = yield* _(FileSystem.FileSystem);
|
|
4812
|
+
const path = yield* _(Path.Path);
|
|
4813
|
+
const root = resolveStateRoot(path, process.cwd());
|
|
4814
|
+
yield* _(initRepoIfNeeded(fs, path, root, input, env));
|
|
4815
|
+
yield* _(ensureOriginRemote(root, input.repoUrl, env));
|
|
4816
|
+
yield* _(adoptRemoteHistoryIfOrphan(root, input.repoRef, env));
|
|
4817
|
+
yield* _(checkoutBranchBestEffort(root, input.repoRef, env));
|
|
4818
|
+
yield* _(ensureStateGitignore(fs, path, root));
|
|
4819
|
+
yield* _(Effect.log(`State dir ready: ${root}`));
|
|
4820
|
+
yield* _(Effect.log(`Remote: ${input.repoUrl}`));
|
|
4821
|
+
}).pipe(Effect.asVoid);
|
|
4822
|
+
const token = input.token?.trim() ?? "";
|
|
4823
|
+
return token.length > 0 && isGithubHttpsRemote(input.repoUrl) ? withGithubAskpassEnv(token, doInit) : doInit(gitBaseEnv$1);
|
|
4824
|
+
};
|
|
4595
4825
|
var stateStatus = Effect.gen(function* (_) {
|
|
4596
4826
|
const output = yield* _(gitCapture$1(resolveStateRoot(yield* _(Path.Path), process.cwd()), [
|
|
4597
4827
|
"status",
|
|
@@ -5838,12 +6068,7 @@ var applyProjectConfig = (command) => runApplyForProjectDir(command.projectDir,
|
|
|
5838
6068
|
return yield* _(runApplyForProjectDir(inferredProjectDir, command));
|
|
5839
6069
|
}) : Effect.fail(error)));
|
|
5840
6070
|
//#endregion
|
|
5841
|
-
//#region ../lib/src/
|
|
5842
|
-
var oauthTokenEnvKey = "DOCKER_GIT_CLAUDE_OAUTH_TOKEN";
|
|
5843
|
-
var tokenMarker = "Your OAuth token (valid for 1 year):";
|
|
5844
|
-
var tokenFooterMarker = "Store this token securely.";
|
|
5845
|
-
var outputWindowSize = 262144;
|
|
5846
|
-
var oauthTokenRegex = /([A-Za-z0-9][A-Za-z0-9._-]{20,})/u;
|
|
6071
|
+
//#region ../lib/src/shell/ansi-strip.ts
|
|
5847
6072
|
var ansiEscape = "\x1B";
|
|
5848
6073
|
var ansiBell = "\x07";
|
|
5849
6074
|
var isAnsiFinalByte = (codePoint) => codePoint !== void 0 && codePoint >= 64 && codePoint <= 126;
|
|
@@ -5887,6 +6112,20 @@ var stripAnsi = (raw) => {
|
|
|
5887
6112
|
}
|
|
5888
6113
|
return cleaned.join("");
|
|
5889
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;
|
|
5890
6129
|
var extractOauthToken = (rawOutput) => {
|
|
5891
6130
|
const normalized = stripAnsi(rawOutput).replaceAll("\r", "\n");
|
|
5892
6131
|
const markerIndex = normalized.lastIndexOf(tokenMarker);
|
|
@@ -5941,21 +6180,14 @@ var buildDockerSetupTokenArgs = (spec) => {
|
|
|
5941
6180
|
...spec.args
|
|
5942
6181
|
];
|
|
5943
6182
|
};
|
|
5944
|
-
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")));
|
|
5945
|
-
var
|
|
5946
|
-
if (fd === 2) {
|
|
5947
|
-
process.stderr.write(chunk);
|
|
5948
|
-
return;
|
|
5949
|
-
}
|
|
5950
|
-
process.stdout.write(chunk);
|
|
5951
|
-
};
|
|
5952
|
-
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) => {
|
|
5953
6185
|
const decoder = new TextDecoder("utf-8");
|
|
5954
6186
|
let outputWindow = "";
|
|
5955
6187
|
return pipe(source, Stream.runForEach((chunk) => Effect.sync(() => {
|
|
5956
6188
|
writeChunkToFd(fd, chunk);
|
|
5957
6189
|
outputWindow += decoder.decode(chunk);
|
|
5958
|
-
if (outputWindow.length > outputWindowSize) outputWindow = outputWindow.slice(-outputWindowSize);
|
|
6190
|
+
if (outputWindow.length > outputWindowSize$1) outputWindow = outputWindow.slice(-outputWindowSize$1);
|
|
5959
6191
|
if (tokenBox.value !== null) return;
|
|
5960
6192
|
const parsed = extractOauthToken(outputWindow);
|
|
5961
6193
|
if (parsed !== null) tokenBox.value = parsed;
|
|
@@ -5977,10 +6209,10 @@ var runClaudeOauthLoginWithPrompt = (cwd, accountPath, options) => {
|
|
|
5977
6209
|
const envToken = oauthTokenFromEnv();
|
|
5978
6210
|
if (envToken !== null) return ensureOauthToken(envToken);
|
|
5979
6211
|
return Effect.scoped(Effect.gen(function* (_) {
|
|
5980
|
-
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)));
|
|
5981
6213
|
const tokenBox = { value: null };
|
|
5982
|
-
const stdoutFiber = yield* _(Effect.forkScoped(pumpDockerOutput(proc.stdout, 1, tokenBox)));
|
|
5983
|
-
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)));
|
|
5984
6216
|
const exitCode = yield* _(proc.exitCode.pipe(Effect.map(Number)));
|
|
5985
6217
|
yield* _(Fiber$1.join(stdoutFiber));
|
|
5986
6218
|
yield* _(Fiber$1.join(stderrFiber));
|
|
@@ -6001,19 +6233,15 @@ var claudeOauthTokenPath = (accountPath) => `${accountPath}/${claudeOauthTokenFi
|
|
|
6001
6233
|
var claudeConfigPath = (accountPath) => `${accountPath}/${claudeConfigFileName}`;
|
|
6002
6234
|
var claudeCredentialsPath = (accountPath) => `${accountPath}/${claudeCredentialsFileName}`;
|
|
6003
6235
|
var claudeNestedCredentialsPath = (accountPath) => `${accountPath}/${claudeCredentialsDirName}/${claudeCredentialsFileName}`;
|
|
6004
|
-
var isRegularFile = (fs, filePath) => Effect.gen(function* (_) {
|
|
6005
|
-
if (!(yield* _(fs.exists(filePath)))) return false;
|
|
6006
|
-
return (yield* _(fs.stat(filePath))).type === "File";
|
|
6007
|
-
});
|
|
6008
6236
|
var syncClaudeCredentialsFile = (fs, accountPath) => Effect.gen(function* (_) {
|
|
6009
6237
|
const nestedPath = claudeNestedCredentialsPath(accountPath);
|
|
6010
6238
|
const rootPath = claudeCredentialsPath(accountPath);
|
|
6011
|
-
if (yield* _(isRegularFile(fs, nestedPath))) {
|
|
6239
|
+
if (yield* _(isRegularFile$1(fs, nestedPath))) {
|
|
6012
6240
|
yield* _(fs.copyFile(nestedPath, rootPath));
|
|
6013
6241
|
yield* _(fs.chmod(rootPath, 384), Effect.orElseSucceed(() => void 0));
|
|
6014
6242
|
return;
|
|
6015
6243
|
}
|
|
6016
|
-
if (yield* _(isRegularFile(fs, rootPath))) {
|
|
6244
|
+
if (yield* _(isRegularFile$1(fs, rootPath))) {
|
|
6017
6245
|
const nestedDirPath = `${accountPath}/${claudeCredentialsDirName}`;
|
|
6018
6246
|
yield* _(fs.makeDirectory(nestedDirPath, { recursive: true }));
|
|
6019
6247
|
yield* _(fs.copyFile(rootPath, nestedPath));
|
|
@@ -6026,12 +6254,12 @@ var clearClaudeSessionCredentials = (fs, accountPath) => Effect.gen(function* (_
|
|
|
6026
6254
|
});
|
|
6027
6255
|
var hasNonEmptyOauthToken$1 = (fs, accountPath) => Effect.gen(function* (_) {
|
|
6028
6256
|
const tokenPath = claudeOauthTokenPath(accountPath);
|
|
6029
|
-
if (!(yield* _(isRegularFile(fs, tokenPath)))) return false;
|
|
6257
|
+
if (!(yield* _(isRegularFile$1(fs, tokenPath)))) return false;
|
|
6030
6258
|
return (yield* _(fs.readFileString(tokenPath), Effect.orElseSucceed(() => ""))).trim().length > 0;
|
|
6031
6259
|
});
|
|
6032
6260
|
var readOauthToken = (fs, accountPath) => Effect.gen(function* (_) {
|
|
6033
6261
|
const tokenPath = claudeOauthTokenPath(accountPath);
|
|
6034
|
-
if (!(yield* _(isRegularFile(fs, tokenPath)))) return null;
|
|
6262
|
+
if (!(yield* _(isRegularFile$1(fs, tokenPath)))) return null;
|
|
6035
6263
|
const token = (yield* _(fs.readFileString(tokenPath), Effect.orElseSucceed(() => ""))).trim();
|
|
6036
6264
|
return token.length > 0 ? token : null;
|
|
6037
6265
|
});
|
|
@@ -6041,7 +6269,7 @@ var resolveClaudeAuthMethod = (fs, accountPath) => Effect.gen(function* (_) {
|
|
|
6041
6269
|
return "oauth-token";
|
|
6042
6270
|
}
|
|
6043
6271
|
yield* _(syncClaudeCredentialsFile(fs, accountPath));
|
|
6044
|
-
return (yield* _(isRegularFile(fs, claudeCredentialsPath(accountPath)))) ? "claude-ai-session" : "none";
|
|
6272
|
+
return (yield* _(isRegularFile$1(fs, claudeCredentialsPath(accountPath)))) ? "claude-ai-session" : "none";
|
|
6045
6273
|
});
|
|
6046
6274
|
var buildClaudeAuthEnv = (interactive, oauthToken = null) => [...interactive ? [
|
|
6047
6275
|
`HOME=${claudeContainerHomeDir}`,
|
|
@@ -6266,53 +6494,387 @@ var authCodexStatus = (command) => withCodexAuth(command, ({ accountPath, cwd })
|
|
|
6266
6494
|
}));
|
|
6267
6495
|
var authCodexLogout = (command) => withCodexAuth(command, ({ accountPath, cwd }) => runCodexLogout(cwd, accountPath)).pipe(Effect.zipRight(autoSyncState(`chore(state): auth codex logout ${normalizeAccountLabel(command.label, "default")}`)));
|
|
6268
6496
|
//#endregion
|
|
6269
|
-
//#region ../lib/src/usecases/auth-
|
|
6270
|
-
var
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
var
|
|
6286
|
-
const normalized =
|
|
6287
|
-
|
|
6288
|
-
return
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
var
|
|
6292
|
-
|
|
6293
|
-
label: labelFromKey(entry.key),
|
|
6294
|
-
token: entry.value
|
|
6295
|
-
})).filter((entry) => entry.token.trim().length > 0);
|
|
6296
|
-
var defaultGithubScopes = "repo,workflow,read:org";
|
|
6297
|
-
var normalizeGithubScopes = (value) => {
|
|
6298
|
-
const raw = value?.trim() ?? "";
|
|
6299
|
-
const scopes = (raw.length === 0 ? defaultGithubScopes : raw).split(/[,\s]+/g).map((scope) => scope.trim()).filter((scope) => scope.length > 0 && scope !== "delete_repo");
|
|
6300
|
-
return scopes.length === 0 ? defaultGithubScopes.split(",") : scopes;
|
|
6301
|
-
};
|
|
6302
|
-
var withEnvContext = (envGlobalPath, run) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
|
|
6303
|
-
yield* _(ensureGithubOrchLayout(cwd, envGlobalPath));
|
|
6304
|
-
const envPath = resolvePathFromCwd(path, cwd, envGlobalPath);
|
|
6305
|
-
return yield* _(run({
|
|
6306
|
-
fs,
|
|
6307
|
-
envPath,
|
|
6308
|
-
current: yield* _(readEnvText(fs, envPath))
|
|
6309
|
-
}));
|
|
6310
|
-
}));
|
|
6311
|
-
var resolveGithubTokenFromGh = (cwd, accountPath) => runDockerAuthCapture(buildDockerAuthSpec({
|
|
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) => ({
|
|
6312
6521
|
cwd,
|
|
6313
|
-
image
|
|
6522
|
+
image,
|
|
6314
6523
|
hostPath: accountPath,
|
|
6315
|
-
containerPath
|
|
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
|
|
6750
|
+
//#region ../lib/src/usecases/state-repo-github.ts
|
|
6751
|
+
var dotDockerGitRepoName = ".docker-git";
|
|
6752
|
+
var defaultStateRef = "main";
|
|
6753
|
+
var resolveViewerLogin = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
6754
|
+
const raw = yield* _(runGhApiCapture(cwd, hostPath, token, [
|
|
6755
|
+
"/user",
|
|
6756
|
+
"--jq",
|
|
6757
|
+
".login"
|
|
6758
|
+
]));
|
|
6759
|
+
if (raw.length === 0) return yield* _(Effect.fail(new CommandFailedError({
|
|
6760
|
+
command: "gh api /user --jq .login",
|
|
6761
|
+
exitCode: 1
|
|
6762
|
+
})));
|
|
6763
|
+
return raw;
|
|
6764
|
+
});
|
|
6765
|
+
var getRepoCloneUrl = (cwd, hostPath, token, login) => runGhApiNullable(cwd, hostPath, token, [
|
|
6766
|
+
`/repos/${login}/${dotDockerGitRepoName}`,
|
|
6767
|
+
"--jq",
|
|
6768
|
+
".clone_url"
|
|
6769
|
+
]);
|
|
6770
|
+
var createStateRepo = (cwd, hostPath, token) => runGhApiNullable(cwd, hostPath, token, [
|
|
6771
|
+
"-X",
|
|
6772
|
+
"POST",
|
|
6773
|
+
"/user/repos",
|
|
6774
|
+
"-f",
|
|
6775
|
+
`name=${dotDockerGitRepoName}`,
|
|
6776
|
+
"-f",
|
|
6777
|
+
"private=false",
|
|
6778
|
+
"-f",
|
|
6779
|
+
"auto_init=true",
|
|
6780
|
+
"--jq",
|
|
6781
|
+
".clone_url"
|
|
6782
|
+
]);
|
|
6783
|
+
/**
|
|
6784
|
+
* Ensures the .docker-git state repository exists on GitHub and is initialised locally.
|
|
6785
|
+
*
|
|
6786
|
+
* On GitHub auth, immediately:
|
|
6787
|
+
* 1. Resolve the authenticated user's login via the GitHub API
|
|
6788
|
+
* 2. Check whether `<login>/.docker-git` exists on GitHub
|
|
6789
|
+
* 3. If missing, create the repository (public, auto-initialised with a README)
|
|
6790
|
+
* 4. Initialise the local `~/.docker-git` directory as a clone of that repository
|
|
6791
|
+
*
|
|
6792
|
+
* All failures are swallowed and logged as warnings so they never abort the auth
|
|
6793
|
+
* flow itself.
|
|
6794
|
+
*
|
|
6795
|
+
* @param token - A valid GitHub personal-access or OAuth token
|
|
6796
|
+
* @returns Effect<void, never, GithubStateRepoRuntime>
|
|
6797
|
+
*
|
|
6798
|
+
* @pure false
|
|
6799
|
+
* @effect FileSystem, CommandExecutor (Docker gh CLI, git)
|
|
6800
|
+
* @invariant ∀token ∈ ValidTokens: ensureStateDotDockerGitRepo(token) → cloned(~/.docker-git) ∨ warned
|
|
6801
|
+
* @precondition token.length > 0
|
|
6802
|
+
* @postcondition ~/.docker-git is a git repo with origin pointing to github.com/<login>/.docker-git
|
|
6803
|
+
* @complexity O(1) API calls
|
|
6804
|
+
* @throws Never - all errors are caught and logged
|
|
6805
|
+
*/
|
|
6806
|
+
var ensureStateDotDockerGitRepo = (token) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
|
|
6807
|
+
const ghRoot = resolvePathFromCwd(path, cwd, ghAuthRoot);
|
|
6808
|
+
yield* _(fs.makeDirectory(ghRoot, { recursive: true }));
|
|
6809
|
+
yield* _(ensureGhAuthImage(fs, path, cwd, "gh api"));
|
|
6810
|
+
const login = yield* _(resolveViewerLogin(cwd, ghRoot, token));
|
|
6811
|
+
let cloneUrl = yield* _(getRepoCloneUrl(cwd, ghRoot, token, login));
|
|
6812
|
+
if (cloneUrl === null) {
|
|
6813
|
+
yield* _(Effect.log(`Creating .docker-git repository for ${login}...`));
|
|
6814
|
+
cloneUrl = yield* _(createStateRepo(cwd, ghRoot, token));
|
|
6815
|
+
}
|
|
6816
|
+
if (cloneUrl === null) {
|
|
6817
|
+
yield* _(Effect.logWarning(`Could not resolve or create .docker-git repository for ${login}`));
|
|
6818
|
+
return;
|
|
6819
|
+
}
|
|
6820
|
+
yield* _(Effect.log(`Initializing state repository: ${cloneUrl}`));
|
|
6821
|
+
yield* _(stateInit({
|
|
6822
|
+
repoUrl: cloneUrl,
|
|
6823
|
+
repoRef: defaultStateRef,
|
|
6824
|
+
token
|
|
6825
|
+
}));
|
|
6826
|
+
})).pipe(Effect.matchEffect({
|
|
6827
|
+
onFailure: (error) => Effect.logWarning(`State repo setup failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6828
|
+
onSuccess: () => Effect.void
|
|
6829
|
+
}));
|
|
6830
|
+
//#endregion
|
|
6831
|
+
//#region ../lib/src/usecases/auth-github.ts
|
|
6832
|
+
var ensureGithubOrchLayout = (cwd, envGlobalPath) => migrateLegacyOrchLayout(cwd, {
|
|
6833
|
+
envGlobalPath,
|
|
6834
|
+
envProjectPath: defaultTemplateConfig.envProjectPath,
|
|
6835
|
+
codexAuthPath: defaultTemplateConfig.codexAuthPath,
|
|
6836
|
+
ghAuthPath: ghAuthRoot,
|
|
6837
|
+
claudeAuthPath: ".docker-git/.orch/auth/claude"
|
|
6838
|
+
});
|
|
6839
|
+
var normalizeGithubLabel = (value) => {
|
|
6840
|
+
const trimmed = value?.trim() ?? "";
|
|
6841
|
+
if (trimmed.length === 0) return "";
|
|
6842
|
+
const cleaned = trimRightChar(trimLeftChar(trimmed.toUpperCase().replaceAll(/[^A-Z0-9]+/g, "_"), "_"), "_");
|
|
6843
|
+
return cleaned.length > 0 ? cleaned : "";
|
|
6844
|
+
};
|
|
6845
|
+
var tokenKey = "GITHUB_TOKEN";
|
|
6846
|
+
var tokenPrefix = "GITHUB_TOKEN__";
|
|
6847
|
+
var buildGithubTokenKey = (label) => {
|
|
6848
|
+
const normalized = normalizeGithubLabel(label);
|
|
6849
|
+
if (normalized === "DEFAULT" || normalized.length === 0) return tokenKey;
|
|
6850
|
+
return `${tokenPrefix}${normalized}`;
|
|
6851
|
+
};
|
|
6852
|
+
var labelFromKey = (key) => key.startsWith(tokenPrefix) ? key.slice(14) : "default";
|
|
6853
|
+
var listGithubTokens = (envText) => parseEnvEntries(envText).filter((entry) => entry.key === tokenKey || entry.key.startsWith(tokenPrefix)).map((entry) => ({
|
|
6854
|
+
key: entry.key,
|
|
6855
|
+
label: labelFromKey(entry.key),
|
|
6856
|
+
token: entry.value
|
|
6857
|
+
})).filter((entry) => entry.token.trim().length > 0);
|
|
6858
|
+
var defaultGithubScopes = "repo,workflow,read:org";
|
|
6859
|
+
var normalizeGithubScopes = (value) => {
|
|
6860
|
+
const raw = value?.trim() ?? "";
|
|
6861
|
+
const scopes = (raw.length === 0 ? defaultGithubScopes : raw).split(/[,\s]+/g).map((scope) => scope.trim()).filter((scope) => scope.length > 0 && scope !== "delete_repo");
|
|
6862
|
+
return scopes.length === 0 ? defaultGithubScopes.split(",") : scopes;
|
|
6863
|
+
};
|
|
6864
|
+
var withEnvContext = (envGlobalPath, run) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
|
|
6865
|
+
yield* _(ensureGithubOrchLayout(cwd, envGlobalPath));
|
|
6866
|
+
const envPath = resolvePathFromCwd(path, cwd, envGlobalPath);
|
|
6867
|
+
return yield* _(run({
|
|
6868
|
+
fs,
|
|
6869
|
+
envPath,
|
|
6870
|
+
current: yield* _(readEnvText(fs, envPath))
|
|
6871
|
+
}));
|
|
6872
|
+
}));
|
|
6873
|
+
var resolveGithubTokenFromGh = (cwd, accountPath) => runDockerAuthCapture(buildDockerAuthSpec({
|
|
6874
|
+
cwd,
|
|
6875
|
+
image: ghImageName,
|
|
6876
|
+
hostPath: accountPath,
|
|
6877
|
+
containerPath: ghAuthDir,
|
|
6316
6878
|
env: `GH_CONFIG_DIR=${ghAuthDir}`,
|
|
6317
6879
|
args: ["auth", "token"],
|
|
6318
6880
|
interactive: false
|
|
@@ -6363,6 +6925,7 @@ var runGithubInteractiveLogin = (cwd, fs, path, envPath, command) => Effect.gen(
|
|
|
6363
6925
|
const resolved = yield* _(resolveGithubTokenFromGh(cwd, accountPath));
|
|
6364
6926
|
yield* _(ensureEnvFile$1(fs, path, envPath));
|
|
6365
6927
|
yield* _(persistGithubToken(fs, envPath, buildGithubTokenKey(command.label), resolved));
|
|
6928
|
+
return resolved;
|
|
6366
6929
|
});
|
|
6367
6930
|
var authGithubLogin = (command) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
|
|
6368
6931
|
yield* _(ensureGithubOrchLayout(cwd, command.envGlobalPath));
|
|
@@ -6373,10 +6936,11 @@ var authGithubLogin = (command) => withFsPathContext(({ cwd, fs, path }) => Effe
|
|
|
6373
6936
|
if (token.length > 0) {
|
|
6374
6937
|
yield* _(ensureEnvFile$1(fs, path, envPath));
|
|
6375
6938
|
yield* _(persistGithubToken(fs, envPath, key, token));
|
|
6939
|
+
yield* _(ensureStateDotDockerGitRepo(token));
|
|
6376
6940
|
yield* _(autoSyncState(`chore(state): auth gh ${label}`));
|
|
6377
6941
|
return;
|
|
6378
6942
|
}
|
|
6379
|
-
yield* _(runGithubInteractiveLogin(cwd, fs, path, envPath, command));
|
|
6943
|
+
yield* _(ensureStateDotDockerGitRepo(yield* _(runGithubInteractiveLogin(cwd, fs, path, envPath, command))));
|
|
6380
6944
|
yield* _(autoSyncState(`chore(state): auth gh ${label}`));
|
|
6381
6945
|
}));
|
|
6382
6946
|
var authGithubStatus = (command) => withEnvContext(command.envGlobalPath, ({ current, envPath }) => Effect.gen(function* (_) {
|
|
@@ -7480,10 +8044,12 @@ var normalizeLabel$1 = (value) => {
|
|
|
7480
8044
|
var defaultEnvGlobalPath = ".docker-git/.orch/env/global.env";
|
|
7481
8045
|
var defaultCodexAuthPath = ".docker-git/.orch/auth/codex";
|
|
7482
8046
|
var defaultClaudeAuthPath = ".docker-git/.orch/auth/claude";
|
|
8047
|
+
var defaultGeminiAuthPath = ".docker-git/.orch/auth/gemini";
|
|
7483
8048
|
var resolveAuthOptions = (raw) => ({
|
|
7484
8049
|
envGlobalPath: raw.envGlobalPath ?? defaultEnvGlobalPath,
|
|
7485
8050
|
codexAuthPath: raw.codexAuthPath ?? defaultCodexAuthPath,
|
|
7486
8051
|
claudeAuthPath: defaultClaudeAuthPath,
|
|
8052
|
+
geminiAuthPath: defaultGeminiAuthPath,
|
|
7487
8053
|
label: normalizeLabel$1(raw.label),
|
|
7488
8054
|
token: normalizeLabel$1(raw.token),
|
|
7489
8055
|
scopes: normalizeLabel$1(raw.scopes),
|
|
@@ -7529,7 +8095,20 @@ var buildClaudeCommand = (action, options) => Match.value(action).pipe(Match.whe
|
|
|
7529
8095
|
label: options.label,
|
|
7530
8096
|
claudeAuthPath: options.claudeAuthPath
|
|
7531
8097
|
})), Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`))));
|
|
7532
|
-
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}'`))));
|
|
7533
8112
|
var parseAuth = (args) => {
|
|
7534
8113
|
if (args.length < 2) return Either.left(missingArgument(args.length === 0 ? "auth provider" : "auth action"));
|
|
7535
8114
|
const provider = args[0] ?? "";
|
|
@@ -7636,13 +8215,15 @@ var buildDefaultPathConfig = (normalizedSecretsRoot) => normalizedSecretsRoot ==
|
|
|
7636
8215
|
authorizedKeysPath: defaultTemplateConfig.authorizedKeysPath,
|
|
7637
8216
|
envGlobalPath: defaultTemplateConfig.envGlobalPath,
|
|
7638
8217
|
envProjectPath: defaultTemplateConfig.envProjectPath,
|
|
7639
|
-
codexAuthPath: defaultTemplateConfig.codexAuthPath
|
|
8218
|
+
codexAuthPath: defaultTemplateConfig.codexAuthPath,
|
|
8219
|
+
geminiAuthPath: defaultTemplateConfig.geminiAuthPath
|
|
7640
8220
|
} : {
|
|
7641
8221
|
dockerGitPath: defaultTemplateConfig.dockerGitPath,
|
|
7642
8222
|
authorizedKeysPath: defaultTemplateConfig.authorizedKeysPath,
|
|
7643
8223
|
envGlobalPath: `${normalizedSecretsRoot}/global.env`,
|
|
7644
8224
|
envProjectPath: defaultTemplateConfig.envProjectPath,
|
|
7645
|
-
codexAuthPath: `${normalizedSecretsRoot}/codex
|
|
8225
|
+
codexAuthPath: `${normalizedSecretsRoot}/codex`,
|
|
8226
|
+
geminiAuthPath: `${normalizedSecretsRoot}/gemini`
|
|
7646
8227
|
};
|
|
7647
8228
|
var resolvePaths = (raw, repoPath) => Either.gen(function* (_) {
|
|
7648
8229
|
const defaults = buildDefaultPathConfig(resolveNormalizedSecretsRoot(raw.secretsRoot));
|
|
@@ -7659,6 +8240,8 @@ var resolvePaths = (raw, repoPath) => Either.gen(function* (_) {
|
|
|
7659
8240
|
codexAuthPath,
|
|
7660
8241
|
codexSharedAuthPath: codexAuthPath,
|
|
7661
8242
|
codexHome: yield* _(nonEmpty("--codex-home", raw.codexHome, defaultTemplateConfig.codexHome)),
|
|
8243
|
+
geminiAuthPath: defaults.geminiAuthPath,
|
|
8244
|
+
geminiHome: defaultTemplateConfig.geminiHome,
|
|
7662
8245
|
outDir: yield* _(nonEmpty("--out-dir", raw.outDir, `.docker-git/${repoPath}`))
|
|
7663
8246
|
};
|
|
7664
8247
|
});
|
|
@@ -7688,6 +8271,8 @@ var buildTemplateConfig = ({ agentAuto, agentMode, claudeAuthLabel, codexAuthLab
|
|
|
7688
8271
|
codexAuthPath: paths.codexAuthPath,
|
|
7689
8272
|
codexSharedAuthPath: paths.codexSharedAuthPath,
|
|
7690
8273
|
codexHome: paths.codexHome,
|
|
8274
|
+
geminiAuthPath: paths.geminiAuthPath,
|
|
8275
|
+
geminiHome: paths.geminiHome,
|
|
7691
8276
|
cpuLimit,
|
|
7692
8277
|
ramLimit,
|
|
7693
8278
|
dockerNetworkMode,
|
|
@@ -8769,6 +9354,12 @@ var countAuthAccountDirectories = (fs, path, root) => Effect.gen(function* (_) {
|
|
|
8769
9354
|
return count;
|
|
8770
9355
|
});
|
|
8771
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
|
|
8772
9363
|
//#region src/docker-git/menu-labeled-env.ts
|
|
8773
9364
|
var normalizeLabel = (value) => {
|
|
8774
9365
|
const trimmed = value.trim();
|
|
@@ -8817,6 +9408,18 @@ var authMenuItems = [
|
|
|
8817
9408
|
action: "ClaudeLogout",
|
|
8818
9409
|
label: "Claude Code: logout (clear cache)"
|
|
8819
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
|
+
},
|
|
8820
9423
|
{
|
|
8821
9424
|
action: "Refresh",
|
|
8822
9425
|
label: "Refresh snapshot"
|
|
@@ -8876,34 +9479,62 @@ var flowSteps$1 = {
|
|
|
8876
9479
|
label: "Label to logout (empty = default)",
|
|
8877
9480
|
required: false,
|
|
8878
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
|
|
8879
9505
|
}]
|
|
8880
9506
|
};
|
|
8881
|
-
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);
|
|
8882
|
-
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);
|
|
8883
9509
|
var buildGlobalEnvPath$1 = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/env/global.env`;
|
|
8884
9510
|
var buildClaudeAuthPath$1 = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/claude`;
|
|
9511
|
+
var buildGeminiAuthPath$1 = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/gemini`;
|
|
8885
9512
|
var loadAuthEnvText = (cwd) => Effect.gen(function* (_) {
|
|
8886
9513
|
const fs = yield* _(FileSystem.FileSystem);
|
|
8887
9514
|
const path = yield* _(Path.Path);
|
|
8888
9515
|
const globalEnvPath = buildGlobalEnvPath$1(cwd);
|
|
8889
9516
|
const claudeAuthPath = buildClaudeAuthPath$1(cwd);
|
|
9517
|
+
const geminiAuthPath = buildGeminiAuthPath$1(cwd);
|
|
8890
9518
|
yield* _(ensureEnvFile$1(fs, path, globalEnvPath));
|
|
8891
9519
|
return {
|
|
8892
9520
|
fs,
|
|
8893
9521
|
path,
|
|
8894
9522
|
globalEnvPath,
|
|
8895
9523
|
claudeAuthPath,
|
|
9524
|
+
geminiAuthPath,
|
|
8896
9525
|
envText: yield* _(readEnvText(fs, globalEnvPath))
|
|
8897
9526
|
};
|
|
8898
9527
|
});
|
|
8899
|
-
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 }) => ({
|
|
8900
9529
|
globalEnvPath,
|
|
8901
9530
|
claudeAuthPath,
|
|
9531
|
+
geminiAuthPath,
|
|
8902
9532
|
totalEntries: parseEnvEntries(envText).filter((entry) => entry.value.trim().length > 0).length,
|
|
8903
9533
|
githubTokenEntries: countKeyEntries(envText, "GITHUB_TOKEN"),
|
|
8904
9534
|
gitTokenEntries: countKeyEntries(envText, "GIT_AUTH_TOKEN"),
|
|
8905
9535
|
gitUserEntries: countKeyEntries(envText, "GIT_AUTH_USER"),
|
|
8906
|
-
claudeAuthEntries
|
|
9536
|
+
claudeAuthEntries,
|
|
9537
|
+
geminiAuthEntries
|
|
8907
9538
|
})))));
|
|
8908
9539
|
var writeAuthFlow = (cwd, flow, values) => pipe(loadAuthEnvText(cwd), Effect.flatMap(({ envText, fs, globalEnvPath }) => {
|
|
8909
9540
|
const label = values["label"] ?? "";
|
|
@@ -8932,6 +9563,67 @@ var authMenuActionByIndex = (index) => {
|
|
|
8932
9563
|
};
|
|
8933
9564
|
var authMenuSize = () => authMenuItems.length;
|
|
8934
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
|
|
8935
9627
|
//#region src/docker-git/menu-input-utils.ts
|
|
8936
9628
|
var parseMenuIndex = (input) => {
|
|
8937
9629
|
const trimmed = input.trim();
|
|
@@ -8988,14 +9680,6 @@ var defaultLabel = (value) => {
|
|
|
8988
9680
|
const trimmed = value.trim();
|
|
8989
9681
|
return trimmed.length > 0 ? trimmed : "default";
|
|
8990
9682
|
};
|
|
8991
|
-
var startAuthMenuWithSnapshot = (snapshot, context) => {
|
|
8992
|
-
context.setView({
|
|
8993
|
-
_tag: "AuthMenu",
|
|
8994
|
-
selected: 0,
|
|
8995
|
-
snapshot
|
|
8996
|
-
});
|
|
8997
|
-
context.setMessage(null);
|
|
8998
|
-
};
|
|
8999
9683
|
var startAuthPrompt = (snapshot, flow, context) => {
|
|
9000
9684
|
context.setView({
|
|
9001
9685
|
_tag: "AuthPrompt",
|
|
@@ -9007,39 +9691,6 @@ var startAuthPrompt = (snapshot, flow, context) => {
|
|
|
9007
9691
|
});
|
|
9008
9692
|
context.setMessage(null);
|
|
9009
9693
|
};
|
|
9010
|
-
var resolveLabelOption = (values) => {
|
|
9011
|
-
const labelValue = (values["label"] ?? "").trim();
|
|
9012
|
-
return labelValue.length > 0 ? labelValue : null;
|
|
9013
|
-
};
|
|
9014
|
-
var resolveAuthPromptEffect = (view, cwd, values) => {
|
|
9015
|
-
const labelOption = resolveLabelOption(values);
|
|
9016
|
-
return Match.value(view.flow).pipe(Match.when("GithubOauth", () => authGithubLogin({
|
|
9017
|
-
_tag: "AuthGithubLogin",
|
|
9018
|
-
label: labelOption,
|
|
9019
|
-
token: null,
|
|
9020
|
-
scopes: null,
|
|
9021
|
-
envGlobalPath: view.snapshot.globalEnvPath
|
|
9022
|
-
})), Match.when("ClaudeOauth", () => authClaudeLogin({
|
|
9023
|
-
_tag: "AuthClaudeLogin",
|
|
9024
|
-
label: labelOption,
|
|
9025
|
-
claudeAuthPath: claudeAuthRoot
|
|
9026
|
-
})), Match.when("ClaudeLogout", () => authClaudeLogout({
|
|
9027
|
-
_tag: "AuthClaudeLogout",
|
|
9028
|
-
label: labelOption,
|
|
9029
|
-
claudeAuthPath: claudeAuthRoot
|
|
9030
|
-
})), 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);
|
|
9031
|
-
};
|
|
9032
|
-
var runAuthPromptEffect = (effect, view, label, context, options) => {
|
|
9033
|
-
const withOptionalSuspension = options.suspendTui ? withSuspendedTui(effect, {
|
|
9034
|
-
onError: pauseOnError(renderError),
|
|
9035
|
-
onResume: resumeSshWithSkipInputs(context)
|
|
9036
|
-
}) : effect;
|
|
9037
|
-
context.setSshActive(options.suspendTui);
|
|
9038
|
-
context.runner.runEffect(pipe(withOptionalSuspension, Effect.zipRight(readAuthSnapshot(context.state.cwd)), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9039
|
-
startAuthMenuWithSnapshot(snapshot, context);
|
|
9040
|
-
context.setMessage(successMessage$1(view.flow, label));
|
|
9041
|
-
})), Effect.asVoid));
|
|
9042
|
-
};
|
|
9043
9694
|
var loadAuthMenuView = (cwd, context) => pipe(readAuthSnapshot(cwd), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9044
9695
|
startAuthMenuWithSnapshot(snapshot, context);
|
|
9045
9696
|
})), Effect.asVoid);
|
|
@@ -9059,7 +9710,10 @@ var submitAuthPrompt = (view, context) => {
|
|
|
9059
9710
|
startAuthMenuWithSnapshot(view.snapshot, context);
|
|
9060
9711
|
}, (nextValues) => {
|
|
9061
9712
|
const label = defaultLabel(nextValues["label"] ?? "");
|
|
9062
|
-
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" });
|
|
9063
9717
|
});
|
|
9064
9718
|
};
|
|
9065
9719
|
var setAuthMenuSelection = (view, selected, context) => {
|
|
@@ -9101,6 +9755,15 @@ var handleAuthMenuInput = (input, key, view, context) => {
|
|
|
9101
9755
|
}
|
|
9102
9756
|
handleAuthMenuNumberInput(input, view, context);
|
|
9103
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
|
+
};
|
|
9104
9767
|
var handleAuthPromptInput = (input, key, view, context) => {
|
|
9105
9768
|
if (key.escape) {
|
|
9106
9769
|
startAuthMenuWithSnapshot(view.snapshot, context);
|
|
@@ -9117,15 +9780,6 @@ var handleAuthPromptInput = (input, key, view, context) => {
|
|
|
9117
9780
|
context
|
|
9118
9781
|
});
|
|
9119
9782
|
};
|
|
9120
|
-
var setAuthPromptBuffer = (args) => {
|
|
9121
|
-
const { context, input, key, view } = args;
|
|
9122
|
-
const nextBuffer = nextBufferValue(input, key, view.buffer);
|
|
9123
|
-
if (nextBuffer === null) return;
|
|
9124
|
-
context.setView({
|
|
9125
|
-
...view,
|
|
9126
|
-
buffer: nextBuffer
|
|
9127
|
-
});
|
|
9128
|
-
};
|
|
9129
9783
|
var openAuthMenu = (context) => {
|
|
9130
9784
|
context.setMessage("Loading auth profiles...");
|
|
9131
9785
|
context.runner.runEffect(loadAuthMenuView(context.state.cwd, context));
|
|
@@ -9218,15 +9872,17 @@ var loadRuntimeByProject = (items) => pipe(runDockerPsNames(process.cwd()), Effe
|
|
|
9218
9872
|
}));
|
|
9219
9873
|
var runtimeForSelection = (view, selected) => view.runtimeByProject[selected.projectDir] ?? stoppedRuntime$1();
|
|
9220
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
|
|
9221
9881
|
//#region src/docker-git/menu-project-auth-claude.ts
|
|
9222
9882
|
var oauthTokenFileName = ".oauth-token";
|
|
9223
9883
|
var legacyConfigFileName = ".config.json";
|
|
9224
9884
|
var credentialsFileName = ".credentials.json";
|
|
9225
9885
|
var nestedCredentialsFileName = ".claude/.credentials.json";
|
|
9226
|
-
var hasFileAtPath = (fs, filePath) => Effect.gen(function* (_) {
|
|
9227
|
-
if (!(yield* _(fs.exists(filePath)))) return false;
|
|
9228
|
-
return (yield* _(fs.stat(filePath))).type === "File";
|
|
9229
|
-
});
|
|
9230
9886
|
var hasNonEmptyOauthToken = (fs, tokenPath) => Effect.gen(function* (_) {
|
|
9231
9887
|
if (!(yield* _(hasFileAtPath(fs, tokenPath)))) return false;
|
|
9232
9888
|
return (yield* _(fs.readFileString(tokenPath), Effect.orElseSucceed(() => ""))).trim().length > 0;
|
|
@@ -9253,6 +9909,107 @@ var hasClaudeAccountCredentials = (fs, accountPath) => hasFileAtPath(fs, `${acco
|
|
|
9253
9909
|
}));
|
|
9254
9910
|
}));
|
|
9255
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
|
|
9256
10013
|
//#region src/docker-git/menu-project-auth-data.ts
|
|
9257
10014
|
var projectAuthMenuItems = [
|
|
9258
10015
|
{
|
|
@@ -9279,6 +10036,14 @@ var projectAuthMenuItems = [
|
|
|
9279
10036
|
action: "ProjectClaudeDisconnect",
|
|
9280
10037
|
label: "Project: Claude disconnect"
|
|
9281
10038
|
},
|
|
10039
|
+
{
|
|
10040
|
+
action: "ProjectGeminiConnect",
|
|
10041
|
+
label: "Project: Gemini connect label"
|
|
10042
|
+
},
|
|
10043
|
+
{
|
|
10044
|
+
action: "ProjectGeminiDisconnect",
|
|
10045
|
+
label: "Project: Gemini disconnect"
|
|
10046
|
+
},
|
|
9282
10047
|
{
|
|
9283
10048
|
action: "Refresh",
|
|
9284
10049
|
label: "Refresh snapshot"
|
|
@@ -9309,7 +10074,14 @@ var flowSteps = {
|
|
|
9309
10074
|
required: false,
|
|
9310
10075
|
secret: false
|
|
9311
10076
|
}],
|
|
9312
|
-
ProjectClaudeDisconnect: []
|
|
10077
|
+
ProjectClaudeDisconnect: [],
|
|
10078
|
+
ProjectGeminiConnect: [{
|
|
10079
|
+
key: "label",
|
|
10080
|
+
label: "Label (empty = default)",
|
|
10081
|
+
required: false,
|
|
10082
|
+
secret: false
|
|
10083
|
+
}],
|
|
10084
|
+
ProjectGeminiDisconnect: []
|
|
9313
10085
|
};
|
|
9314
10086
|
var resolveCanonicalLabel = (value) => {
|
|
9315
10087
|
const normalized = normalizeLabel(value);
|
|
@@ -9317,18 +10089,19 @@ var resolveCanonicalLabel = (value) => {
|
|
|
9317
10089
|
};
|
|
9318
10090
|
var githubTokenBaseKey = "GITHUB_TOKEN";
|
|
9319
10091
|
var gitTokenBaseKey = "GIT_AUTH_TOKEN";
|
|
9320
|
-
var gitUserBaseKey = "GIT_AUTH_USER";
|
|
9321
10092
|
var projectGithubLabelKey = "GITHUB_AUTH_LABEL";
|
|
9322
10093
|
var projectGitLabelKey = "GIT_AUTH_LABEL";
|
|
9323
10094
|
var projectClaudeLabelKey = "CLAUDE_AUTH_LABEL";
|
|
9324
|
-
var
|
|
10095
|
+
var projectGeminiLabelKey = "GEMINI_AUTH_LABEL";
|
|
9325
10096
|
var buildGlobalEnvPath = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/env/global.env`;
|
|
9326
10097
|
var buildClaudeAuthPath = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/claude`;
|
|
10098
|
+
var buildGeminiAuthPath = (cwd) => `${defaultProjectsRoot(cwd)}/.orch/auth/gemini`;
|
|
9327
10099
|
var loadProjectAuthEnvText = (project) => Effect.gen(function* (_) {
|
|
9328
10100
|
const fs = yield* _(FileSystem.FileSystem);
|
|
9329
10101
|
const path = yield* _(Path.Path);
|
|
9330
10102
|
const globalEnvPath = buildGlobalEnvPath(process.cwd());
|
|
9331
10103
|
const claudeAuthPath = buildClaudeAuthPath(process.cwd());
|
|
10104
|
+
const geminiAuthPath = buildGeminiAuthPath(process.cwd());
|
|
9332
10105
|
yield* _(ensureEnvFile$1(fs, path, globalEnvPath));
|
|
9333
10106
|
yield* _(ensureEnvFile$1(fs, path, project.envProjectPath));
|
|
9334
10107
|
const globalEnvText = yield* _(readEnvText(fs, globalEnvPath));
|
|
@@ -9339,69 +10112,29 @@ var loadProjectAuthEnvText = (project) => Effect.gen(function* (_) {
|
|
|
9339
10112
|
globalEnvPath,
|
|
9340
10113
|
projectEnvPath: project.envProjectPath,
|
|
9341
10114
|
claudeAuthPath,
|
|
10115
|
+
geminiAuthPath,
|
|
9342
10116
|
globalEnvText,
|
|
9343
10117
|
projectEnvText
|
|
9344
10118
|
};
|
|
9345
10119
|
});
|
|
9346
|
-
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 }) => ({
|
|
9347
10121
|
projectDir: project.projectDir,
|
|
9348
10122
|
projectName: project.displayName,
|
|
9349
10123
|
envGlobalPath: globalEnvPath,
|
|
9350
10124
|
envProjectPath: projectEnvPath,
|
|
9351
10125
|
claudeAuthPath,
|
|
10126
|
+
geminiAuthPath,
|
|
9352
10127
|
githubTokenEntries: countKeyEntries(globalEnvText, githubTokenBaseKey),
|
|
9353
10128
|
gitTokenEntries: countKeyEntries(globalEnvText, gitTokenBaseKey),
|
|
9354
10129
|
claudeAuthEntries,
|
|
10130
|
+
geminiAuthEntries,
|
|
9355
10131
|
activeGithubLabel: findEnvValue(projectEnvText, projectGithubLabelKey),
|
|
9356
10132
|
activeGitLabel: findEnvValue(projectEnvText, projectGitLabelKey),
|
|
9357
|
-
activeClaudeLabel: findEnvValue(projectEnvText, projectClaudeLabelKey)
|
|
10133
|
+
activeClaudeLabel: findEnvValue(projectEnvText, projectClaudeLabelKey),
|
|
10134
|
+
activeGeminiLabel: findEnvValue(projectEnvText, projectGeminiLabelKey)
|
|
9358
10135
|
})))));
|
|
9359
|
-
var
|
|
9360
|
-
var
|
|
9361
|
-
const key = buildLabeledEnvKey(githubTokenBaseKey, spec.rawLabel);
|
|
9362
|
-
const token = findEnvValue(spec.globalEnvText, key);
|
|
9363
|
-
if (token === null) return Effect.fail(missingSecret("GitHub token", spec.canonicalLabel, spec.globalEnvPath));
|
|
9364
|
-
const withoutGitLabel = upsertEnvKey(upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", token), "GH_TOKEN", token), projectGitLabelKey, "");
|
|
9365
|
-
return Effect.succeed(upsertEnvKey(withoutGitLabel, projectGithubLabelKey, spec.canonicalLabel));
|
|
9366
|
-
};
|
|
9367
|
-
var clearProjectGitLabels = (envText) => {
|
|
9368
|
-
return upsertEnvKey(upsertEnvKey(upsertEnvKey(envText, "GH_TOKEN", ""), projectGitLabelKey, ""), projectGithubLabelKey, "");
|
|
9369
|
-
};
|
|
9370
|
-
var updateProjectGithubDisconnect = (spec) => {
|
|
9371
|
-
const withoutGitToken = upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", "");
|
|
9372
|
-
return Effect.succeed(clearProjectGitLabels(withoutGitToken));
|
|
9373
|
-
};
|
|
9374
|
-
var updateProjectGitConnect = (spec) => {
|
|
9375
|
-
const tokenKey = buildLabeledEnvKey(gitTokenBaseKey, spec.rawLabel);
|
|
9376
|
-
const userKey = buildLabeledEnvKey(gitUserBaseKey, spec.rawLabel);
|
|
9377
|
-
const token = findEnvValue(spec.globalEnvText, tokenKey);
|
|
9378
|
-
if (token === null) return Effect.fail(missingSecret("Git credentials", spec.canonicalLabel, spec.globalEnvPath));
|
|
9379
|
-
const defaultUser = findEnvValue(spec.globalEnvText, gitUserBaseKey) ?? defaultGitUser;
|
|
9380
|
-
const user = findEnvValue(spec.globalEnvText, userKey) ?? defaultUser;
|
|
9381
|
-
const withGitLabel = upsertEnvKey(upsertEnvKey(upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", token), "GIT_AUTH_USER", user), "GH_TOKEN", token), projectGitLabelKey, spec.canonicalLabel);
|
|
9382
|
-
return Effect.succeed(upsertEnvKey(withGitLabel, projectGithubLabelKey, spec.canonicalLabel));
|
|
9383
|
-
};
|
|
9384
|
-
var updateProjectGitDisconnect = (spec) => {
|
|
9385
|
-
const withoutUser = upsertEnvKey(upsertEnvKey(spec.projectEnvText, "GIT_AUTH_TOKEN", ""), "GIT_AUTH_USER", "");
|
|
9386
|
-
return Effect.succeed(clearProjectGitLabels(withoutUser));
|
|
9387
|
-
};
|
|
9388
|
-
var resolveClaudeAccountCandidates = (claudeAuthPath, accountLabel) => accountLabel === "default" ? [`${claudeAuthPath}/default`, claudeAuthPath] : [`${claudeAuthPath}/${accountLabel}`];
|
|
9389
|
-
var updateProjectClaudeConnect = (spec) => {
|
|
9390
|
-
const accountLabel = normalizeAccountLabel(spec.rawLabel, "default");
|
|
9391
|
-
const accountCandidates = resolveClaudeAccountCandidates(spec.claudeAuthPath, accountLabel);
|
|
9392
|
-
return Effect.gen(function* (_) {
|
|
9393
|
-
for (const accountPath of accountCandidates) {
|
|
9394
|
-
if (!(yield* _(spec.fs.exists(accountPath)))) continue;
|
|
9395
|
-
if (yield* _(hasClaudeAccountCredentials(spec.fs, accountPath), Effect.orElseSucceed(() => false))) return upsertEnvKey(spec.projectEnvText, projectClaudeLabelKey, spec.canonicalLabel);
|
|
9396
|
-
}
|
|
9397
|
-
return yield* _(Effect.fail(missingSecret("Claude Code login", spec.canonicalLabel, spec.claudeAuthPath)));
|
|
9398
|
-
});
|
|
9399
|
-
};
|
|
9400
|
-
var updateProjectClaudeDisconnect = (spec) => {
|
|
9401
|
-
return Effect.succeed(upsertEnvKey(spec.projectEnvText, projectClaudeLabelKey, ""));
|
|
9402
|
-
};
|
|
9403
|
-
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);
|
|
9404
|
-
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 }) => {
|
|
9405
10138
|
const rawLabel = values["label"] ?? "";
|
|
9406
10139
|
const canonicalLabel = resolveCanonicalLabel(rawLabel);
|
|
9407
10140
|
const nextProjectEnv = resolveProjectEnvUpdate(flow, {
|
|
@@ -9411,9 +10144,10 @@ var writeProjectAuthFlow = (project, flow, values) => pipe(loadProjectAuthEnvTex
|
|
|
9411
10144
|
globalEnvPath,
|
|
9412
10145
|
globalEnvText,
|
|
9413
10146
|
projectEnvText,
|
|
9414
|
-
claudeAuthPath
|
|
10147
|
+
claudeAuthPath,
|
|
10148
|
+
geminiAuthPath
|
|
9415
10149
|
});
|
|
9416
|
-
const syncMessage =
|
|
10150
|
+
const syncMessage = resolveSyncMessage(flow, canonicalLabel, project.displayName);
|
|
9417
10151
|
return pipe(nextProjectEnv, Effect.flatMap((nextText) => fs.writeFileString(projectEnvPath, nextText)), Effect.zipRight(autoSyncState(syncMessage)));
|
|
9418
10152
|
}), Effect.asVoid);
|
|
9419
10153
|
var projectAuthViewSteps = (flow) => flowSteps[flow];
|
|
@@ -9449,7 +10183,7 @@ var startProjectAuthPrompt = (project, snapshot, flow, context) => {
|
|
|
9449
10183
|
var loadProjectAuthMenuView = (project, context) => pipe(readProjectAuthSnapshot(project), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9450
10184
|
startProjectAuthMenu(project, snapshot, context);
|
|
9451
10185
|
})), Effect.asVoid);
|
|
9452
|
-
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);
|
|
9453
10187
|
var runProjectAuthEffect = (project, flow, values, label, context) => {
|
|
9454
10188
|
context.runner.runEffect(pipe(writeProjectAuthFlow(project, flow, values), Effect.zipRight(readProjectAuthSnapshot(project)), Effect.tap((snapshot) => Effect.sync(() => {
|
|
9455
10189
|
startProjectAuthMenu(project, snapshot, context);
|
|
@@ -9474,7 +10208,7 @@ var runProjectAuthAction$1 = (action, view, context) => {
|
|
|
9474
10208
|
context.runner.runEffect(loadProjectAuthMenuView(view.project, context));
|
|
9475
10209
|
return;
|
|
9476
10210
|
}
|
|
9477
|
-
if (action === "ProjectGithubDisconnect" || action === "ProjectGitDisconnect" || action === "ProjectClaudeDisconnect") {
|
|
10211
|
+
if (action === "ProjectGithubDisconnect" || action === "ProjectGitDisconnect" || action === "ProjectClaudeDisconnect" || action === "ProjectGeminiDisconnect") {
|
|
9478
10212
|
runProjectAuthEffect(view.project, action, {}, "default", context);
|
|
9479
10213
|
return;
|
|
9480
10214
|
}
|
|
@@ -10644,7 +11378,7 @@ var setExitCode = (code) => Effect.sync(() => {
|
|
|
10644
11378
|
});
|
|
10645
11379
|
var logWarningAndExit = (error) => pipe(Effect.logWarning(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
|
|
10646
11380
|
var logErrorAndExit = (error) => pipe(Effect.logError(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
|
|
10647
|
-
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);
|
|
10648
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({
|
|
10649
11383
|
onFailure: (error) => isParseError(error) ? logErrorAndExit(error) : pipe(Effect.logError(renderError(error)), Effect.flatMap(() => Effect.fail(error))),
|
|
10650
11384
|
onSuccess: () => Effect.void
|