mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-17 16:12:21 -06:00
d6f70a96cb
* fix(plugins): preserve selected command identity * test(telegram): use scoped command registries * test(telegram): isolate command runtime fixtures * test(telegram): warm native command runtime * refactor(plugins): keep command metadata private * fix(plugins): accept synchronous command handlers * fix(plugins): scope command drain bypass to live execution * test(telegram): use scoped command registry fixtures * test(telegram): isolate native menu runtime fixtures * test(telegram): isolate login session store * test(telegram): surface login flow failures * test(telegram): preload native login module * test(telegram): scope native command registries * fix(plugins): complete command dispatch contracts * fix(plugins): break command dispatch import cycles * fix(plugins): stabilize command dispatch contracts * fix(channels): keep plugin dispatch options internal * fix(plugins): keep command dispatch carrier opaque * test(channels): align delivery adapter fixtures * test(delivery): align custody ownership coverage * test(delivery): align latest queue reconciliation * test(channels): drop obsolete delivery wrappers * fix(plugins): rebind channel reload starts * fix(plugins): scope command catalog reloads * fix(ci): align current runtime contracts * chore(plugin-sdk): refresh API baseline
149 lines
4.7 KiB
TypeScript
149 lines
4.7 KiB
TypeScript
// Normalizes plugin command specs for CLI and slash command surfaces.
|
|
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
|
|
import { getLoadedChannelPlugin } from "../channels/plugins/index.js";
|
|
import { resolveReadOnlyChannelCommandDefaults } from "../channels/plugins/read-only-command-defaults.js";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import { pluginCommands } from "./command-registry-state.js";
|
|
import {
|
|
pluginCommandSupportsChannel,
|
|
projectPluginCommandNativeMetadata,
|
|
} from "./plugin-command-metadata.js";
|
|
import type { PluginCommandRegistration } from "./registry-types.js";
|
|
import type { OpenClawPluginCommandDefinition } from "./types.js";
|
|
|
|
type PluginCommandSpecOptions = {
|
|
env?: NodeJS.ProcessEnv;
|
|
stateDir?: string;
|
|
workspaceDir?: string;
|
|
config?: OpenClawConfig;
|
|
};
|
|
|
|
type PluginCommandEntrySpec = {
|
|
name: string;
|
|
description: string;
|
|
acceptsArgs: boolean;
|
|
nativeName?: string;
|
|
};
|
|
|
|
function resolvePluginTextName(command: OpenClawPluginCommandDefinition): string {
|
|
const name = command.name.trim();
|
|
return name || command.name;
|
|
}
|
|
|
|
function pluginNativeCommandsEnabled(
|
|
providerName: string | undefined,
|
|
options: PluginCommandSpecOptions,
|
|
): boolean {
|
|
if (!providerName) {
|
|
return true;
|
|
}
|
|
const commandDefaults = options.config
|
|
? resolveReadOnlyChannelCommandDefaults(providerName, {
|
|
...options,
|
|
config: options.config,
|
|
})
|
|
: undefined;
|
|
return (
|
|
(getLoadedChannelPlugin(providerName)?.commands ?? commandDefaults)
|
|
?.nativeCommandsAutoEnabled === true
|
|
);
|
|
}
|
|
|
|
export function getPluginCommandSpecs(
|
|
provider?: string,
|
|
options: PluginCommandSpecOptions = {},
|
|
): Array<{
|
|
name: string;
|
|
description: string;
|
|
descriptionLocalizations?: Record<string, string>;
|
|
acceptsArgs: boolean;
|
|
}> {
|
|
const providerName = normalizeOptionalLowercaseString(provider);
|
|
if (!pluginNativeCommandsEnabled(providerName, options)) {
|
|
return [];
|
|
}
|
|
return listProviderPluginCommandSpecs(providerName);
|
|
}
|
|
|
|
export function getPluginCommandEntrySpecs(
|
|
provider?: string,
|
|
options: PluginCommandSpecOptions = {},
|
|
): PluginCommandEntrySpec[] {
|
|
const providerName = normalizeOptionalLowercaseString(provider);
|
|
const nativeCommandsEnabled = pluginNativeCommandsEnabled(providerName, options);
|
|
return Array.from(pluginCommands.values())
|
|
.map((cmd) => serializePluginCommandEntrySpec(cmd, providerName, nativeCommandsEnabled))
|
|
.filter((spec): spec is PluginCommandEntrySpec => spec !== null);
|
|
}
|
|
|
|
export function getPluginCommandEntrySpecsFromRegistrations(
|
|
commands: readonly PluginCommandRegistration[],
|
|
provider?: string,
|
|
options: PluginCommandSpecOptions = {},
|
|
): PluginCommandEntrySpec[] {
|
|
const providerName = normalizeOptionalLowercaseString(provider);
|
|
const nativeCommandsEnabled = pluginNativeCommandsEnabled(providerName, options);
|
|
return commands
|
|
.map((entry) =>
|
|
serializePluginCommandEntrySpec(entry.command, providerName, nativeCommandsEnabled),
|
|
)
|
|
.filter((spec): spec is PluginCommandEntrySpec => spec !== null);
|
|
}
|
|
|
|
/** Resolve plugin command specs for a provider's native naming surface without support gating. */
|
|
export function listProviderPluginCommandSpecs(provider?: string): Array<{
|
|
name: string;
|
|
description: string;
|
|
descriptionLocalizations?: Record<string, string>;
|
|
acceptsArgs: boolean;
|
|
}> {
|
|
return Array.from(pluginCommands.values())
|
|
.filter((cmd) => pluginCommandSupportsChannel(cmd, provider))
|
|
.map((cmd) => serializePluginCommandSpec(cmd, provider));
|
|
}
|
|
|
|
function serializePluginCommandSpec(
|
|
cmd: OpenClawPluginCommandDefinition,
|
|
provider?: string,
|
|
): {
|
|
name: string;
|
|
description: string;
|
|
descriptionLocalizations?: Record<string, string>;
|
|
acceptsArgs: boolean;
|
|
} {
|
|
const metadata = projectPluginCommandNativeMetadata(cmd, provider);
|
|
const spec: {
|
|
name: string;
|
|
description: string;
|
|
descriptionLocalizations?: Record<string, string>;
|
|
acceptsArgs: boolean;
|
|
} = {
|
|
name: metadata.name,
|
|
description: metadata.description,
|
|
acceptsArgs: metadata.acceptsArgs,
|
|
};
|
|
if (metadata.descriptionLocalizations) {
|
|
spec.descriptionLocalizations = { ...metadata.descriptionLocalizations };
|
|
}
|
|
return spec;
|
|
}
|
|
|
|
function serializePluginCommandEntrySpec(
|
|
cmd: OpenClawPluginCommandDefinition,
|
|
provider: string | undefined,
|
|
nativeCommandsEnabled: boolean,
|
|
): PluginCommandEntrySpec | null {
|
|
if (!pluginCommandSupportsChannel(cmd, provider)) {
|
|
return null;
|
|
}
|
|
const nativeName = nativeCommandsEnabled
|
|
? projectPluginCommandNativeMetadata(cmd, provider).name
|
|
: undefined;
|
|
return {
|
|
name: resolvePluginTextName(cmd),
|
|
description: cmd.description.trim(),
|
|
acceptsArgs: cmd.acceptsArgs ?? false,
|
|
...(nativeName ? { nativeName } : {}),
|
|
};
|
|
}
|