From 2f5e5e9a71655dfcb7eae09773018f93f5141dd7 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 26 Apr 2026 09:15:43 +0100 Subject: [PATCH] fix: break plugin command spec import cycle --- src/agents/acp-spawn.test.ts | 3 ++- src/gateway/server-methods/commands.test.ts | 2 +- src/gateway/server-methods/commands.ts | 2 +- src/plugin-sdk/command-auth.ts | 6 ++---- src/plugins/command-registration.ts | 8 +------- src/plugins/command-registry-state.ts | 20 ------------------- src/plugins/command-specs.ts | 22 +++++++++++++++++++++ src/plugins/commands.ts | 2 +- src/plugins/loader.test.ts | 3 ++- 9 files changed, 32 insertions(+), 36 deletions(-) create mode 100644 src/plugins/command-specs.ts diff --git a/src/agents/acp-spawn.test.ts b/src/agents/acp-spawn.test.ts index 059dfd96afc..cbbc3dbb7b8 100644 --- a/src/agents/acp-spawn.test.ts +++ b/src/agents/acp-spawn.test.ts @@ -2454,7 +2454,8 @@ describe("spawnAcpDirect", () => { conversation: expect.objectContaining({ channel: "telegram", accountId: "default", - conversationId: "-1003342490704:topic:2", + conversationId: "2", + parentConversationId: "-1003342490704", }), }), ); diff --git a/src/gateway/server-methods/commands.test.ts b/src/gateway/server-methods/commands.test.ts index 3abe62bdf91..37c087e2a97 100644 --- a/src/gateway/server-methods/commands.test.ts +++ b/src/gateway/server-methods/commands.test.ts @@ -79,7 +79,7 @@ vi.mock("../../auto-reply/commands-registry.js", () => ({ vi.mock("../../auto-reply/skill-commands.js", () => ({ listSkillCommandsForAgents: vi.fn(() => mockSkillCommands), })); -vi.mock("../../plugins/command-registry-state.js", () => ({ +vi.mock("../../plugins/command-specs.js", () => ({ getPluginCommandSpecs: vi.fn((provider?: string) => { if (provider === "whatsapp") { return []; diff --git a/src/gateway/server-methods/commands.ts b/src/gateway/server-methods/commands.ts index 83ff99b53b3..946ce068b24 100644 --- a/src/gateway/server-methods/commands.ts +++ b/src/gateway/server-methods/commands.ts @@ -9,7 +9,7 @@ import { listSkillCommandsForAgents } from "../../auto-reply/skill-commands.js"; import { getChannelPlugin } from "../../channels/plugins/index.js"; import { loadConfig } from "../../config/config.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; -import { getPluginCommandSpecs } from "../../plugins/command-registry-state.js"; +import { getPluginCommandSpecs } from "../../plugins/command-specs.js"; import { listPluginCommands } from "../../plugins/commands.js"; import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js"; import type { CommandEntry, CommandsListResult } from "../protocol/index.js"; diff --git a/src/plugin-sdk/command-auth.ts b/src/plugin-sdk/command-auth.ts index 9f15d842a12..e3eaf56ff02 100644 --- a/src/plugin-sdk/command-auth.ts +++ b/src/plugin-sdk/command-auth.ts @@ -77,10 +77,8 @@ export { listSkillCommandsForWorkspace, resolveSkillCommandInvocation, } from "../auto-reply/skill-commands.js"; -export { - getPluginCommandSpecs, - listProviderPluginCommandSpecs, -} from "../plugins/command-registration.js"; +export { getPluginCommandSpecs } from "../plugins/command-specs.js"; +export { listProviderPluginCommandSpecs } from "../plugins/command-registry-state.js"; export type { SkillCommandSpec } from "../agents/skills.js"; export { buildModelsProviderData, diff --git a/src/plugins/command-registration.ts b/src/plugins/command-registration.ts index 7a6e3f63762..945a26f67a2 100644 --- a/src/plugins/command-registration.ts +++ b/src/plugins/command-registration.ts @@ -6,7 +6,6 @@ import { import { clearPluginCommands, clearPluginCommandsForPlugin, - getPluginCommandSpecs, isPluginCommandRegistryLocked, listProviderPluginCommandSpecs, pluginCommands, @@ -197,10 +196,5 @@ export function registerPluginCommand( return { ok: true }; } -export { - clearPluginCommands, - clearPluginCommandsForPlugin, - getPluginCommandSpecs, - listProviderPluginCommandSpecs, -}; +export { clearPluginCommands, clearPluginCommandsForPlugin, listProviderPluginCommandSpecs }; export type { RegisteredPluginCommand }; diff --git a/src/plugins/command-registry-state.ts b/src/plugins/command-registry-state.ts index 0a783974d27..fb1f8cc0db4 100644 --- a/src/plugins/command-registry-state.ts +++ b/src/plugins/command-registry-state.ts @@ -1,5 +1,3 @@ -import { getLoadedChannelPlugin } from "../channels/plugins/index.js"; -import { resolveReadOnlyChannelCommandDefaults } from "../channels/plugins/read-only-command-defaults.js"; import { resolveGlobalSingleton } from "../shared/global-singleton.js"; import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; import type { OpenClawPluginCommandDefinition } from "./types.js"; @@ -83,24 +81,6 @@ function resolvePluginNativeName( return command.name; } -export function getPluginCommandSpecs(provider?: string): Array<{ - name: string; - description: string; - acceptsArgs: boolean; -}> { - const providerName = normalizeOptionalLowercaseString(provider); - if ( - providerName && - ( - getLoadedChannelPlugin(providerName)?.commands ?? - resolveReadOnlyChannelCommandDefaults(providerName) - )?.nativeCommandsAutoEnabled !== true - ) { - return []; - } - return listProviderPluginCommandSpecs(provider); -} - /** Resolve plugin command specs for a provider's native naming surface without support gating. */ export function listProviderPluginCommandSpecs(provider?: string): Array<{ name: string; diff --git a/src/plugins/command-specs.ts b/src/plugins/command-specs.ts new file mode 100644 index 00000000000..3b8aaf25b8a --- /dev/null +++ b/src/plugins/command-specs.ts @@ -0,0 +1,22 @@ +import { getLoadedChannelPlugin } from "../channels/plugins/index.js"; +import { resolveReadOnlyChannelCommandDefaults } from "../channels/plugins/read-only.js"; +import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; +import { listProviderPluginCommandSpecs } from "./command-registry-state.js"; + +export function getPluginCommandSpecs(provider?: string): Array<{ + name: string; + description: string; + acceptsArgs: boolean; +}> { + const providerName = normalizeOptionalLowercaseString(provider); + if ( + providerName && + ( + getLoadedChannelPlugin(providerName)?.commands ?? + resolveReadOnlyChannelCommandDefaults(providerName) + )?.nativeCommandsAutoEnabled !== true + ) { + return []; + } + return listProviderPluginCommandSpecs(provider); +} diff --git a/src/plugins/commands.ts b/src/plugins/commands.ts index 225e1fb5ab0..8cfa4762e08 100644 --- a/src/plugins/commands.ts +++ b/src/plugins/commands.ts @@ -12,7 +12,6 @@ import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { clearPluginCommands, clearPluginCommandsForPlugin, - getPluginCommandSpecs, listPluginInvocationKeys, listProviderPluginCommandSpecs, registerPluginCommand, @@ -24,6 +23,7 @@ import { setPluginCommandRegistryLocked, type RegisteredPluginCommand, } from "./command-registry-state.js"; +import { getPluginCommandSpecs } from "./command-specs.js"; import { detachPluginConversationBinding, getCurrentPluginConversationBinding, diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index 17a1ebff203..71272bdebc0 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -22,7 +22,8 @@ import { type DetachedTaskLifecycleRuntime, } from "../tasks/detached-task-runtime-state.js"; import { withEnv } from "../test-utils/env.js"; -import { clearPluginCommands, getPluginCommandSpecs } from "./command-registry-state.js"; +import { clearPluginCommands } from "./command-registry-state.js"; +import { getPluginCommandSpecs } from "./command-specs.js"; import { getGlobalHookRunner, getGlobalPluginRegistry,