mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 13:00:44 +00:00
fix(cli): show configured chat channels in list
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/models: keep per-agent primary models strict when `fallbacks` is omitted, so probe-only custom providers are not tried as hidden fallback candidates unless the agent explicitly opts in. Fixes #73332. Thanks @haumanto.
|
||||
- Cron/Telegram: preserve explicit `:topic:` delivery targets over stale session-derived thread IDs when isolated cron announces to Telegram forum topics. Carries forward #59069; refs #49704 and #43808. Thanks @roytong9.
|
||||
- Build/runtime: write the runtime-postbuild stamp after `pnpm build` writes the build stamp, so the next CLI invocation does not re-sync runtime artifacts after a successful build. Fixes #73151. Thanks @bittoby.
|
||||
- CLI/channels: list configured chat channel accounts from read-only setup metadata even when the standalone CLI has not loaded the runtime channel registry, so `openclaw channels list` shows Telegram accounts before auth providers. Fixes #73319 and #73322. Thanks @mlaihk.
|
||||
- CLI/model probes: reject empty or whitespace-only `infer model run --prompt` values before calling local providers or the Gateway, so smoke checks do not spend provider calls on invalid turns. Fixes #73185. Thanks @iot2edge.
|
||||
- Gateway/media: route text-only `chat.send` image offloads through media-understanding fields so `agents.defaults.imageModel` can describe WebChat attachments instead of leaving only an opaque `media://inbound` marker. Fixes #72968. Thanks @vorajeeah.
|
||||
- Gateway/Windows: route no-listener restart handoffs through the Windows supervisor without leaving restart tokens in flight, so failed task scheduling can be retried and successful handoffs do not coalesce later restart requests. (#69056) Thanks @Thatgfsj.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { baseConfigSnapshot, createTestRuntime } from "./test-runtime-config-helpers.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -9,7 +9,8 @@ const mocks = vi.hoisted(() => ({
|
||||
diagnostics: [],
|
||||
})),
|
||||
loadAuthProfileStoreWithoutExternalProfiles: vi.fn(),
|
||||
listChannelPlugins: vi.fn(() => []),
|
||||
listReadOnlyChannelPluginsForConfig: vi.fn(() => []),
|
||||
buildChannelAccountSnapshot: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
@@ -28,13 +29,26 @@ vi.mock("../agents/auth-profiles.js", () => ({
|
||||
loadAuthProfileStoreWithoutExternalProfiles: mocks.loadAuthProfileStoreWithoutExternalProfiles,
|
||||
}));
|
||||
|
||||
vi.mock("../channels/plugins/index.js", () => ({
|
||||
listChannelPlugins: mocks.listChannelPlugins,
|
||||
vi.mock("../channels/plugins/read-only.js", () => ({
|
||||
listReadOnlyChannelPluginsForConfig: mocks.listReadOnlyChannelPluginsForConfig,
|
||||
}));
|
||||
|
||||
vi.mock("../channels/plugins/status.js", () => ({
|
||||
buildChannelAccountSnapshot: mocks.buildChannelAccountSnapshot,
|
||||
}));
|
||||
|
||||
import { channelsListCommand } from "./channels/list.js";
|
||||
|
||||
describe("channels list auth profiles", () => {
|
||||
beforeEach(() => {
|
||||
mocks.readConfigFileSnapshot.mockReset();
|
||||
mocks.resolveCommandConfigWithSecrets.mockClear();
|
||||
mocks.loadAuthProfileStoreWithoutExternalProfiles.mockReset();
|
||||
mocks.listReadOnlyChannelPluginsForConfig.mockReset();
|
||||
mocks.listReadOnlyChannelPluginsForConfig.mockReturnValue([]);
|
||||
mocks.buildChannelAccountSnapshot.mockReset();
|
||||
});
|
||||
|
||||
it("includes local auth profiles in JSON output without loading external profiles", async () => {
|
||||
const runtime = createTestRuntime();
|
||||
mocks.readConfigFileSnapshot.mockResolvedValue({
|
||||
@@ -73,4 +87,92 @@ describe("channels list auth profiles", () => {
|
||||
expect(ids).toContain("anthropic:default");
|
||||
expect(ids).toContain("openai-codex:default");
|
||||
});
|
||||
|
||||
it("includes configured chat channel accounts in JSON output", async () => {
|
||||
const runtime = createTestRuntime();
|
||||
mocks.listReadOnlyChannelPluginsForConfig.mockReturnValue([
|
||||
{
|
||||
id: "telegram",
|
||||
meta: { id: "telegram", label: "Telegram" },
|
||||
config: {
|
||||
listAccountIds: () => ["alerts", "default"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
mocks.readConfigFileSnapshot.mockResolvedValue({
|
||||
...baseConfigSnapshot,
|
||||
config: {
|
||||
channels: {
|
||||
telegram: {
|
||||
accounts: {
|
||||
default: { botToken: "123:abc" },
|
||||
alerts: { botToken: "456:def" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
mocks.loadAuthProfileStoreWithoutExternalProfiles.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {},
|
||||
});
|
||||
|
||||
await channelsListCommand({ json: true, usage: false }, runtime);
|
||||
|
||||
expect(mocks.listReadOnlyChannelPluginsForConfig).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
expect.objectContaining({ includeSetupRuntimeFallback: true }),
|
||||
);
|
||||
const payload = JSON.parse(runtime.log.mock.calls[0]?.[0] as string) as {
|
||||
chat?: Record<string, string[]>;
|
||||
};
|
||||
expect(payload.chat?.telegram).toEqual(["alerts", "default"]);
|
||||
});
|
||||
|
||||
it("prints configured chat channel accounts before auth providers", async () => {
|
||||
const runtime = createTestRuntime();
|
||||
mocks.listReadOnlyChannelPluginsForConfig.mockReturnValue([
|
||||
{
|
||||
id: "telegram",
|
||||
meta: { id: "telegram", label: "Telegram" },
|
||||
config: {
|
||||
listAccountIds: () => ["default"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
mocks.buildChannelAccountSnapshot.mockResolvedValue({
|
||||
accountId: "default",
|
||||
configured: true,
|
||||
tokenSource: "config",
|
||||
enabled: true,
|
||||
});
|
||||
mocks.readConfigFileSnapshot.mockResolvedValue({
|
||||
...baseConfigSnapshot,
|
||||
config: {
|
||||
channels: {
|
||||
telegram: {
|
||||
accounts: {
|
||||
default: { botToken: "123:abc" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
mocks.loadAuthProfileStoreWithoutExternalProfiles.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {},
|
||||
});
|
||||
|
||||
await channelsListCommand({ usage: false }, runtime);
|
||||
|
||||
expect(mocks.listReadOnlyChannelPluginsForConfig).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
expect.objectContaining({ includeSetupRuntimeFallback: true }),
|
||||
);
|
||||
const output = runtime.log.mock.calls[0]?.[0] as string;
|
||||
expect(output).toContain("Chat channels:");
|
||||
expect(output).toContain("Telegram default:");
|
||||
expect(output).toContain("configured");
|
||||
expect(output.indexOf("Telegram default:")).toBeLessThan(output.indexOf("Auth providers"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ export async function channelsListCommand(
|
||||
const includeUsage = opts.usage !== false;
|
||||
|
||||
const plugins = listReadOnlyChannelPluginsForConfig(cfg, {
|
||||
includeSetupRuntimeFallback: false,
|
||||
includeSetupRuntimeFallback: true,
|
||||
});
|
||||
|
||||
const authStore = loadAuthProfileStoreWithoutExternalProfiles();
|
||||
|
||||
Reference in New Issue
Block a user