@@ -13,7 +13,13 @@ import { noopAuditStore, permissiveAuthorize } from "@intx/agent/testing";
1313import { getLogger } from "@intx/log" ;
1414import { createOptimizedContextStore , loadRecentTurns } from "../session/optimized-context-store.js" ;
1515import { type } from "arktype" ;
16- import { buildCodexSource , buildOpenAISource , buildXaiSource , type Config } from "../config/index.js" ;
16+ import {
17+ buildCodexSource ,
18+ buildOpenAISource ,
19+ buildXaiSource ,
20+ refreshLiveProviderCatalog ,
21+ type Config ,
22+ } from "../config/index.js" ;
1723import {
1824 globalSettingsPath ,
1925 loadLocalSettings ,
@@ -31,11 +37,13 @@ import {
3137 markLastChangelogVersion ,
3238 toggleFavoriteModel ,
3339 type ModelRef ,
40+ type ResolvedProvider ,
3441 type Settings ,
3542 type LocalSettings ,
3643 type PluginConfig ,
3744} from "../config/settings.js" ;
38- import { providerChoices } from "../tui-opentui/provider-setup.js" ;
45+ import { unconnectedProviderChoices } from "../tui-opentui/provider-setup.js" ;
46+ import { connectProviderInline } from "../tui-opentui/provider-connect.js" ;
3947import type { SessionModeScope } from "../tui-opentui/command-surfaces.js" ;
4048import { resolveWaitForApproval , type ToolWatchdogConfig } from "./tool-execution-watchdog.js" ;
4149import { createGateRequestApproval } from "./request-approval.js" ;
@@ -1930,6 +1938,17 @@ export async function runTUI(initialConfig: Config): Promise<number> {
19301938 // Mount OpenTUI before the initial task is sent so gate and stream listeners
19311939 // are registered first. Ctrl+C stays with the shell (interrupt the run);
19321940 // OpenTUI owns the alternate screen and mouse reporting itself.
1941+ // Providers the picker offers as "connect →" rows: known choices minus
1942+ // whatever is already in the live catalog. Recomputed after a live connect
1943+ // so a newly authorized provider drops out of this list immediately.
1944+ const computeUnconnectedProviders = ( providers : Config [ "providers" ] ) =>
1945+ unconnectedProviderChoices ( providers ) . map ( ( choice ) => ( {
1946+ name : choice . id ,
1947+ label : choice . label ,
1948+ modelCount : choice . models . length ,
1949+ authKind : choice . oauth !== null ? ( "oauth" as const ) : ( "key" as const ) ,
1950+ } ) ) ;
1951+
19331952 const host = await mountRunnerHost ( {
19341953 // An unnamed session shows nothing rather than a placeholder.
19351954 title : runTaskTitle ,
@@ -1954,21 +1973,50 @@ export async function runTUI(initialConfig: Config): Promise<number> {
19541973 providers : config . providers ,
19551974 recentModels : listRecentModels ( config . settings ?? { providers : { } } ) ,
19561975 favoriteModels : listFavoriteModels ( config . settings ?? { providers : { } } ) ,
1957- unconnectedProviders : providerChoices ( )
1958- . filter ( ( choice ) => ! choice . custom )
1959- . filter ( ( choice ) => ! config . providers . some ( ( p ) => p . name === choice . id ) )
1960- . map ( ( choice ) => ( {
1961- name : choice . id ,
1962- label : choice . label ,
1963- modelCount : choice . models . length ,
1964- authKind : choice . oauth !== null ? ( "oauth" as const ) : ( "key" as const ) ,
1965- } ) ) ,
1976+ unconnectedProviders : computeUnconnectedProviders ( config . providers ) ,
19661977 onConnectProvider : ( providerName ) => {
1967- // Live inline connect (CL-5499) needs a text-input-capable overlay that
1968- // does not exist in shell.ts's list-overlay kit yet — see AGENTS report.
1969- systemRow (
1970- `Connecting ${ providerName } from the running session isn't wired up yet — run /model after restarting, or reconnect via onboarding.` ,
1971- ) ;
1978+ void ( async ( ) => {
1979+ let result : Awaited < ReturnType < typeof connectProviderInline > > ;
1980+ try {
1981+ result = await connectProviderInline ( {
1982+ providerId : providerName ,
1983+ settingsPath : trueGlobalSettingsPath ,
1984+ localSettingsPath : localSettingsFile ,
1985+ cwd : config . cwd ,
1986+ existing : config . settings ?? null ,
1987+ } ) ;
1988+ } catch ( err ) {
1989+ systemRow (
1990+ `Connecting ${ providerName } failed: ${ err instanceof Error ? err . message : String ( err ) } ` ,
1991+ ) ;
1992+ return ;
1993+ }
1994+ if ( ! result . connected ) return ;
1995+
1996+ const onDisk = await loadSettings ( trueGlobalSettingsPath ) ;
1997+ const resolvedForCatalog : ResolvedProvider = {
1998+ apiKey : config . apiKey ,
1999+ baseURL : config . baseURL ,
2000+ model : config . model ,
2001+ providerName : config . providerName ,
2002+ ...( config . keyless !== undefined ? { keyless : config . keyless } : { } ) ,
2003+ } ;
2004+ const providers = await refreshLiveProviderCatalog ( onDisk , resolvedForCatalog ) ;
2005+ config = { ...config , providers, ...( onDisk !== null ? { settings : onDisk } : { } ) } ;
2006+ liveSubAgentCatalog . current = providers ;
2007+ liveSubAgentSettings . current = config . settings ;
2008+ host . refreshModels (
2009+ listRecentModels ( config . settings ?? { providers : { } } ) ,
2010+ listFavoriteModels ( config . settings ?? { providers : { } } ) ,
2011+ providers ,
2012+ computeUnconnectedProviders ( providers ) ,
2013+ ) ;
2014+ systemRow ( `Connected ${ result . providerName ?? providerName } . Open /model to pick a model.` ) ;
2015+ } ) ( ) . catch ( ( err : unknown ) => {
2016+ tuiLogger . debug ( "provider connect failed: {error}" , {
2017+ error : err instanceof Error ? err . message : String ( err ) ,
2018+ } ) ;
2019+ } ) ;
19722020 } ,
19732021 modelLabel : ( ) => ( {
19742022 profile : config . providerName ,
0 commit comments