feat(tui): extend /login parser to accept mode subcommand

Add support for `/login <provider> mode <api_key|oauth|auto>` 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 <noreply@anthropic.com>
This commit is contained in:
William Valentin
2026-02-26 09:28:47 -08:00
parent 01e4e43a88
commit 7726b6d42a
2 changed files with 33 additions and 5 deletions
+19
View File
@@ -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);
+14 -5
View File
@@ -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 <provider> mode <value>
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 <task> Delegate a task to the configured research agent
/council <task> 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 <p> mode <m> 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 <ch> <id> Revoke an approved sender
@@ -318,7 +327,7 @@ export const COMMAND_TOOLTIPS: Record<string, string> = {
'/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',