From 7726b6d42aa96f5ba2a884040580468970bbf456 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Thu, 26 Feb 2026 09:28:47 -0800 Subject: [PATCH] feat(tui): extend /login parser to accept mode subcommand Add support for `/login mode ` syntax to allow switching authentication mode per provider in the TUI. The Command union type now includes an optional mode field, and parseCommand handles regex matching for the "mode" subcommand pattern while maintaining backward compatibility. Co-Authored-By: Claude Sonnet 4.6 --- src/frontends/tui/commands.test.ts | 19 +++++++++++++++++++ src/frontends/tui/commands.ts | 19 ++++++++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/frontends/tui/commands.test.ts b/src/frontends/tui/commands.test.ts index 9478979..eca95e8 100644 --- a/src/frontends/tui/commands.test.ts +++ b/src/frontends/tui/commands.test.ts @@ -188,6 +188,25 @@ describe('/pair command', () => { }); }); +describe('/login command with mode subcommand', () => { + it('parses /login with mode subcommand', () => { + expect(parseCommand('/login anthropic mode oauth')).toEqual({ + type: 'login', provider: 'anthropic', mode: 'oauth', + }); + expect(parseCommand('/login openai mode api_key')).toEqual({ + type: 'login', provider: 'openai', mode: 'api_key', + }); + expect(parseCommand('/login anthropic mode auto')).toEqual({ + type: 'login', provider: 'anthropic', mode: 'auto', + }); + }); + + it('parses /login without mode unchanged', () => { + expect(parseCommand('/login')).toEqual({ type: 'login' }); + expect(parseCommand('/login anthropic')).toEqual({ type: 'login', provider: 'anthropic' }); + }); +}); + describe('PROVIDER_NAMES', () => { it('matches all providers from the config schema', () => { expect(PROVIDER_NAMES).toEqual(MODEL_PROVIDERS); diff --git a/src/frontends/tui/commands.ts b/src/frontends/tui/commands.ts index bebe25c..b675a0f 100644 --- a/src/frontends/tui/commands.ts +++ b/src/frontends/tui/commands.ts @@ -15,7 +15,7 @@ export type Command = | { type: 'model'; name?: string; providerModel?: string } | { type: 'backend'; provider?: string } | { type: 'runtime'; input?: string } - | { type: 'login'; provider?: string } + | { type: 'login'; provider?: string; mode?: 'api_key' | 'oauth' | 'auto' } | { type: 'transfer'; target: string } | { type: 'pair'; action?: 'generate' | 'list' | 'revoke'; args?: string } | { type: 'queue'; action?: 'show' | 'set' | 'reset'; args?: string } @@ -178,8 +178,16 @@ export function parseCommand(input: string): Command | null { return { type: 'login' }; } if (trimmed.startsWith('/login ')) { - const provider = trimmed.slice('/login '.length).trim(); - return { type: 'login', provider: provider || undefined }; + const rest = trimmed.slice('/login '.length).trim(); + // /login mode + const modeMatch = rest.match(/^(\S+)\s+mode\s+(\S+)$/); + if (modeMatch) { + const modeValue = modeMatch[2].toLowerCase(); + if (modeValue === 'api_key' || modeValue === 'oauth' || modeValue === 'auto') { + return { type: 'login', provider: modeMatch[1] || undefined, mode: modeValue }; + } + } + return { type: 'login', provider: rest || undefined }; } // Pair @@ -237,7 +245,8 @@ Commands: /research Delegate a task to the configured research agent /council Run the councils pipeline for a task /council preflight Check council tier routing, endpoint/auth mode, and probe latency - /login [provider] Authenticate with GitHub, OpenAI, Anthropic, or Z.AI + /login [provider] Authenticate (github, openai, anthropic, zai) + /login

mode Set auth mode for provider (api_key|oauth|auto) /pair List pending pairing codes and approved senders /pair generate [label] Generate a new DM pairing code /pair revoke Revoke an approved sender @@ -318,7 +327,7 @@ export const COMMAND_TOOLTIPS: Record = { '/status': 'Show session info and token usage', '/fullscreen': 'Switch to fullscreen mode', '/fs': 'Switch to fullscreen mode', - '/login': 'Authenticate with GitHub/OpenAI/Anthropic (OAuth/token or API key) or Z.AI (API key store)', + '/login': 'Authenticate with provider; use "mode api_key|oauth|auto" to switch auth mode', '/pair': 'Generate/list/revoke DM pairing codes', '/queue': 'Show or update per-session queue policy', '/approvals': 'List pending guarded actions for this session',