fabro/lib/crates
brynary-fabro[bot] 8a9b89deda
fabro provider login command (#102)
This PR adds a standalone `fabro provider login --provider <name>`
command so users can re-authenticate with LLM providers outside of the
initial `fabro install` wizard—useful when tokens expire or when
switching accounts. For OpenAI, it offers the browser-based OAuth PKCE
flow with an automatic fallback to manual API key entry; all other
providers go straight to an API key prompt with validation. Credentials
are merged non-destructively into `~/.fabro/.env`.

To support this, shared authentication helpers (`provider_key_url`,
`provider_display_name`, `openai_oauth_env_pairs`, `write_env_file`,
`validate_api_key`, `prompt_and_validate_key`, and the underlying prompt
functions) are extracted from `install.rs` into a new `provider_auth.rs`
module. The install flow is updated to import from the shared module and
delegate the OpenAI OAuth-or-fallback logic to a single
`run_openai_oauth_or_api_key` helper, eliminating duplication between
the two code paths.

The new command is wired into the CLI via a `ProviderCommand::Login`
subcommand under `Command::Provider`, with clap-level parsing tests
covering valid providers, missing flags, and unknown provider names.

### Fabro Details

<details>
<summary>Ran 10 stages in 26m 57s for $7.49</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 1m 11s | – | 0 |
| preflight_lint | 13s | – | 0 |
| implement | 9m 16s | $2.76 | 0 |
| simplify_opus | 6m 32s | $2.05 | 0 |
| simplify_gemini | 4m 44s | $1.58 | 0 |
| simplify_gpt | 4m 2s | $1.10 | 0 |
| verify | 22s | – | 0 |
| fmt | 1s | – | 0 |
| **Total** | **26m 57s** | **$7.49** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (13 nodes and 16
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    rankdir=LR

    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]

    toolchain         [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
    preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1", max_retries=0]
    fix_lints         [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
    implement         [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD."]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gemini   [label="Simplify (Gemini)", prompt="@prompts/simplify.md", model="gemini-3.1-pro-preview-customtools"]
    simplify_gpt      [label="Simplify (GPT-54)", prompt="@prompts/simplify.md", model="gpt-54"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1", goal_gate=true, retry_target="fixup"]
    fixup             [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings and test failures.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gemini -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
2026-03-19 12:30:10 -04:00
..
fabro-agent Relax web_fetch parity test assertions for LLM summarization variance 2026-03-19 11:03:20 -04:00
fabro-api Add skip_clone to DaytonaConfig to fix concurrent test failures 2026-03-19 12:27:56 -04:00
fabro-beastie Fix fabro-beastie linker error by linking IOKit framework 2026-03-15 18:27:42 -04:00
fabro-cli fabro provider login command (#102) 2026-03-19 12:30:10 -04:00
fabro-config Add skip_clone to DaytonaConfig to fix concurrent test failures 2026-03-19 12:27:56 -04:00
fabro-daytona Add skip_clone to DaytonaConfig to fix concurrent test failures 2026-03-19 12:27:56 -04:00
fabro-db rename Arc to Fabro in all Rust crates, symbols, env vars, and supporting files 2026-03-12 12:25:58 -04:00
fabro-devcontainer rename Arc to Fabro in all Rust crates, symbols, env vars, and supporting files 2026-03-12 12:25:58 -04:00
fabro-exe Fix fabro-config dependency inversion 2026-03-17 15:17:55 -04:00
fabro-git-storage Add fabro rewind command to rewind workflow runs to earlier checkpoints 2026-03-14 11:38:17 -04:00
fabro-github Detect GitHub App visibility mismatch during repo init (#99) 2026-03-19 12:29:55 -04:00
fabro-graphviz Extract fabro-validate crate from fabro-workflows 2026-03-17 13:33:34 -04:00
fabro-hooks Move config type tests into fabro-config 2026-03-17 15:21:16 -04:00
fabro-interview Clean up fabro-interview extraction 2026-03-17 13:56:51 -04:00
fabro-linear rename Arc to Fabro in all Rust crates, symbols, env vars, and supporting files 2026-03-12 12:25:58 -04:00
fabro-llm Fix OpenAI multi-turn failures by only setting store: false in Codex mode 2026-03-19 11:03:19 -04:00
fabro-mcp Move config type tests into fabro-config 2026-03-17 15:21:16 -04:00
fabro-openai-oauth Add Codex endpoint support for OpenAI OAuth flow 2026-03-17 00:47:47 -04:00
fabro-retro Extract fabro-retro crate from fabro-workflows 2026-03-17 11:48:40 -04:00
fabro-slack Extract fabro-interview crate from fabro-workflows 2026-03-17 13:53:36 -04:00
fabro-sprites Fix sprite exec flag parsing and document E2E test instructions 2026-03-19 11:03:19 -04:00
fabro-ssh Fix fabro-config dependency inversion 2026-03-17 15:17:55 -04:00
fabro-tracker rename Arc to Fabro in all Rust crates, symbols, env vars, and supporting files 2026-03-12 12:25:58 -04:00
fabro-types rename Arc to Fabro in all Rust crates, symbols, env vars, and supporting files 2026-03-12 12:25:58 -04:00
fabro-util JSONL analytics event file format (#100) 2026-03-19 12:30:02 -04:00
fabro-validate Clean up fabro-validate extraction 2026-03-17 13:42:54 -04:00
fabro-workflows Add skip_clone to DaytonaConfig to fix concurrent test failures 2026-03-19 12:27:56 -04:00