From 07d13ba15e55eba6d4d4976d828c36a3f6a31b1f Mon Sep 17 00:00:00 2001 From: can1357 Date: Tue, 26 May 2026 19:56:43 +0200 Subject: [PATCH] refactor(auth-broker): migrated OAuth flow from pi-ai CLI to AuthStorage - Migrated OAuth provider authentication from standalone `pi-ai` CLI to in-process `AuthStorage.login()` flow in coding-agent. - Made provider argument optional for `login` and `logout` commands with interactive provider picker when omitted. - Added `list` command to enumerate registered OAuth providers with optional `--json` output format. - Removed `pi-ai` CLI binary and `bin` entry from @oh-my-pi/ai package; library API remains unchanged. - Updated documentation and examples to reflect new `omp auth-broker` command interface and in-process OAuth flow. --- docs/auth-broker-gateway.md | 10 +- packages/ai/CHANGELOG.md | 4 + packages/ai/README.md | 11 +- packages/ai/package.json | 3 - packages/ai/src/cli.ts | 262 ------------------ packages/coding-agent/CHANGELOG.md | 13 + .../coding-agent/src/cli/auth-broker-cli.ts | 193 +++++++++++-- .../coding-agent/src/commands/auth-broker.ts | 3 + 8 files changed, 203 insertions(+), 296 deletions(-) delete mode 100755 packages/ai/src/cli.ts diff --git a/docs/auth-broker-gateway.md b/docs/auth-broker-gateway.md index 07aabda59..e13219a91 100644 --- a/docs/auth-broker-gateway.md +++ b/docs/auth-broker-gateway.md @@ -47,8 +47,9 @@ The broker is the only writer of OAuth refresh tokens. Clients (including the ga ``` omp auth-broker serve [--bind=host:port] # boot the broker omp auth-broker token [--regenerate] [--json] # print or rotate the bearer token -omp auth-broker login [--via=user@host] [--dry-run] -omp auth-broker logout +omp auth-broker login [] [--via=user@host] [--dry-run] +omp auth-broker logout [] +omp auth-broker list [--json] omp auth-broker import [--provider=] [--include-disabled] [--dry-run] [--json] omp auth-broker migrate --from-local [--dry-run] [--json] omp auth-broker status [--json] @@ -56,8 +57,9 @@ omp auth-broker status [--json] - `serve` opens the local SQLite store at `getAgentDbPath()` and binds an HTTP listener (default `127.0.0.1:8765`). On startup a token is ensured at `/auth-broker.token` (mode `0600`, `0700` parent dir). The background refresher refreshes any OAuth credential whose `expires - Date.now() < refreshSkewMs` (default 5 min) every `refreshIntervalMs` (default 60 s). - `token` prints the cached bearer or generates a new one. `--regenerate` rotates it. -- `login ` runs the per-provider OAuth flow locally, or — with `--via=user@host` — `ssh -L :127.0.0.1: user@host omp auth-broker login ` so the OAuth callback hits the local browser but the credential is written on the broker host. Built-in callback ports: `anthropic:54545`, `openai-codex:1455`, `google-gemini-cli:8085`, `google-antigravity:51121`, `gitlab-duo:8080`. -- `logout ` deletes every credential row for ``. +- `login []` runs the per-provider OAuth flow locally — when no provider is supplied, it falls back to an interactive numbered picker. With `--via=user@host` it shells out `ssh -L :127.0.0.1: user@host omp auth-broker login ` so the OAuth callback hits the local browser but the credential is written on the broker host (`--via` requires ``). Built-in callback ports: `anthropic:54545`, `openai-codex:1455`, `google-gemini-cli:8085`, `google-antigravity:51121`, `gitlab-duo:8080`. The OAuth dance is driven in-process via `AuthStorage.login()` — there is no longer a `pi-ai` bin to spawn. +- `logout []` deletes every credential row for ``. With no argument it shows an interactive numbered picker of currently-stored providers. +- `list` enumerates every registered OAuth provider id/name (the union of built-ins + `registerOAuthProvider` custom providers). `--json` emits a machine-readable array. - `import ` imports CLIProxyAPI-style JSON credentials into the local SQLite store. Maps `type` field → omp provider (`claude → anthropic`, `codex → openai-codex`, `gemini → google-gemini-cli`, `antigravity → google-antigravity`, `gemini-cli → google-gemini-cli`). - `migrate --from-local` walks the local SQLite store + env-derived credentials and idempotently uploads them to the configured broker (`POST /v1/credential`). - `status` health-pings the configured remote broker. diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 0a1bfdfc7..3c4c72fc4 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Removed + +- Removed the `pi-ai` CLI binary (`packages/ai/src/cli.ts`) and its `bin` entry. Use the in-process equivalent in the omp coding-agent CLI: `omp auth-broker login [provider]`, `omp auth-broker logout [provider]`, and `omp auth-broker list`. The library API (`AuthStorage.login()`, `getOAuthProviders()`, etc.) is unchanged. + ## [15.4.3] - 2026-05-26 ### Fixed diff --git a/packages/ai/README.md b/packages/ai/README.md index 3ada12a74..99be71531 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -1057,13 +1057,14 @@ Official docs: [Application Default Credentials](https://cloud.google.com/docs/a ### CLI Login -The quickest way to authenticate: +Authenticate via the [`omp`](https://omp.sh) coding-agent CLI, which drives this library's OAuth/API-key flows in-process and persists into `agent.db`: ```bash -bunx @oh-my-pi/pi-ai login # interactive provider selection -bunx @oh-my-pi/pi-ai login anthropic # login to specific provider -bunx @oh-my-pi/pi-ai login vllm # store vLLM API key (or placeholder for local no-auth) -bunx @oh-my-pi/pi-ai list # list available providers +omp auth-broker login # interactive provider selection +omp auth-broker login anthropic # login to a specific provider +omp auth-broker login vllm # store vLLM API key (or placeholder for local no-auth) +omp auth-broker list # list supported providers +omp auth-broker logout # interactive — pick a stored credential to remove ``` Credentials are saved to `agent.db` in the agent directory. `/login qianfan` opens the Qianfan console and stores the pasted API key. diff --git a/packages/ai/package.json b/packages/ai/package.json index dfeead8bf..a4de241be 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -28,9 +28,6 @@ ], "main": "./src/index.ts", "types": "./src/index.ts", - "bin": { - "pi-ai": "./src/cli.ts" - }, "scripts": { "check": "biome check . && bun run check:types", "check:types": "tsgo -p tsconfig.json --noEmit", diff --git a/packages/ai/src/cli.ts b/packages/ai/src/cli.ts deleted file mode 100755 index 66c5c69db..000000000 --- a/packages/ai/src/cli.ts +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/env bun -import * as readline from "node:readline"; -import { AuthStorage, SqliteAuthCredentialStore } from "./auth-storage"; -import { getOAuthProviders } from "./utils/oauth"; -import type { OAuthProvider } from "./utils/oauth/types"; - -const PROVIDERS = getOAuthProviders(); - -function prompt(rl: readline.Interface, question: string): Promise { - const { promise, resolve, reject } = Promise.withResolvers(); - const input = process.stdin as NodeJS.ReadStream; - const supportsRawMode = input.isTTY && typeof input.setRawMode === "function"; - const wasRaw = supportsRawMode ? input.isRaw : false; - let settled = false; - - const cleanup = () => { - rl.off("SIGINT", onSigint); - if (supportsRawMode) { - input.off("keypress", onKeypress); - input.setRawMode?.(wasRaw); - } - }; - - const finish = (result: () => void) => { - if (settled) return; - settled = true; - cleanup(); - result(); - }; - - const cancel = () => { - finish(() => reject(new Error("Login cancelled"))); - }; - - const onSigint = () => { - cancel(); - }; - - const onKeypress = (_str: string, key: readline.Key) => { - if (key.name === "escape" || (key.ctrl && key.name === "c")) { - cancel(); - rl.close(); - } - }; - - if (supportsRawMode) { - readline.emitKeypressEvents(input, rl); - input.setRawMode(true); - input.on("keypress", onKeypress); - } - - rl.once("SIGINT", onSigint); - rl.question(question, answer => { - finish(() => resolve(answer)); - }); - return promise; -} - -async function login(provider: OAuthProvider): Promise { - const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); - const promptFn = (msg: string) => prompt(rl, `${msg} `); - const store = await SqliteAuthCredentialStore.open(); - const storage = new AuthStorage(store); - await storage.reload(); - - try { - await storage.login(provider, { - onAuth(info) { - const { url, instructions } = info; - console.log(`\nOpen this URL in your browser:\n${url}`); - if (instructions) console.log(instructions); - console.log(); - }, - onProgress(message) { - console.log(message); - }, - onPrompt(p) { - return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`); - }, - }); - console.log(`\nCredentials saved to ~/.omp/agent/agent.db`); - } finally { - store.close(); - rl.close(); - } -} - -async function main(): Promise { - const args = process.argv.slice(2); - const command = args[0]; - - if (!command || command === "help" || command === "--help" || command === "-h") { - console.log(`Usage: bunx @oh-my-pi/pi-ai [provider] - -Commands: - login [provider] Login to a provider - logout [provider] Logout from a provider - status Show logged-in providers - list List available providers - -Providers: - anthropic Anthropic (Claude Pro/Max) - github-copilot GitHub Copilot - google-gemini-cli Google Gemini CLI - google-antigravity Antigravity (Gemini 3, Claude, GPT-OSS) - openai-codex OpenAI Codex (ChatGPT Plus/Pro) - kimi-code Kimi Code - kilo Kilo Gateway - kagi Kagi - tavily Tavily - zai Z.AI (GLM Coding Plan) - deepseek DeepSeek - nanogpt NanoGPT - minimax-code MiniMax Coding Plan (International) - minimax-code-cn MiniMax Coding Plan (China) - cursor Cursor (Claude, GPT, etc.) - zenmux ZenMux - ollama-cloud Ollama Cloud - -Examples: - bunx @oh-my-pi/pi-ai login # interactive provider selection - bunx @oh-my-pi/pi-ai login anthropic # login to specific provider - bunx @oh-my-pi/pi-ai logout anthropic # logout from specific provider - bunx @oh-my-pi/pi-ai status # show logged-in providers - bunx @oh-my-pi/pi-ai list # list providers -`); - return; - } - - if (command === "status") { - const storage = await SqliteAuthCredentialStore.open(); - try { - const providers = storage.listProviders(); - if (providers.length === 0) { - console.log("No credentials stored."); - console.log(`Use 'bunx @oh-my-pi/pi-ai login' to authenticate.`); - } else { - console.log("Logged-in providers:\n"); - for (const provider of providers) { - const oauth = storage.getOAuth(provider); - if (oauth) { - const expires = new Date(oauth.expires); - const expired = Date.now() >= oauth.expires; - const status = expired ? "(expired)" : `(expires ${expires.toLocaleString()})`; - console.log(` ${provider.padEnd(20)} ${status}`); - continue; - } - const apiKey = storage.getApiKey(provider); - if (apiKey) { - console.log(` ${provider.padEnd(20)} (api key)`); - } - } - } - } finally { - storage.close(); - } - return; - } - - if (command === "list") { - console.log("Available providers:\n"); - for (const p of PROVIDERS) { - console.log(` ${p.id.padEnd(20)} ${p.name}`); - } - return; - } - - if (command === "logout") { - let provider = args[1] as OAuthProvider | undefined; - const storage = await SqliteAuthCredentialStore.open(); - - try { - if (!provider) { - const providers = storage.listProviders(); - if (providers.length === 0) { - console.log("No credentials stored."); - return; - } - - const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); - console.log("Select a provider to logout:\n"); - for (let i = 0; i < providers.length; i++) { - console.log(` ${i + 1}. ${providers[i]}`); - } - console.log(); - - const choice = await prompt(rl, `Enter number (1-${providers.length}): `); - rl.close(); - - const index = parseInt(choice, 10) - 1; - if (index < 0 || index >= providers.length) { - console.error("Invalid selection"); - process.exit(1); - } - provider = providers[index] as OAuthProvider; - } - if (!provider) { - console.error("No provider selected"); - process.exit(1); - } - - const oauth = storage.getOAuth(provider); - const apiKey = storage.getApiKey(provider); - if (!oauth && !apiKey) { - console.error(`Not logged in to ${provider}`); - process.exit(1); - } - - storage.deleteProvider(provider); - console.log(`Logged out from ${provider}`); - } finally { - storage.close(); - } - return; - } - - if (command === "login") { - let provider = args[1] as OAuthProvider | undefined; - - if (!provider) { - const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); - console.log("Select a provider:\n"); - for (let i = 0; i < PROVIDERS.length; i++) { - console.log(` ${i + 1}. ${PROVIDERS[i].name}`); - } - console.log(); - - const choice = await prompt(rl, `Enter number (1-${PROVIDERS.length}): `); - rl.close(); - - const index = parseInt(choice, 10) - 1; - if (index < 0 || index >= PROVIDERS.length) { - console.error("Invalid selection"); - process.exit(1); - } - provider = PROVIDERS[index].id as OAuthProvider; - } - if (!provider) { - console.error("No provider selected"); - process.exit(1); - } - - if (!PROVIDERS.some(p => p.id === provider)) { - console.error(`Unknown provider: ${provider}`); - console.error(`Use 'bunx @oh-my-pi/pi-ai list' to see available providers`); - process.exit(1); - } - - console.log(`Logging in to ${provider}…`); - await login(provider); - return; - } - - console.error(`Unknown command: ${command}`); - console.error(`Use 'bunx @oh-my-pi/pi-ai --help' for usage`); - process.exit(1); -} - -main().catch(err => { - console.error("Error:", err.message); - process.exit(1); -}); diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 3658ac400..b4f718734 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -1,6 +1,19 @@ # Changelog ## [Unreleased] +### Added + +- Added interactive provider selection to `omp auth-broker logout` when no provider argument is supplied +- Added `--json` flag to `omp auth-broker list` for machine-readable output +- Added `omp auth-broker list` to enumerate supported OAuth providers (replaces `bunx @oh-my-pi/pi-ai list`). +- Added interactive provider selection to `omp auth-broker login` and `omp auth-broker logout` when no provider argument is supplied (replaces `bunx @oh-my-pi/pi-ai login` / `logout` interactive flows). + +### Changed + +- Changed `omp auth-broker login` to support interactive provider selection when invoked without a provider argument +- Changed `omp auth-broker logout` to support interactive provider selection from stored credentials when invoked without a provider argument +- Changed `omp auth-broker login` to drive the per-provider OAuth/API-key flow in-process via `AuthStorage.login()` instead of spawning the `pi-ai` CLI subprocess. The pi-ai bin is being removed; the same login surface now lives entirely inside `omp`. +- Changed default per-line truncation cap for search/grep output (`DEFAULT_MAX_COLUMN`) from `1024` to `512` characters. ## [15.4.3] - 2026-05-26 diff --git a/packages/coding-agent/src/cli/auth-broker-cli.ts b/packages/coding-agent/src/cli/auth-broker-cli.ts index 651d139b9..9356b5df2 100644 --- a/packages/coding-agent/src/cli/auth-broker-cli.ts +++ b/packages/coding-agent/src/cli/auth-broker-cli.ts @@ -17,6 +17,7 @@ import * as crypto from "node:crypto"; import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; +import * as readline from "node:readline"; import { AuthBrokerClient, type AuthCredential, @@ -28,6 +29,7 @@ import { listProvidersWithEnvKey, type OAuthCredential, type OAuthProvider, + type OAuthProviderInfo, SqliteAuthCredentialStore, startAuthBroker, } from "@oh-my-pi/pi-ai"; @@ -36,7 +38,7 @@ import { $ } from "bun"; import chalk from "chalk"; import { resolveAuthBrokerConfig } from "../session/auth-broker-config"; -export type AuthBrokerAction = "serve" | "token" | "login" | "logout" | "status" | "import" | "migrate"; +export type AuthBrokerAction = "serve" | "token" | "login" | "logout" | "status" | "import" | "migrate" | "list"; export interface AuthBrokerCommandArgs { action: AuthBrokerAction; @@ -60,7 +62,16 @@ export interface AuthBrokerCommandArgs { }; } -const ACTIONS: readonly AuthBrokerAction[] = ["serve", "token", "login", "logout", "import", "migrate", "status"]; +const ACTIONS: readonly AuthBrokerAction[] = [ + "serve", + "token", + "login", + "logout", + "import", + "migrate", + "status", + "list", +]; /** Callback ports baked from the per-provider OAuth flow modules. */ const CALLBACK_PORTS: Record = { @@ -168,13 +179,23 @@ async function runToken(flags: AuthBrokerCommandArgs["flags"]): Promise { } async function runLogin(flags: AuthBrokerCommandArgs["flags"]): Promise { - const providerArg = flags.provider; + const providers = getOAuthProviders(); + let providerArg = flags.provider; if (!providerArg) { - throw new Error("Usage: omp auth-broker login [--via=user@host]"); + if (flags.via) { + throw new Error( + "Usage: omp auth-broker login --via=user@host (provider required for remote login)", + ); + } + providerArg = await pickProviderInteractively(providers); } - const oauthProviders = new Set(getOAuthProviders().map(p => p.id)); - if (!oauthProviders.has(providerArg)) { - throw new Error(`Unknown OAuth provider '${providerArg}'. Known: ${[...oauthProviders].sort().join(", ")}`); + if (!providers.some(p => p.id === providerArg)) { + throw new Error( + `Unknown OAuth provider '${providerArg}'. Known: ${providers + .map(p => p.id) + .sort() + .join(", ")}`, + ); } if (flags.via) { await runRemoteLogin(providerArg, flags.via, flags.dryRun ?? false); @@ -184,18 +205,107 @@ async function runLogin(flags: AuthBrokerCommandArgs["flags"]): Promise { } async function runLocalLogin(provider: OAuthProvider): Promise { - // Spawn the pi-ai CLI in-process — it handles the per-provider OAuth dance - // and persists into the same SQLite store the broker uses. - const piAiCli = Bun.fileURLToPath(import.meta.resolve("@oh-my-pi/pi-ai/cli")); - const proc = Bun.spawn({ - cmd: [process.execPath, piAiCli, "login", provider], - stdin: "inherit", - stdout: "inherit", - stderr: "inherit", + // Drive the per-provider OAuth dance in-process. Persists into the same + // SQLite store the broker uses. + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + const ask = (msg: string) => promptLine(rl, `${msg} `); + const store = await SqliteAuthCredentialStore.open(getAgentDbPath()); + const storage = new AuthStorage(store); + await storage.reload(); + try { + await storage.login(provider, { + onAuth({ url, instructions }) { + process.stdout.write(`\nOpen this URL in your browser:\n${url}\n`); + if (instructions) process.stdout.write(`${instructions}\n`); + process.stdout.write("\n"); + }, + onProgress(message) { + process.stdout.write(`${message}\n`); + }, + onPrompt(p) { + return ask(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`); + }, + }); + process.stdout.write(`\nCredentials saved to ${getAgentDbPath()}\n`); + } finally { + store.close(); + rl.close(); + } +} + +/** + * Interactive `readline` prompt that cleanly tears down on Ctrl-C / Escape so + * cancelling a half-finished login flow doesn't leave the terminal in raw mode. + */ +function promptLine(rl: readline.Interface, question: string): Promise { + const { promise, resolve, reject } = Promise.withResolvers(); + const input = process.stdin as NodeJS.ReadStream; + const supportsRawMode = input.isTTY && typeof input.setRawMode === "function"; + const wasRaw = supportsRawMode ? input.isRaw : false; + let settled = false; + + const cleanup = () => { + rl.off("SIGINT", onSigint); + if (supportsRawMode) { + input.off("keypress", onKeypress); + input.setRawMode?.(wasRaw); + } + }; + + const finish = (result: () => void) => { + if (settled) return; + settled = true; + cleanup(); + result(); + }; + + const cancel = () => { + finish(() => reject(new Error("Login cancelled"))); + }; + + const onSigint = () => { + cancel(); + }; + + const onKeypress = (_str: string, key: readline.Key) => { + if (key.name === "escape" || (key.ctrl && key.name === "c")) { + cancel(); + rl.close(); + } + }; + + if (supportsRawMode) { + readline.emitKeypressEvents(input, rl); + input.setRawMode(true); + input.on("keypress", onKeypress); + } + + rl.once("SIGINT", onSigint); + rl.question(question, answer => { + finish(() => resolve(answer)); }); - const exitCode = await proc.exited; - if (exitCode !== 0) { - throw new Error(`pi-ai login exited with code ${exitCode}`); + return promise; +} + +async function pickProviderInteractively(providers: readonly OAuthProviderInfo[]): Promise { + if (providers.length === 0) { + throw new Error("No OAuth providers registered"); + } + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + try { + process.stdout.write("Select a provider:\n\n"); + for (let i = 0; i < providers.length; i++) { + process.stdout.write(` ${i + 1}. ${providers[i].name}\n`); + } + process.stdout.write("\n"); + const choice = await promptLine(rl, `Enter number (1-${providers.length}): `); + const index = Number.parseInt(choice, 10) - 1; + if (Number.isNaN(index) || index < 0 || index >= providers.length) { + throw new Error(`Invalid selection: ${choice}`); + } + return providers[index].id; + } finally { + rl.close(); } } @@ -235,12 +345,17 @@ async function runRemoteLogin(provider: string, via: string, dryRun: boolean): P } async function runLogout(flags: AuthBrokerCommandArgs["flags"]): Promise { - const providerArg = flags.provider; - if (!providerArg) { - throw new Error("Usage: omp auth-broker logout "); - } + let providerArg = flags.provider; const store = await SqliteAuthCredentialStore.open(getAgentDbPath()); try { + if (!providerArg) { + const stored = store.listProviders(); + if (stored.length === 0) { + process.stdout.write("No credentials stored.\n"); + return; + } + providerArg = await pickStoredProviderInteractively(stored); + } store.deleteAuthCredentialsForProvider(providerArg, "logged out by user"); process.stdout.write(`Logged out of ${providerArg}\n`); } finally { @@ -248,6 +363,37 @@ async function runLogout(flags: AuthBrokerCommandArgs["flags"]): Promise { } } +async function pickStoredProviderInteractively(providers: string[]): Promise { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + try { + process.stdout.write("Select a provider to logout:\n\n"); + for (let i = 0; i < providers.length; i++) { + process.stdout.write(` ${i + 1}. ${providers[i]}\n`); + } + process.stdout.write("\n"); + const choice = await promptLine(rl, `Enter number (1-${providers.length}): `); + const index = Number.parseInt(choice, 10) - 1; + if (Number.isNaN(index) || index < 0 || index >= providers.length) { + throw new Error(`Invalid selection: ${choice}`); + } + return providers[index]; + } finally { + rl.close(); + } +} + +async function runList(flags: AuthBrokerCommandArgs["flags"]): Promise { + const providers = getOAuthProviders(); + if (flags.json) { + process.stdout.write(`${JSON.stringify(providers.map(p => ({ id: p.id, name: p.name })))}\n`); + return; + } + process.stdout.write("Available providers:\n\n"); + for (const p of providers) { + process.stdout.write(` ${p.id.padEnd(20)} ${p.name}\n`); + } +} + // ─── CLIProxyAPI import ───────────────────────────────────────────────── /** @@ -732,6 +878,9 @@ export async function runAuthBrokerCommand(cmd: AuthBrokerCommandArgs): Promise< case "status": await runStatus(cmd.flags); return; + case "list": + await runList(cmd.flags); + return; default: { // Exhaustive check. const _exhaustive: never = cmd.action; diff --git a/packages/coding-agent/src/commands/auth-broker.ts b/packages/coding-agent/src/commands/auth-broker.ts index c6beb1f14..85bb5906e 100644 --- a/packages/coding-agent/src/commands/auth-broker.ts +++ b/packages/coding-agent/src/commands/auth-broker.ts @@ -56,8 +56,11 @@ export default class AuthBroker extends Command { "# Boot on a non-default port\n omp auth-broker serve --bind=127.0.0.1:9000", "# Print the bearer token\n omp auth-broker token", "# Rotate the bearer token\n omp auth-broker token --regenerate", + "# List supported OAuth providers\n omp auth-broker list", "# Local login (run on the broker host)\n omp auth-broker login anthropic", + "# Interactive provider selection\n omp auth-broker login", "# Remote login over SSH tunnel\n omp auth-broker login anthropic --via=user@broker", + "# Log out of a provider (interactive without provider arg)\n omp auth-broker logout anthropic", "# Import a CLIProxyAPI auth dump\n omp auth-broker import ~/.cliproxy/auth", "# Import a single CLIProxyAPI JSON, overriding the provider mapping\n omp auth-broker import ~/.cliproxy/auth/claude-foo.json --provider anthropic", "# Preview a migration from local store + env vars to the configured broker\n omp auth-broker migrate --from-local --include-env --dry-run",