mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-07 11:52:54 +00:00
* fix: require admin authorization for phone control mutations * addressing codex review * addressing codex review * addressing ci * addressing ci * test: restore provider registry mock isolation * docs: add changelog entry for PR merge
126 lines
3.8 KiB
TypeScript
126 lines
3.8 KiB
TypeScript
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
|
|
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
|
import { normalizeAgentPromptSurfaceKind } from "./agent-prompt-surface-kind.js";
|
|
import type {
|
|
AgentPromptGuidance,
|
|
AgentPromptSurfaceKind,
|
|
OpenClawPluginCommandDefinition,
|
|
} from "./types.js";
|
|
|
|
export type RegisteredPluginCommand = OpenClawPluginCommandDefinition & {
|
|
pluginId: string;
|
|
pluginName?: string;
|
|
pluginRoot?: string;
|
|
trustedOwnerStatusExposure?: true;
|
|
};
|
|
|
|
type PluginCommandState = {
|
|
pluginCommands: Map<string, RegisteredPluginCommand>;
|
|
registryLocked: boolean;
|
|
};
|
|
|
|
const PLUGIN_COMMAND_STATE_KEY = Symbol.for("openclaw.pluginCommandsState");
|
|
|
|
const getState = () =>
|
|
resolveGlobalSingleton<PluginCommandState>(PLUGIN_COMMAND_STATE_KEY, () => ({
|
|
pluginCommands: new Map<string, RegisteredPluginCommand>(),
|
|
registryLocked: false,
|
|
}));
|
|
|
|
const getPluginCommandMap = () => getState().pluginCommands;
|
|
|
|
export const pluginCommands = new Proxy(new Map<string, RegisteredPluginCommand>(), {
|
|
get(_target, property) {
|
|
const value = Reflect.get(getPluginCommandMap(), property, getPluginCommandMap());
|
|
return typeof value === "function" ? value.bind(getPluginCommandMap()) : value;
|
|
},
|
|
});
|
|
|
|
export function isPluginCommandRegistryLocked(): boolean {
|
|
return getState().registryLocked;
|
|
}
|
|
|
|
export function setPluginCommandRegistryLocked(locked: boolean): void {
|
|
getState().registryLocked = locked;
|
|
}
|
|
|
|
export function clearPluginCommands(): void {
|
|
pluginCommands.clear();
|
|
}
|
|
|
|
export function clearPluginCommandsForPlugin(pluginId: string): void {
|
|
for (const [key, cmd] of pluginCommands.entries()) {
|
|
if (cmd.pluginId === pluginId) {
|
|
pluginCommands.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function isTrustedReservedCommandOwner(command: RegisteredPluginCommand): boolean {
|
|
return command.ownership === "reserved";
|
|
}
|
|
|
|
export function canExposeSenderIsOwner(command: RegisteredPluginCommand): boolean {
|
|
return (
|
|
(Array.isArray(command.requiredScopes) && command.requiredScopes.length > 0) ||
|
|
command.trustedOwnerStatusExposure === true
|
|
);
|
|
}
|
|
|
|
export function listRegisteredPluginCommands(): RegisteredPluginCommand[] {
|
|
return Array.from(pluginCommands.values());
|
|
}
|
|
|
|
export function listRegisteredPluginAgentPromptGuidance(params?: {
|
|
surface?: AgentPromptSurfaceKind;
|
|
includeLegacyGlobalGuidance?: boolean;
|
|
}): string[] {
|
|
const lines: string[] = [];
|
|
const seen = new Set<string>();
|
|
for (const command of pluginCommands.values()) {
|
|
for (const entry of command.agentPromptGuidance ?? []) {
|
|
const trimmed = resolveAgentPromptGuidanceTextForSurface(entry, {
|
|
surface: params?.surface ? normalizeAgentPromptSurfaceKind(params.surface) : undefined,
|
|
includeLegacyGlobalGuidance: params?.includeLegacyGlobalGuidance ?? true,
|
|
});
|
|
if (!trimmed || seen.has(trimmed)) {
|
|
continue;
|
|
}
|
|
seen.add(trimmed);
|
|
lines.push(trimmed);
|
|
}
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
function resolveAgentPromptGuidanceTextForSurface(
|
|
entry: AgentPromptGuidance,
|
|
params: {
|
|
surface?: AgentPromptSurfaceKind;
|
|
includeLegacyGlobalGuidance: boolean;
|
|
},
|
|
): string | undefined {
|
|
if (typeof entry === "string") {
|
|
return params.includeLegacyGlobalGuidance ? entry.trim() : undefined;
|
|
}
|
|
const text = entry.text.trim();
|
|
if (!params.surface) {
|
|
return text;
|
|
}
|
|
if (!entry.surfaces || entry.surfaces.length === 0) {
|
|
return params.includeLegacyGlobalGuidance ? text : undefined;
|
|
}
|
|
return entry.surfaces.includes(params.surface) ? text : undefined;
|
|
}
|
|
|
|
export function restorePluginCommands(commands: readonly RegisteredPluginCommand[]): void {
|
|
pluginCommands.clear();
|
|
for (const command of commands) {
|
|
const name = normalizeOptionalLowercaseString(command.name);
|
|
if (!name) {
|
|
continue;
|
|
}
|
|
pluginCommands.set(`/${name}`, command);
|
|
}
|
|
}
|