@@ -42,6 +42,63 @@ type ResolvedChannelAccountRowParams = {
4242 accountId : string ;
4343} ;
4444
45+ function getLiveChannelAccounts ( params : {
46+ liveChannelStatus : unknown ;
47+ channelId : string ;
48+ } ) : Array < Record < string , unknown > > {
49+ const payload = asRecord ( params . liveChannelStatus ) ;
50+ const accountsByChannel = asRecord ( payload . channelAccounts ) ;
51+ const raw = accountsByChannel [ params . channelId ] ;
52+ return Array . isArray ( raw ) ? raw . map ( asRecord ) : [ ] ;
53+ }
54+
55+ function getLiveAccountId ( account : Record < string , unknown > ) : string {
56+ return (
57+ normalizeOptionalString ( account . accountId ) ??
58+ normalizeOptionalString ( account . id ) ??
59+ normalizeOptionalString ( account . name ) ??
60+ "default"
61+ ) ;
62+ }
63+
64+ function findLiveChannelAccount ( params : {
65+ liveAccounts : Array < Record < string , unknown > > ;
66+ accountId : string ;
67+ } ) : Record < string , unknown > | null {
68+ return (
69+ params . liveAccounts . find ( ( account ) => getLiveAccountId ( account ) === params . accountId ) ??
70+ ( params . accountId === "default" && params . liveAccounts . length === 1
71+ ? ( params . liveAccounts [ 0 ] ?? null )
72+ : null )
73+ ) ;
74+ }
75+
76+ function hasLiveCredentialAvailable ( params : {
77+ liveAccounts : Array < Record < string , unknown > > ;
78+ accountId : string ;
79+ } ) : boolean {
80+ const account = findLiveChannelAccount ( params ) ;
81+ if ( ! account ) {
82+ return false ;
83+ }
84+ if ( hasConfiguredUnavailableCredentialStatus ( account ) ) {
85+ return false ;
86+ }
87+ return account . running === true || account . connected === true ;
88+ }
89+
90+ function markConfiguredUnavailableCredentialStatusesAvailable (
91+ account : unknown ,
92+ ) : Record < string , unknown > {
93+ const record = { ...asRecord ( account ) } ;
94+ for ( const key of [ "tokenStatus" , "botTokenStatus" , "appTokenStatus" , "signingSecretStatus" ] ) {
95+ if ( record [ key ] === "configured_unavailable" ) {
96+ record [ key ] = "available" ;
97+ }
98+ }
99+ return record ;
100+ }
101+
45102function existsSyncMaybe ( p : string | undefined ) : boolean | null {
46103 const path = normalizeOptionalString ( p ) ?? "" ;
47104 if ( ! path ) {
@@ -87,6 +144,7 @@ const buildAccountNotes = (params: {
87144 plugin : ChannelPlugin ;
88145 cfg : OpenClawConfig ;
89146 entry : ChannelAccountRow ;
147+ liveCredentialAvailable ?: boolean ;
90148} ) => {
91149 const { plugin, cfg, entry } = params ;
92150 const notes : string [ ] = [ ] ;
@@ -112,7 +170,9 @@ const buildAccountNotes = (params: {
112170 ) {
113171 notes . push ( `signing:${ snapshot . signingSecretSource } ` ) ;
114172 }
115- if ( hasConfiguredUnavailableCredentialStatus ( entry . account ) ) {
173+ if ( params . liveCredentialAvailable ) {
174+ notes . push ( "credential available in gateway runtime" ) ;
175+ } else if ( hasConfiguredUnavailableCredentialStatus ( entry . account ) ) {
116176 notes . push ( "secret unavailable in this command path" ) ;
117177 }
118178 if ( snapshot . baseUrl ) {
@@ -192,6 +252,7 @@ export async function buildChannelsTable(
192252 showSecrets ?: boolean ;
193253 sourceConfig ?: OpenClawConfig ;
194254 includeSetupFallbackPlugins ?: boolean ;
255+ liveChannelStatus ?: unknown ;
195256 } ,
196257) : Promise < {
197258 rows : ChannelRow [ ] ;
@@ -234,12 +295,27 @@ export async function buildChannelsTable(
234295 } ) ,
235296 ) ;
236297 }
298+ const liveAccounts = getLiveChannelAccounts ( {
299+ liveChannelStatus : opts ?. liveChannelStatus ,
300+ channelId : plugin . id ,
301+ } ) ;
237302
238303 const anyEnabled = accounts . some ( ( a ) => a . enabled ) ;
239304 const enabledAccounts = accounts . filter ( ( a ) => a . enabled ) ;
240305 const configuredAccounts = enabledAccounts . filter ( ( a ) => a . configured ) ;
241- const unavailableConfiguredAccounts = enabledAccounts . filter ( ( a ) =>
242- hasConfiguredUnavailableCredentialStatus ( a . account ) ,
306+ const unavailableConfiguredAccounts = enabledAccounts . filter (
307+ ( a ) =>
308+ hasConfiguredUnavailableCredentialStatus ( a . account ) &&
309+ ! hasLiveCredentialAvailable ( { liveAccounts, accountId : a . accountId } ) ,
310+ ) ;
311+ const accountsForTokenSummary = accounts . map ( ( entry ) =>
312+ hasConfiguredUnavailableCredentialStatus ( entry . account ) &&
313+ hasLiveCredentialAvailable ( { liveAccounts, accountId : entry . accountId } )
314+ ? {
315+ ...entry ,
316+ account : markConfiguredUnavailableCredentialStatusesAvailable ( entry . account ) ,
317+ }
318+ : entry ,
243319 ) ;
244320 const defaultEntry = accounts . find ( ( a ) => a . accountId === defaultAccountId ) ?? accounts [ 0 ] ;
245321
@@ -256,7 +332,7 @@ export async function buildChannelsTable(
256332 const link = resolveLinkFields ( summary ) ;
257333 const missingPaths = collectMissingPaths ( enabledAccounts ) ;
258334 const tokenSummary = summarizeTokenConfig ( {
259- accounts,
335+ accounts : accountsForTokenSummary ,
260336 showSecrets,
261337 } ) ;
262338
@@ -383,14 +459,19 @@ export async function buildChannelsTable(
383459 title : `${ label } accounts` ,
384460 columns : [ "Account" , "Status" , "Notes" ] ,
385461 rows : configuredAccounts . map ( ( entry ) => {
386- const notes = buildAccountNotes ( { plugin, cfg, entry } ) ;
462+ const liveCredentialAvailable = hasLiveCredentialAvailable ( {
463+ liveAccounts,
464+ accountId : entry . accountId ,
465+ } ) ;
466+ const notes = buildAccountNotes ( { plugin, cfg, entry, liveCredentialAvailable } ) ;
387467 return {
388468 Account : formatAccountLabel ( {
389469 accountId : entry . accountId ,
390470 name : entry . snapshot . name ,
391471 } ) ,
392472 Status :
393- entry . enabled && ! hasConfiguredUnavailableCredentialStatus ( entry . account )
473+ entry . enabled &&
474+ ( ! hasConfiguredUnavailableCredentialStatus ( entry . account ) || liveCredentialAvailable )
394475 ? "OK"
395476 : "WARN" ,
396477 Notes : notes . join ( " · " ) ,
0 commit comments