Skip to content

Extend interactive backend sign-in to codex, gemini and qwen #332

Description

@saucam

feat: sign in to a backend from codeoid, instead of pasting an API key (#331) wired the mechanism and one backend. Claude works; codex, gemini and qwen do not.

What is already generic

The broker (src/daemon/auth/backend-login.ts) has no per-backend branching. A backend is a LoginFlow entry:

{
  backend: "codex",
  resolve(): { file, args } | null,   // the login command, or null if absent
  urlPattern: RegExp,                 // the authorize URL in raw terminal output
  failurePattern: RegExp,             // the vendor's own rejection
  codeHint: string,                   // what the user brings back
  extractSecret(transcript): { key, value } | null,
}

…plus the backend id in three places: LoginBackend, LOGIN_CAPABLE_BACKENDS (protocol), and loginBackendField (the zod enum). The UI needs CREDENTIAL_KEY and LABEL entries in BackendLoginPanel.tsx and nothing else — the panel renders for any tab whose id is login-capable.

Everything hard is already done and shared: the pty, timeouts, cancellation by process group, single-attempt-per-backend, redaction, and the rule that a pty-echoed input is never mistaken for the vendor's answer.

What is NOT generic, and why this is not one commit

The patterns can only be written after watching the real command. Every assumption in the Claude flow came from a captured transcript, and two of them were surprises: setup-token writes nothing at all to a pipe (it needs a pty, it does not degrade), and it keeps running after a rejected code to offer a retry. Neither is in any documentation. Guessing the equivalents for three more CLIs would produce three plausible flows that fail on contact.

So each backend is: capture a transcript, read it, write the entry, add a fake-command test modelled on that transcript.

Per backend

  • codexcodex login. codeoid already detects the result (~/.codex/auth.json, see the Codex settings tab), so only the interactive half is missing. Open question worth resolving first: whether it is a paste-a-code flow at all or a localhost callback, which a remote daemon cannot receive. If it is localhost-only, that is a different design (a tunnelled callback), not a LoginFlow — say so in the issue rather than forcing it.
  • gemini — the Gemini CLI's own sign-in. Same first question: out-of-band code or localhost callback.
  • qwen — codeoid already reads ~/.qwen/oauth_creds.json and picks qwen-oauth over openai when it exists (src/daemon/providers/qwen/index.ts), so the credential path is proven; only the login is out-of-band today.

Note on extractSecret

Returning null is not failure — it means "nothing for codeoid to store", and the attempt then succeeds on the command's exit status. A backend that writes its credential to disk (codex and qwen both do) may legitimately need no secret extraction at all. That is the easy case.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions