





















@@ -42,6 +42,63 @@ type ResolvedChannelAccountRowParams = {
4242accountId: string;
4343};
444445+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 {
46103const path = normalizeOptionalString(p) ?? "";
47104if (!path) {
@@ -87,6 +144,7 @@ const buildAccountNotes = (params: {
87144plugin: ChannelPlugin;
88145cfg: OpenClawConfig;
89146entry: ChannelAccountRow;
147+liveCredentialAvailable?: boolean;
90148}) => {
91149const { plugin, cfg, entry } = params;
92150const notes: string[] = [];
@@ -112,7 +170,9 @@ const buildAccountNotes = (params: {
112170) {
113171notes.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)) {
116176notes.push("secret unavailable in this command path");
117177}
118178if (snapshot.baseUrl) {
@@ -192,6 +252,7 @@ export async function buildChannelsTable(
192252showSecrets?: boolean;
193253sourceConfig?: OpenClawConfig;
194254includeSetupFallbackPlugins?: boolean;
255+liveChannelStatus?: unknown;
195256},
196257): Promise<{
197258rows: 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+});
237302238303const anyEnabled = accounts.some((a) => a.enabled);
239304const enabledAccounts = accounts.filter((a) => a.enabled);
240305const 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);
244320const defaultEntry = accounts.find((a) => a.accountId === defaultAccountId) ?? accounts[0];
245321@@ -256,7 +332,7 @@ export async function buildChannelsTable(
256332const link = resolveLinkFields(summary);
257333const missingPaths = collectMissingPaths(enabledAccounts);
258334const tokenSummary = summarizeTokenConfig({
259- accounts,
335+accounts: accountsForTokenSummary,
260336 showSecrets,
261337});
262338@@ -383,14 +459,19 @@ export async function buildChannelsTable(
383459title: `${label} accounts`,
384460columns: ["Account", "Status", "Notes"],
385461rows: 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 });
387467return {
388468Account: formatAccountLabel({
389469accountId: entry.accountId,
390470name: entry.snapshot.name,
391471}),
392472Status:
393-entry.enabled && !hasConfiguredUnavailableCredentialStatus(entry.account)
473+entry.enabled &&
474+(!hasConfiguredUnavailableCredentialStatus(entry.account) || liveCredentialAvailable)
394475 ? "OK"
395476 : "WARN",
396477Notes: notes.join(" · "),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。