mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-20 17:41:33 -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
76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import {
|
|
normalizeLowercaseStringOrEmpty,
|
|
normalizeOptionalLowercaseString,
|
|
} from "@openclaw/normalization-core/string-coerce";
|
|
import type { RegisteredPluginCommand } from "./command-registry-state.js";
|
|
import { pluginCommandSupportsChannel } from "./plugin-command-metadata.js";
|
|
|
|
type PluginCommandAliasScope = { kind: "all" } | { kind: "provider"; provider: string };
|
|
|
|
function listInvocationKeys(
|
|
command: RegisteredPluginCommand,
|
|
aliasScope: PluginCommandAliasScope,
|
|
): string[] {
|
|
const keys = new Set<string>();
|
|
const add = (value: string | undefined) => {
|
|
const normalized = normalizeOptionalLowercaseString(value);
|
|
if (normalized) {
|
|
keys.add(`/${normalized}`);
|
|
}
|
|
};
|
|
add(command.name);
|
|
if (aliasScope.kind === "all") {
|
|
for (const alias of Object.values(command.nativeNames ?? {})) {
|
|
if (typeof alias === "string") {
|
|
add(alias);
|
|
}
|
|
}
|
|
return [...keys];
|
|
}
|
|
const provider = normalizeOptionalLowercaseString(aliasScope.provider);
|
|
const providerAlias = provider ? command.nativeNames?.[provider] : undefined;
|
|
add(typeof providerAlias === "string" ? providerAlias : command.nativeNames?.default);
|
|
return [...keys];
|
|
}
|
|
|
|
export function matchRegisteredPluginCommand(params: {
|
|
commands: readonly RegisteredPluginCommand[];
|
|
commandBody: string;
|
|
channel?: string;
|
|
aliasScope: PluginCommandAliasScope;
|
|
}): { command: RegisteredPluginCommand; args?: string } | null {
|
|
const trimmed = params.commandBody.trim();
|
|
if (!trimmed.startsWith("/")) {
|
|
return null;
|
|
}
|
|
const commandMatch = trimmed.match(/^\/\s*([^\s]+)(?:\s+([\s\S]*))?$/);
|
|
if (!commandMatch) {
|
|
return null;
|
|
}
|
|
const key = normalizeLowercaseStringOrEmpty(`/${commandMatch[1]}`);
|
|
const alternateKeys = [key];
|
|
if (key.includes("_")) {
|
|
alternateKeys.push(key.replace(/_/g, "-"));
|
|
}
|
|
if (key.includes("-")) {
|
|
alternateKeys.push(key.replace(/-/g, "_"));
|
|
}
|
|
const command = alternateKeys
|
|
.map((candidateKey) =>
|
|
params.commands.find(
|
|
(candidate) =>
|
|
pluginCommandSupportsChannel(candidate, params.channel) &&
|
|
listInvocationKeys(candidate, params.aliasScope).includes(candidateKey),
|
|
),
|
|
)
|
|
.find((candidate): candidate is RegisteredPluginCommand => candidate !== undefined);
|
|
if (!command) {
|
|
return null;
|
|
}
|
|
const args = commandMatch[2]?.trim();
|
|
if (args && !command.acceptsArgs) {
|
|
return null;
|
|
}
|
|
return { command, args: args || undefined };
|
|
}
|