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..192b71b7b83 100644 --- a/src/plugin-sdk/command-auth.ts +++ b/src/plugin-sdk/command-auth.ts @@ -77,10 +77,7 @@ export { listSkillCommandsForWorkspace, resolveSkillCommandInvocation, } from "../auto-reply/skill-commands.js"; -export { - getPluginCommandSpecs, - listProviderPluginCommandSpecs, -} from "../plugins/command-registration.js"; +export { getPluginCommandSpecs, listProviderPluginCommandSpecs } from "../plugins/command-specs.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..2fbdd039523 100644 --- a/src/plugins/command-registration.ts +++ b/src/plugins/command-registration.ts @@ -6,9 +6,7 @@ import { import { clearPluginCommands, clearPluginCommandsForPlugin, - getPluginCommandSpecs, isPluginCommandRegistryLocked, - listProviderPluginCommandSpecs, pluginCommands, type RegisteredPluginCommand, } from "./command-registry-state.js"; @@ -197,10 +195,5 @@ export function registerPluginCommand( return { ok: true }; } -export { - clearPluginCommands, - clearPluginCommandsForPlugin, - getPluginCommandSpecs, - listProviderPluginCommandSpecs, -}; +export { clearPluginCommands, clearPluginCommandsForPlugin }; export type { RegisteredPluginCommand }; diff --git a/src/plugins/command-registry-state.ts b/src/plugins/command-registry-state.ts index 5cf85408b5b..316574528db 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.js"; import { resolveGlobalSingleton } from "../shared/global-singleton.js"; import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; import type { OpenClawPluginCommandDefinition } from "./types.js"; @@ -66,50 +64,3 @@ export function restorePluginCommands(commands: readonly RegisteredPluginCommand pluginCommands.set(`/${name}`, command); } } - -function resolvePluginNativeName( - command: OpenClawPluginCommandDefinition, - provider?: string, -): string { - const providerName = normalizeOptionalLowercaseString(provider); - const providerOverride = providerName ? command.nativeNames?.[providerName] : undefined; - if (typeof providerOverride === "string" && providerOverride.trim()) { - return providerOverride.trim(); - } - const defaultOverride = command.nativeNames?.default; - if (typeof defaultOverride === "string" && defaultOverride.trim()) { - return defaultOverride.trim(); - } - 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; - description: string; - acceptsArgs: boolean; -}> { - return Array.from(pluginCommands.values()).map((cmd) => ({ - name: resolvePluginNativeName(cmd, provider), - description: cmd.description, - acceptsArgs: cmd.acceptsArgs ?? false, - })); -} diff --git a/src/plugins/command-specs.ts b/src/plugins/command-specs.ts new file mode 100644 index 00000000000..4ff77ea8870 --- /dev/null +++ b/src/plugins/command-specs.ts @@ -0,0 +1,52 @@ +import { getLoadedChannelPlugin } from "../channels/plugins/index.js"; +import { resolveReadOnlyChannelCommandDefaults } from "../channels/plugins/read-only.js"; +import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; +import { pluginCommands } from "./command-registry-state.js"; +import type { OpenClawPluginCommandDefinition } from "./types.js"; + +function resolvePluginNativeName( + command: OpenClawPluginCommandDefinition, + provider?: string, +): string { + const providerName = normalizeOptionalLowercaseString(provider); + const providerOverride = providerName ? command.nativeNames?.[providerName] : undefined; + if (typeof providerOverride === "string" && providerOverride.trim()) { + return providerOverride.trim(); + } + const defaultOverride = command.nativeNames?.default; + if (typeof defaultOverride === "string" && defaultOverride.trim()) { + return defaultOverride.trim(); + } + 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; + description: string; + acceptsArgs: boolean; +}> { + return Array.from(pluginCommands.values()).map((cmd) => ({ + name: resolvePluginNativeName(cmd, provider), + description: cmd.description, + acceptsArgs: cmd.acceptsArgs ?? false, + })); +} diff --git a/src/plugins/commands.ts b/src/plugins/commands.ts index 225e1fb5ab0..635e67d2397 100644 --- a/src/plugins/commands.ts +++ b/src/plugins/commands.ts @@ -12,9 +12,7 @@ import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { clearPluginCommands, clearPluginCommandsForPlugin, - getPluginCommandSpecs, listPluginInvocationKeys, - listProviderPluginCommandSpecs, registerPluginCommand, validateCommandName, validatePluginCommandDefinition, @@ -24,6 +22,7 @@ import { setPluginCommandRegistryLocked, type RegisteredPluginCommand, } from "./command-registry-state.js"; +import { getPluginCommandSpecs, listProviderPluginCommandSpecs } 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,