fix(status): show configured channels without gateway

This commit is contained in:
Vincent Koc
2026-05-02 14:16:38 -07:00
parent 0841bcdc01
commit 871cd475af
5 changed files with 22 additions and 6 deletions

View File

@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Plugins/externalization: keep ACPX, Google Chat, and LINE publishable plugin dist trees out of the core npm package file list.
- Status/channels: show configured channels in `openclaw status` and config-only `openclaw channels status` output even when the Gateway is unreachable, avoiding empty Channels tables on WSL and other no-Gateway paths. Thanks @vincentkoc.
- Plugins/ClawHub: explain unavailable explicit ClawHub ClawPack artifact downloads with a temporary npm install hint while ClawHub artifact routing rolls out. Thanks @vincentkoc.
- Media: accept home-relative `MEDIA:~/...` attachment paths while preserving existing file-read policy, traversal checks, and media type validation. Fixes #73796. Thanks @fabkury.
- Onboarding/search: install official external web-search plugins such as Brave before saving provider config, and make doctor repair reconcile selected external search providers whose npm payload is missing. Thanks @vincentkoc.

View File

@@ -4,6 +4,7 @@ import { makeDirectPlugin } from "../test-utils/channel-plugin-test-fixtures.js"
import { formatConfigChannelsStatusLines } from "./channels/status-config-format.js";
const activeChannelPlugins = vi.hoisted(() => [] as ChannelPlugin[]);
const listReadOnlyChannelPluginsForConfig = vi.hoisted(() => vi.fn(() => activeChannelPlugins));
vi.mock("../channels/plugins/index.js", () => ({
listChannelPlugins: () => activeChannelPlugins,
@@ -12,7 +13,7 @@ vi.mock("../channels/plugins/index.js", () => ({
}));
vi.mock("../channels/plugins/read-only.js", () => ({
listReadOnlyChannelPluginsForConfig: () => activeChannelPlugins,
listReadOnlyChannelPluginsForConfig,
}));
vi.mock("../channels/plugins/status.js", () => ({
@@ -191,6 +192,20 @@ function expectResolvedTokenStatusSummary(
}
describe("config-only channels status output", () => {
it("uses setup fallback plugins so configured external channels can be shown", async () => {
registerSingleTestPlugin("token-only", makeUnavailableTokenPlugin());
listReadOnlyChannelPluginsForConfig.mockClear();
await formatLocalStatusSummary({ channels: { "token-only": { enabled: true } } });
expect(listReadOnlyChannelPluginsForConfig).toHaveBeenCalledWith(
expect.any(Object),
expect.objectContaining({
includeSetupFallbackPlugins: true,
}),
);
});
it("shows configured-but-unavailable credentials distinctly from not configured", async () => {
registerSingleTestPlugin("token-only", makeUnavailableTokenPlugin());

View File

@@ -60,7 +60,7 @@ export async function formatConfigChannelsStatusLines(
const sourceConfig = opts?.sourceConfig ?? cfg;
const plugins = listReadOnlyChannelPluginsForConfig(cfg, {
activationSourceConfig: sourceConfig,
includeSetupFallbackPlugins: false,
includeSetupFallbackPlugins: true,
});
for (const plugin of plugins) {
const accountIds = plugin.config.listAccountIds(cfg);

View File

@@ -90,13 +90,13 @@ describe("scanStatus", () => {
expect(mocks.buildChannelsTable).toHaveBeenCalledWith(
expect.objectContaining({ marker: "resolved" }),
expect.objectContaining({
includeSetupFallbackPlugins: false,
includeSetupFallbackPlugins: true,
sourceConfig: expect.objectContaining({ marker: "source" }),
}),
);
});
it("keeps default text status off live channel status and setup runtime fallback", async () => {
it("keeps default text status off live channel status while keeping configured channel setup fallback", async () => {
configureScanStatus({ hasConfiguredChannels: true });
mocks.probeGateway.mockResolvedValue({
ok: true,
@@ -117,7 +117,7 @@ describe("scanStatus", () => {
);
expect(mocks.buildChannelsTable).toHaveBeenCalledWith(
expect.any(Object),
expect.objectContaining({ includeSetupFallbackPlugins: false }),
expect.objectContaining({ includeSetupFallbackPlugins: true }),
);
});

View File

@@ -53,7 +53,7 @@ export async function scanStatus(
opts,
showSecrets: process.env.OPENCLAW_SHOW_SECRETS?.trim() !== "0",
includeLiveChannelStatus: includeLiveChannelChecks,
includeChannelSetupRuntimeFallback: includeLiveChannelChecks,
includeChannelSetupRuntimeFallback: true,
progress,
labels: {
loadingConfig: "Loading config…",