Files
openclaw/src/plugins/command-registry-state.ts
Pavan Kumar Gondhi 91a4635bdc Tighten phone-control mutation authorization [AI] (#87150)
* 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
2026-05-28 16:00:01 +05:30

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);
}
}