diff --git a/AGENTS.md b/AGENTS.md index 4b74344f..71f7e871 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -375,9 +375,9 @@ describe("Message invariants", () => { ПРИНЦИП: Сначала формализуем, потом программируем. -Issue workspace: #39 -Issue URL: https://github.com/ProverCoderAI/docker-git/issues/39 -Workspace path: /home/dev/provercoderai/docker-git/issue-39 +Issue workspace: #61 +Issue URL: https://github.com/ProverCoderAI/docker-git/issues/61 +Workspace path: /home/dev/provercoderai/docker-git/issue-61 Работай только над этим issue, если пользователь не попросил другое. Если нужен первоисточник требований, открой Issue URL. diff --git a/README.md b/README.md index 9ed4a275..372defa1 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,10 @@ Structure (simplified): authorized_keys .orch/ env/ - global.env + global.env # shared tokens/keys (GitHub, Git, Claude) with labels auth/ - codex/ # shared Codex auth cache (credentials) - gh/ # shared GitHub auth (optional) + codex/ # shared Codex auth/config (when CODEX_SHARE_AUTH=1) + gh/ # GH CLI auth cache for OAuth login container // docker-compose.yml Dockerfile @@ -70,7 +70,6 @@ Structure (simplified): docker-git.json .orch/ env/ - global.env # copied/synced from root .orch/env/global.env project.env # per-project env knobs (see below) auth/ codex/ # project-local Codex state (sessions/logs/tmp/etc) @@ -79,7 +78,7 @@ Structure (simplified): ## Codex Auth: Shared Credentials, Per-Project Sessions Default behavior: -- Shared credentials live in `/home/dev/.codex-shared/auth.json` (mounted from projects root). +- Shared credentials live in `/home/dev/.codex-shared/auth.json` (mounted from `/.orch/auth/codex`). - Each project keeps its own Codex state under `/home/dev/.codex/` (mounted from project `.orch/auth/codex`). - The entrypoint links `/home/dev/.codex/auth.json -> /home/dev/.codex-shared/auth.json`. @@ -160,7 +159,7 @@ Clone auth error (`Invalid username or token`): ```bash pnpm run docker-git auth github status pnpm run docker-git auth github logout - pnpm run docker-git auth github login --token '' + pnpm run docker-git auth github login --web pnpm run docker-git auth github status ``` - Token requirements: diff --git a/package.json b/package.json index f365c6f4..60a00d95 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,10 @@ "changeset-version": "changeset version", "clone": "pnpm --filter ./packages/app build && node packages/app/dist/main.js clone", "docker-git": "pnpm --filter ./packages/app build:docker-git && node packages/app/dist/src/docker-git/main.js", + "e2e": "bash scripts/e2e/run-all.sh", + "e2e:clone-cache": "bash scripts/e2e/clone-cache.sh", + "e2e:login-context": "bash scripts/e2e/login-context.sh", + "e2e:opencode-autoconnect": "bash scripts/e2e/opencode-autoconnect.sh", "list": "pnpm --filter ./packages/app build && node packages/app/dist/main.js list", "dev": "pnpm --filter ./packages/app dev", "lint": "pnpm --filter ./packages/app lint && pnpm --filter ./packages/lib lint", diff --git a/packages/app/src/docker-git/cli/parser-auth.ts b/packages/app/src/docker-git/cli/parser-auth.ts index edc1ac00..c046e17f 100644 --- a/packages/app/src/docker-git/cli/parser-auth.ts +++ b/packages/app/src/docker-git/cli/parser-auth.ts @@ -1,21 +1,18 @@ import { Either, Match } from "effect" import type { RawOptions } from "@effect-template/lib/core/command-options" -import { - type AuthCommand, - type Command, - defaultTemplateConfig, - type ParseError -} from "@effect-template/lib/core/domain" +import { type AuthCommand, type Command, type ParseError } from "@effect-template/lib/core/domain" import { parseRawOptions } from "./parser-options.js" type AuthOptions = { readonly envGlobalPath: string readonly codexAuthPath: string + readonly claudeAuthPath: string readonly label: string | null readonly token: string | null readonly scopes: string | null + readonly authWeb: boolean } const missingArgument = (name: string): ParseError => ({ @@ -34,24 +31,32 @@ const normalizeLabel = (value: string | undefined): string | null => { return trimmed.length === 0 ? null : trimmed } +const defaultEnvGlobalPath = ".docker-git/.orch/env/global.env" +const defaultCodexAuthPath = ".docker-git/.orch/auth/codex" +const defaultClaudeAuthPath = ".docker-git/.orch/auth/claude" + const resolveAuthOptions = (raw: RawOptions): AuthOptions => ({ - envGlobalPath: raw.envGlobalPath ?? defaultTemplateConfig.envGlobalPath, - codexAuthPath: raw.codexAuthPath ?? defaultTemplateConfig.codexAuthPath, + envGlobalPath: raw.envGlobalPath ?? defaultEnvGlobalPath, + codexAuthPath: raw.codexAuthPath ?? defaultCodexAuthPath, + claudeAuthPath: defaultClaudeAuthPath, label: normalizeLabel(raw.label), token: normalizeLabel(raw.token), - scopes: normalizeLabel(raw.scopes) + scopes: normalizeLabel(raw.scopes), + authWeb: raw.authWeb === true }) const buildGithubCommand = (action: string, options: AuthOptions): Either.Either => Match.value(action).pipe( Match.when("login", () => - Either.right({ - _tag: "AuthGithubLogin", - label: options.label, - token: options.token, - scopes: options.scopes, - envGlobalPath: options.envGlobalPath - })), + options.authWeb && options.token !== null + ? Either.left(invalidArgument("--token", "cannot be combined with --web")) + : Either.right({ + _tag: "AuthGithubLogin", + label: options.label, + token: options.authWeb ? null : options.token, + scopes: options.scopes, + envGlobalPath: options.envGlobalPath + })), Match.when("status", () => Either.right({ _tag: "AuthGithubStatus", @@ -89,6 +94,29 @@ const buildCodexCommand = (action: string, options: AuthOptions): Either.Either< Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`))) ) +const buildClaudeCommand = (action: string, options: AuthOptions): Either.Either => + Match.value(action).pipe( + Match.when("login", () => + Either.right({ + _tag: "AuthClaudeLogin", + label: options.label, + claudeAuthPath: options.claudeAuthPath + })), + Match.when("status", () => + Either.right({ + _tag: "AuthClaudeStatus", + label: options.label, + claudeAuthPath: options.claudeAuthPath + })), + Match.when("logout", () => + Either.right({ + _tag: "AuthClaudeLogout", + label: options.label, + claudeAuthPath: options.claudeAuthPath + })), + Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`))) + ) + const buildAuthCommand = ( provider: string, action: string, @@ -98,6 +126,8 @@ const buildAuthCommand = ( 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.orElse(() => Either.left(invalidArgument("auth provider", `unknown provider '${provider}'`))) ) diff --git a/packages/app/src/docker-git/cli/parser-mcp-playwright.ts b/packages/app/src/docker-git/cli/parser-mcp-playwright.ts index 2a4179c8..42abcd5f 100644 --- a/packages/app/src/docker-git/cli/parser-mcp-playwright.ts +++ b/packages/app/src/docker-git/cli/parser-mcp-playwright.ts @@ -22,4 +22,3 @@ export const parseMcpPlaywright = ( projectDir, runUp: raw.up ?? true })) - diff --git a/packages/app/src/docker-git/cli/parser.ts b/packages/app/src/docker-git/cli/parser.ts index 829f4da9..8824f5ca 100644 --- a/packages/app/src/docker-git/cli/parser.ts +++ b/packages/app/src/docker-git/cli/parser.ts @@ -22,7 +22,7 @@ const statusCommand: Command = { _tag: "Status" } const downAllCommand: Command = { _tag: "DownAll" } const parseCreate = (args: ReadonlyArray): Either.Either => - Either.flatMap(parseRawOptions(args), buildCreateCommand) + Either.flatMap(parseRawOptions(args), (raw) => buildCreateCommand(raw)) // CHANGE: parse CLI arguments into a typed command // WHY: enforce deterministic, pure parsing before any effects run diff --git a/packages/app/src/docker-git/cli/usage.ts b/packages/app/src/docker-git/cli/usage.ts index 630c3915..9859b617 100644 --- a/packages/app/src/docker-git/cli/usage.ts +++ b/packages/app/src/docker-git/cli/usage.ts @@ -28,7 +28,7 @@ Commands: sessions List/kill/log container terminal processes ps, status Show docker compose status for all docker-git projects down-all Stop all docker-git containers (docker compose down) - auth Manage GitHub/Codex auth for docker-git + auth Manage GitHub/Codex/Claude Code auth for docker-git state Manage docker-git state directory via git (sync across machines) Options: @@ -40,7 +40,6 @@ Options: --container-name Docker container name (default: dg-) --service-name Compose service name (default: dg-) --volume-name Docker volume name (default: dg--home) - --secrets-root Host root for shared secrets (default: n/a) --authorized-keys Host path to authorized_keys (default: /authorized_keys) --env-global Host path to shared env file (default: /.orch/env/global.env) --env-project Host path to project env file (default: ./.orch/env/project.env) @@ -72,6 +71,7 @@ Container runtime env (set via .orch/env/project.env): Auth providers: github, gh GitHub CLI auth (tokens saved to env file) codex Codex CLI auth (stored under .orch/auth/codex) + claude, cc Claude Code CLI auth (OAuth cache stored under .orch/auth/claude) Auth actions: login Run login flow and store credentials @@ -80,7 +80,8 @@ Auth actions: Auth options: --label