feat(coder-lite): add Press Enter to log in with GitHub

26b698d44e7f · AtlantisPleb · · parent 6a7d96d0b582

feat(coder-lite): add Press Enter to log in with GitHub

coder-lite now checks for an OPENAGENTS_API_KEY or stored token before
starting the TUI. If neither exists, it starts the backend device
authorization flow (POST /api/v1/device/authorizations), opens the
verification URL in the browser, polls the token endpoint, and stores the
resulting token in the openagents-cli credential store.

The --dev flag now sets OPENAGENTS_API_URL (origin) and OPENAGENTS_BASE_URL
(/api/v1 base) so the runtime and auth resolver agree on the local server,
and it stops pre-seeding the fake OPENAGENTS_API_KEY.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified crates/coder-lite/src/interactive.rs
  • modified crates/coder-lite/src/main.rs

Diff

2 files changed, +41 -3

crates/coder-lite/src/interactive.rs modified +37

@@ -25,6 +25,7 @@ use crossterm::{

25 25
    },
26 26
    terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
27 27
};
28
use openagents_cli::auth::{CredentialStore, DeviceClient, open_browser};
28 29
use openagents_cli::composer::complete::{Completion, complete};
29 30
use openagents_cli::composer::history::History;
30 31
use openagents_cli::composer::ComposerAction;

@@ -59,6 +60,8 @@ pub async fn run_tui(options: SessionOptions) -> Result<(), Box<dyn std::error::

59 60
        return Ok(());
60 61
    }
61 62
63
    ensure_authenticated().await?;
64
62 65
    let lane = Lane::from_str(&options.lane_name);
63 66
    let (tx, rx) = mpsc::channel::<Control>();
64 67
    let mut ui = CoderUi::new();

@@ -205,6 +208,40 @@ pub async fn run_tui(options: SessionOptions) -> Result<(), Box<dyn std::error::

205 208
    Ok(())
206 209
}
207 210
211
/// If no credential is already in the environment or store, start a GitHub
212
/// device-authorization flow and store the resulting token for the runtime to
213
/// spend. The TUI is not yet on the alternate screen, so this prints normally.
214
async fn ensure_authenticated() -> Result<(), Box<dyn std::error::Error>> {
215
    if std::env::var("OPENAGENTS_API_KEY").is_ok() {
216
        return Ok(());
217
    }
218
219
    let endpoint = openagents_cli::auth::resolve_endpoint(None, None)?;
220
    let store = CredentialStore::for_origin(&endpoint.origin);
221
    if store.find_token()?.is_some() {
222
        return Ok(());
223
    }
224
225
    println!("Press Enter to log in with GitHub.");
226
    let mut line = String::new();
227
    let mut stdin = tokio::io::BufReader::new(tokio::io::stdin());
228
    tokio::io::AsyncBufReadExt::read_line(&mut stdin, &mut line).await?;
229
230
    let client = DeviceClient::new(&endpoint.origin);
231
    let scopes: &[String] = &[];
232
    let auth = client.start(scopes).await?;
233
234
    println!("Opening {} in your browser...", auth.verification_uri_complete);
235
    open_browser(&auth.verification_uri_complete);
236
    println!("Waiting for approval...");
237
238
    let token = client.wait(&auth).await?;
239
    let _ = store.store(&token)?;
240
    println!("Authenticated.");
241
242
    Ok(())
243
}
244
208 245
/// The columns the composer soft-wraps to: the frame's width less its border
209 246
/// and the `" > "` gutter. It has to match what `CoderUi::render` uses, or the
210 247
/// caret is drawn a column off from where the next character lands.
crates/coder-lite/src/main.rs modified +4 -3

@@ -23,8 +23,8 @@ use tokio::time::{sleep, timeout};

23 23
24 24
use coder_lite::interactive::SessionOptions;
25 25
26
const DEV_API_URL: &str = "http://127.0.0.1:4000";
26 27
const DEV_BASE_URL: &str = "http://127.0.0.1:4000/api/v1";
27
const DEV_API_KEY: &str = "fake";
28 28
29 29
/// Every flag this binary reads. A flag listed here does what it says or the
30 30
/// binary refuses to start; there is nothing accepted-and-ignored.

@@ -48,8 +48,9 @@ Options:

48 48
  -V, --version      Print the version and exit.
49 49
50 50
Environment:
51
  OPENAGENTS_API_URL    The API origin to use. `--dev` sets it.
51 52
  OPENAGENTS_BASE_URL   The /api/v1 base to use. `--dev` sets it.
52
  OPENAGENTS_API_KEY    The credential to spend. Falls back to `oa auth`.
53
  OPENAGENTS_API_KEY    The credential to spend. Optional if signed in.
53 54
  OPENAGENTS_WEB_REPO   Where `--dev` looks for start_server.sh.
54 55
  ACP_REGISTRY          Where the `acp` tool looks for installed agents.
55 56

@@ -72,8 +73,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

72 73
                // before the TUI and its tokio tasks start, so no other thread
73 74
                // exists yet.
74 75
                unsafe {
76
                    env::set_var("OPENAGENTS_API_URL", DEV_API_URL);
75 77
                    env::set_var("OPENAGENTS_BASE_URL", DEV_BASE_URL);
76
                    env::set_var("OPENAGENTS_API_KEY", DEV_API_KEY);
77 78
                }
78 79
            }
79 80
            options

This page updates live while a promote is in flight · changelog