@gguf/claw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,037 lines (1,033 loc) • 30.7 kB
JavaScript
import { t as __exportAll } from "./rolldown-runtime-Cbj13DAv.js";
import { S as escapeRegExp, c as getActivePluginRegistry } from "./registry-C8pj8ctW.js";
import { G as DEFAULT_PROVIDER, W as DEFAULT_MODEL, u as resolveConfiguredModelRef } from "./model-selection-DQIwoYb8.js";
import { f as listChannelDocks, r as listThinkingLevels } from "./thinking-DFXDZFyu.js";
//#region src/auto-reply/commands-args.ts
function normalizeArgValue(value) {
if (value == null) return;
let text;
if (typeof value === "string") text = value.trim();
else if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") text = String(value).trim();
else if (typeof value === "symbol") text = value.toString().trim();
else if (typeof value === "function") text = value.toString().trim();
else text = JSON.stringify(value);
return text ? text : void 0;
}
function formatActionArgs(values, params) {
const action = normalizeArgValue(values.action)?.toLowerCase();
const path = normalizeArgValue(values.path);
const value = normalizeArgValue(values.value);
if (!action) return;
const knownAction = params.formatKnownAction(action, path);
if (knownAction) return knownAction;
return formatSetUnsetArgAction(action, {
path,
value
});
}
const formatConfigArgs = (values) => formatActionArgs(values, { formatKnownAction: (action, path) => {
if (action === "show" || action === "get") return path ? `${action} ${path}` : action;
} });
const formatDebugArgs = (values) => formatActionArgs(values, { formatKnownAction: (action) => {
if (action === "show" || action === "reset") return action;
} });
function formatSetUnsetArgAction(action, params) {
if (action === "unset") return params.path ? `${action} ${params.path}` : action;
if (action === "set") {
if (!params.path) return action;
if (!params.value) return `${action} ${params.path}`;
return `${action} ${params.path}=${params.value}`;
}
return action;
}
const formatQueueArgs = (values) => {
const mode = normalizeArgValue(values.mode);
const debounce = normalizeArgValue(values.debounce);
const cap = normalizeArgValue(values.cap);
const drop = normalizeArgValue(values.drop);
const parts = [];
if (mode) parts.push(mode);
if (debounce) parts.push(`debounce:${debounce}`);
if (cap) parts.push(`cap:${cap}`);
if (drop) parts.push(`drop:${drop}`);
return parts.length > 0 ? parts.join(" ") : void 0;
};
const formatExecArgs = (values) => {
const host = normalizeArgValue(values.host);
const security = normalizeArgValue(values.security);
const ask = normalizeArgValue(values.ask);
const node = normalizeArgValue(values.node);
const parts = [];
if (host) parts.push(`host=${host}`);
if (security) parts.push(`security=${security}`);
if (ask) parts.push(`ask=${ask}`);
if (node) parts.push(`node=${node}`);
return parts.length > 0 ? parts.join(" ") : void 0;
};
const COMMAND_ARG_FORMATTERS = {
config: formatConfigArgs,
debug: formatDebugArgs,
queue: formatQueueArgs,
exec: formatExecArgs
};
//#endregion
//#region src/auto-reply/commands-registry.data.ts
function defineChatCommand(command) {
const aliases = (command.textAliases ?? (command.textAlias ? [command.textAlias] : [])).map((alias) => alias.trim()).filter(Boolean);
const scope = command.scope ?? (command.nativeName ? aliases.length ? "both" : "native" : "text");
const acceptsArgs = command.acceptsArgs ?? Boolean(command.args?.length);
const argsParsing = command.argsParsing ?? (command.args?.length ? "positional" : "none");
return {
key: command.key,
nativeName: command.nativeName,
description: command.description,
acceptsArgs,
args: command.args,
argsParsing,
formatArgs: command.formatArgs,
argsMenu: command.argsMenu,
textAliases: aliases,
scope,
category: command.category
};
}
function defineDockCommand(dock) {
return defineChatCommand({
key: `dock:${dock.id}`,
nativeName: `dock_${dock.id}`,
description: `Switch to ${dock.id} for replies.`,
textAliases: [`/dock-${dock.id}`, `/dock_${dock.id}`],
category: "docks"
});
}
function registerAlias(commands, key, ...aliases) {
const command = commands.find((entry) => entry.key === key);
if (!command) throw new Error(`registerAlias: unknown command key: ${key}`);
const existing = new Set(command.textAliases.map((alias) => alias.trim().toLowerCase()));
for (const alias of aliases) {
const trimmed = alias.trim();
if (!trimmed) continue;
const lowered = trimmed.toLowerCase();
if (existing.has(lowered)) continue;
existing.add(lowered);
command.textAliases.push(trimmed);
}
}
function assertCommandRegistry(commands) {
const keys = /* @__PURE__ */ new Set();
const nativeNames = /* @__PURE__ */ new Set();
const textAliases = /* @__PURE__ */ new Set();
for (const command of commands) {
if (keys.has(command.key)) throw new Error(`Duplicate command key: ${command.key}`);
keys.add(command.key);
const nativeName = command.nativeName?.trim();
if (command.scope === "text") {
if (nativeName) throw new Error(`Text-only command has native name: ${command.key}`);
if (command.textAliases.length === 0) throw new Error(`Text-only command missing text alias: ${command.key}`);
} else if (!nativeName) throw new Error(`Native command missing native name: ${command.key}`);
else {
const nativeKey = nativeName.toLowerCase();
if (nativeNames.has(nativeKey)) throw new Error(`Duplicate native command: ${nativeName}`);
nativeNames.add(nativeKey);
}
if (command.scope === "native" && command.textAliases.length > 0) throw new Error(`Native-only command has text aliases: ${command.key}`);
for (const alias of command.textAliases) {
if (!alias.startsWith("/")) throw new Error(`Command alias missing leading '/': ${alias}`);
const aliasKey = alias.toLowerCase();
if (textAliases.has(aliasKey)) throw new Error(`Duplicate command alias: ${alias}`);
textAliases.add(aliasKey);
}
}
}
let cachedCommands = null;
let cachedRegistry = null;
let cachedNativeCommandSurfaces = null;
let cachedNativeRegistry = null;
function buildChatCommands() {
const commands = [
defineChatCommand({
key: "help",
nativeName: "help",
description: "Show available commands.",
textAlias: "/help",
category: "status"
}),
defineChatCommand({
key: "commands",
nativeName: "commands",
description: "List all slash commands.",
textAlias: "/commands",
category: "status"
}),
defineChatCommand({
key: "skill",
nativeName: "skill",
description: "Run a skill by name.",
textAlias: "/skill",
category: "tools",
args: [{
name: "name",
description: "Skill name",
type: "string",
required: true
}, {
name: "input",
description: "Skill input",
type: "string",
captureRemaining: true
}]
}),
defineChatCommand({
key: "status",
nativeName: "status",
description: "Show current status.",
textAlias: "/status",
category: "status"
}),
defineChatCommand({
key: "allowlist",
description: "List/add/remove allowlist entries.",
textAlias: "/allowlist",
acceptsArgs: true,
scope: "text",
category: "management"
}),
defineChatCommand({
key: "approve",
nativeName: "approve",
description: "Approve or deny exec requests.",
textAlias: "/approve",
acceptsArgs: true,
category: "management"
}),
defineChatCommand({
key: "context",
nativeName: "context",
description: "Explain how context is built and used.",
textAlias: "/context",
acceptsArgs: true,
category: "status"
}),
defineChatCommand({
key: "export-session",
nativeName: "export-session",
description: "Export current session to HTML file with full system prompt.",
textAliases: ["/export-session", "/export"],
acceptsArgs: true,
category: "status",
args: [{
name: "path",
description: "Output path (default: workspace)",
type: "string",
required: false
}]
}),
defineChatCommand({
key: "tts",
nativeName: "tts",
description: "Control text-to-speech (TTS).",
textAlias: "/tts",
category: "media",
args: [{
name: "action",
description: "TTS action",
type: "string",
choices: [
{
value: "on",
label: "On"
},
{
value: "off",
label: "Off"
},
{
value: "status",
label: "Status"
},
{
value: "provider",
label: "Provider"
},
{
value: "limit",
label: "Limit"
},
{
value: "summary",
label: "Summary"
},
{
value: "audio",
label: "Audio"
},
{
value: "help",
label: "Help"
}
]
}, {
name: "value",
description: "Provider, limit, or text",
type: "string",
captureRemaining: true
}],
argsMenu: {
arg: "action",
title: "TTS Actions:\n• On – Enable TTS for responses\n• Off – Disable TTS\n• Status – Show current settings\n• Provider – Set voice provider (edge, elevenlabs, openai)\n• Limit – Set max characters for TTS\n• Summary – Toggle AI summary for long texts\n• Audio – Generate TTS from custom text\n• Help – Show usage guide"
}
}),
defineChatCommand({
key: "whoami",
nativeName: "whoami",
description: "Show your sender id.",
textAlias: "/whoami",
category: "status"
}),
defineChatCommand({
key: "subagents",
nativeName: "subagents",
description: "List, kill, log, spawn, or steer subagent runs for this session.",
textAlias: "/subagents",
category: "management",
args: [
{
name: "action",
description: "list | kill | log | info | send | steer | spawn",
type: "string",
choices: [
"list",
"kill",
"log",
"info",
"send",
"steer",
"spawn"
]
},
{
name: "target",
description: "Run id, index, or session key",
type: "string"
},
{
name: "value",
description: "Additional input (limit/message)",
type: "string",
captureRemaining: true
}
],
argsMenu: "auto"
}),
defineChatCommand({
key: "kill",
nativeName: "kill",
description: "Kill a running subagent (or all).",
textAlias: "/kill",
category: "management",
args: [{
name: "target",
description: "Label, run id, index, or all",
type: "string"
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "steer",
nativeName: "steer",
description: "Send guidance to a running subagent.",
textAlias: "/steer",
category: "management",
args: [{
name: "target",
description: "Label, run id, or index",
type: "string"
}, {
name: "message",
description: "Steering message",
type: "string",
captureRemaining: true
}]
}),
defineChatCommand({
key: "config",
nativeName: "config",
description: "Show or set config values.",
textAlias: "/config",
category: "management",
args: [
{
name: "action",
description: "show | get | set | unset",
type: "string",
choices: [
"show",
"get",
"set",
"unset"
]
},
{
name: "path",
description: "Config path",
type: "string"
},
{
name: "value",
description: "Value for set",
type: "string",
captureRemaining: true
}
],
argsParsing: "none",
formatArgs: COMMAND_ARG_FORMATTERS.config
}),
defineChatCommand({
key: "debug",
nativeName: "debug",
description: "Set runtime debug overrides.",
textAlias: "/debug",
category: "management",
args: [
{
name: "action",
description: "show | reset | set | unset",
type: "string",
choices: [
"show",
"reset",
"set",
"unset"
]
},
{
name: "path",
description: "Debug path",
type: "string"
},
{
name: "value",
description: "Value for set",
type: "string",
captureRemaining: true
}
],
argsParsing: "none",
formatArgs: COMMAND_ARG_FORMATTERS.debug
}),
defineChatCommand({
key: "usage",
nativeName: "usage",
description: "Usage footer or cost summary.",
textAlias: "/usage",
category: "options",
args: [{
name: "mode",
description: "off, tokens, full, or cost",
type: "string",
choices: [
"off",
"tokens",
"full",
"cost"
]
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "stop",
nativeName: "stop",
description: "Stop the current run.",
textAlias: "/stop",
category: "session"
}),
defineChatCommand({
key: "restart",
nativeName: "restart",
description: "Restart OpenClaw.",
textAlias: "/restart",
category: "tools"
}),
defineChatCommand({
key: "activation",
nativeName: "activation",
description: "Set group activation mode.",
textAlias: "/activation",
category: "management",
args: [{
name: "mode",
description: "mention or always",
type: "string",
choices: ["mention", "always"]
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "send",
nativeName: "send",
description: "Set send policy.",
textAlias: "/send",
category: "management",
args: [{
name: "mode",
description: "on, off, or inherit",
type: "string",
choices: [
"on",
"off",
"inherit"
]
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "reset",
nativeName: "reset",
description: "Reset the current session.",
textAlias: "/reset",
acceptsArgs: true,
category: "session"
}),
defineChatCommand({
key: "new",
nativeName: "new",
description: "Start a new session.",
textAlias: "/new",
acceptsArgs: true,
category: "session"
}),
defineChatCommand({
key: "compact",
nativeName: "compact",
description: "Compact the session context.",
textAlias: "/compact",
category: "session",
args: [{
name: "instructions",
description: "Extra compaction instructions",
type: "string",
captureRemaining: true
}]
}),
defineChatCommand({
key: "think",
nativeName: "think",
description: "Set thinking level.",
textAlias: "/think",
category: "options",
args: [{
name: "level",
description: "off, minimal, low, medium, high, xhigh",
type: "string",
choices: ({ provider, model }) => listThinkingLevels(provider, model)
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "verbose",
nativeName: "verbose",
description: "Toggle verbose mode.",
textAlias: "/verbose",
category: "options",
args: [{
name: "mode",
description: "on or off",
type: "string",
choices: ["on", "off"]
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "reasoning",
nativeName: "reasoning",
description: "Toggle reasoning visibility.",
textAlias: "/reasoning",
category: "options",
args: [{
name: "mode",
description: "on, off, or stream",
type: "string",
choices: [
"on",
"off",
"stream"
]
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "elevated",
nativeName: "elevated",
description: "Toggle elevated mode.",
textAlias: "/elevated",
category: "options",
args: [{
name: "mode",
description: "on, off, ask, or full",
type: "string",
choices: [
"on",
"off",
"ask",
"full"
]
}],
argsMenu: "auto"
}),
defineChatCommand({
key: "exec",
nativeName: "exec",
description: "Set exec defaults for this session.",
textAlias: "/exec",
category: "options",
args: [
{
name: "host",
description: "sandbox, gateway, or node",
type: "string",
choices: [
"sandbox",
"gateway",
"node"
]
},
{
name: "security",
description: "deny, allowlist, or full",
type: "string",
choices: [
"deny",
"allowlist",
"full"
]
},
{
name: "ask",
description: "off, on-miss, or always",
type: "string",
choices: [
"off",
"on-miss",
"always"
]
},
{
name: "node",
description: "Node id or name",
type: "string"
}
],
argsParsing: "none",
formatArgs: COMMAND_ARG_FORMATTERS.exec
}),
defineChatCommand({
key: "model",
nativeName: "model",
description: "Show or set the model.",
textAlias: "/model",
category: "options",
args: [{
name: "model",
description: "Model id (provider/model or id)",
type: "string"
}]
}),
defineChatCommand({
key: "models",
nativeName: "models",
description: "List model providers or provider models.",
textAlias: "/models",
argsParsing: "none",
acceptsArgs: true,
category: "options"
}),
defineChatCommand({
key: "queue",
nativeName: "queue",
description: "Adjust queue settings.",
textAlias: "/queue",
category: "options",
args: [
{
name: "mode",
description: "queue mode",
type: "string",
choices: [
"steer",
"interrupt",
"followup",
"collect",
"steer-backlog"
]
},
{
name: "debounce",
description: "debounce duration (e.g. 500ms, 2s)",
type: "string"
},
{
name: "cap",
description: "queue cap",
type: "number"
},
{
name: "drop",
description: "drop policy",
type: "string",
choices: [
"old",
"new",
"summarize"
]
}
],
argsParsing: "none",
formatArgs: COMMAND_ARG_FORMATTERS.queue
}),
defineChatCommand({
key: "bash",
description: "Run host shell commands (host-only).",
textAlias: "/bash",
scope: "text",
category: "tools",
args: [{
name: "command",
description: "Shell command",
type: "string",
captureRemaining: true
}]
}),
...listChannelDocks().filter((dock) => dock.capabilities.nativeCommands).map((dock) => defineDockCommand(dock))
];
registerAlias(commands, "whoami", "/id");
registerAlias(commands, "think", "/thinking", "/t");
registerAlias(commands, "verbose", "/v");
registerAlias(commands, "reasoning", "/reason");
registerAlias(commands, "elevated", "/elev");
registerAlias(commands, "steer", "/tell");
assertCommandRegistry(commands);
return commands;
}
function getChatCommands() {
const registry = getActivePluginRegistry();
if (cachedCommands && registry === cachedRegistry) return cachedCommands;
const commands = buildChatCommands();
cachedCommands = commands;
cachedRegistry = registry;
cachedNativeCommandSurfaces = null;
return commands;
}
function getNativeCommandSurfaces() {
const registry = getActivePluginRegistry();
if (cachedNativeCommandSurfaces && registry === cachedNativeRegistry) return cachedNativeCommandSurfaces;
cachedNativeCommandSurfaces = new Set(listChannelDocks().filter((dock) => dock.capabilities.nativeCommands).map((dock) => dock.id));
cachedNativeRegistry = registry;
return cachedNativeCommandSurfaces;
}
//#endregion
//#region src/auto-reply/commands-registry.ts
var commands_registry_exports = /* @__PURE__ */ __exportAll({
buildCommandText: () => buildCommandText,
buildCommandTextFromArgs: () => buildCommandTextFromArgs,
findCommandByNativeName: () => findCommandByNativeName,
getCommandDetection: () => getCommandDetection,
isCommandEnabled: () => isCommandEnabled,
isCommandMessage: () => isCommandMessage,
isNativeCommandSurface: () => isNativeCommandSurface,
listChatCommands: () => listChatCommands,
listChatCommandsForConfig: () => listChatCommandsForConfig,
listNativeCommandSpecs: () => listNativeCommandSpecs,
listNativeCommandSpecsForConfig: () => listNativeCommandSpecsForConfig,
maybeResolveTextAlias: () => maybeResolveTextAlias,
normalizeCommandBody: () => normalizeCommandBody,
parseCommandArgs: () => parseCommandArgs,
resolveCommandArgChoices: () => resolveCommandArgChoices,
resolveCommandArgMenu: () => resolveCommandArgMenu,
resolveTextCommand: () => resolveTextCommand,
serializeCommandArgs: () => serializeCommandArgs,
shouldHandleTextCommands: () => shouldHandleTextCommands
});
let cachedTextAliasMap = null;
let cachedTextAliasCommands = null;
let cachedDetection;
let cachedDetectionCommands = null;
function getTextAliasMap() {
const commands = getChatCommands();
if (cachedTextAliasMap && cachedTextAliasCommands === commands) return cachedTextAliasMap;
const map = /* @__PURE__ */ new Map();
for (const command of commands) {
const canonical = command.textAliases[0]?.trim() || `/${command.key}`;
const acceptsArgs = Boolean(command.acceptsArgs);
for (const alias of command.textAliases) {
const normalized = alias.trim().toLowerCase();
if (!normalized) continue;
if (!map.has(normalized)) map.set(normalized, {
key: command.key,
canonical,
acceptsArgs
});
}
}
cachedTextAliasMap = map;
cachedTextAliasCommands = commands;
return map;
}
function buildSkillCommandDefinitions(skillCommands) {
if (!skillCommands || skillCommands.length === 0) return [];
return skillCommands.map((spec) => ({
key: `skill:${spec.skillName}`,
nativeName: spec.name,
description: spec.description,
textAliases: [`/${spec.name}`],
acceptsArgs: true,
argsParsing: "none",
scope: "both"
}));
}
function listChatCommands(params) {
const commands = getChatCommands();
if (!params?.skillCommands?.length) return [...commands];
return [...commands, ...buildSkillCommandDefinitions(params.skillCommands)];
}
function isCommandEnabled(cfg, commandKey) {
if (commandKey === "config") return cfg.commands?.config === true;
if (commandKey === "debug") return cfg.commands?.debug === true;
if (commandKey === "bash") return cfg.commands?.bash === true;
return true;
}
function listChatCommandsForConfig(cfg, params) {
const base = getChatCommands().filter((command) => isCommandEnabled(cfg, command.key));
if (!params?.skillCommands?.length) return base;
return [...base, ...buildSkillCommandDefinitions(params.skillCommands)];
}
const NATIVE_NAME_OVERRIDES = { discord: { tts: "voice" } };
function resolveNativeName(command, provider) {
if (!command.nativeName) return;
if (provider) {
const override = NATIVE_NAME_OVERRIDES[provider]?.[command.key];
if (override) return override;
}
return command.nativeName;
}
function toNativeCommandSpec(command, provider) {
return {
name: resolveNativeName(command, provider) ?? command.key,
description: command.description,
acceptsArgs: Boolean(command.acceptsArgs),
args: command.args
};
}
function listNativeSpecsFromCommands(commands, provider) {
return commands.filter((command) => command.scope !== "text" && command.nativeName).map((command) => toNativeCommandSpec(command, provider));
}
function listNativeCommandSpecs(params) {
return listNativeSpecsFromCommands(listChatCommands({ skillCommands: params?.skillCommands }), params?.provider);
}
function listNativeCommandSpecsForConfig(cfg, params) {
return listNativeSpecsFromCommands(listChatCommandsForConfig(cfg, params), params?.provider);
}
function findCommandByNativeName(name, provider) {
const normalized = name.trim().toLowerCase();
return getChatCommands().find((command) => command.scope !== "text" && resolveNativeName(command, provider)?.toLowerCase() === normalized);
}
function buildCommandText(commandName, args) {
const trimmedArgs = args?.trim();
return trimmedArgs ? `/${commandName} ${trimmedArgs}` : `/${commandName}`;
}
function parsePositionalArgs(definitions, raw) {
const values = {};
const trimmed = raw.trim();
if (!trimmed) return values;
const tokens = trimmed.split(/\s+/).filter(Boolean);
let index = 0;
for (const definition of definitions) {
if (index >= tokens.length) break;
if (definition.captureRemaining) {
values[definition.name] = tokens.slice(index).join(" ");
index = tokens.length;
break;
}
values[definition.name] = tokens[index];
index += 1;
}
return values;
}
function formatPositionalArgs(definitions, values) {
const parts = [];
for (const definition of definitions) {
const value = values[definition.name];
if (value == null) continue;
let rendered;
if (typeof value === "string") rendered = value.trim();
else rendered = String(value);
if (!rendered) continue;
parts.push(rendered);
if (definition.captureRemaining) break;
}
return parts.length > 0 ? parts.join(" ") : void 0;
}
function parseCommandArgs(command, raw) {
const trimmed = raw?.trim();
if (!trimmed) return;
if (!command.args || command.argsParsing === "none") return { raw: trimmed };
return {
raw: trimmed,
values: parsePositionalArgs(command.args, trimmed)
};
}
function serializeCommandArgs(command, args) {
if (!args) return;
const raw = args.raw?.trim();
if (raw) return raw;
if (!args.values || !command.args) return;
if (command.formatArgs) return command.formatArgs(args.values);
return formatPositionalArgs(command.args, args.values);
}
function buildCommandTextFromArgs(command, args) {
return buildCommandText(command.nativeName ?? command.key, serializeCommandArgs(command, args));
}
function resolveDefaultCommandContext(cfg) {
const resolved = resolveConfiguredModelRef({
cfg: cfg ?? {},
defaultProvider: DEFAULT_PROVIDER,
defaultModel: DEFAULT_MODEL
});
return {
provider: resolved.provider ?? DEFAULT_PROVIDER,
model: resolved.model ?? DEFAULT_MODEL
};
}
function resolveCommandArgChoices(params) {
const { command, arg, cfg } = params;
if (!arg.choices) return [];
const provided = arg.choices;
return (Array.isArray(provided) ? provided : (() => {
const defaults = resolveDefaultCommandContext(cfg);
return provided({
cfg,
provider: params.provider ?? defaults.provider,
model: params.model ?? defaults.model,
command,
arg
});
})()).map((choice) => typeof choice === "string" ? {
value: choice,
label: choice
} : choice);
}
function resolveCommandArgMenu(params) {
const { command, args, cfg } = params;
if (!command.args || !command.argsMenu) return null;
if (command.argsParsing === "none") return null;
const argSpec = command.argsMenu;
const argName = argSpec === "auto" ? command.args.find((arg) => resolveCommandArgChoices({
command,
arg,
cfg
}).length > 0)?.name : argSpec.arg;
if (!argName) return null;
if (args?.values && args.values[argName] != null) return null;
if (args?.raw && !args.values) return null;
const arg = command.args.find((entry) => entry.name === argName);
if (!arg) return null;
const choices = resolveCommandArgChoices({
command,
arg,
cfg
});
if (choices.length === 0) return null;
return {
arg,
choices,
title: argSpec !== "auto" ? argSpec.title : void 0
};
}
function normalizeCommandBody(raw, options) {
const trimmed = raw.trim();
if (!trimmed.startsWith("/")) return trimmed;
const newline = trimmed.indexOf("\n");
const singleLine = newline === -1 ? trimmed : trimmed.slice(0, newline).trim();
const colonMatch = singleLine.match(/^\/([^\s:]+)\s*:(.*)$/);
const normalized = colonMatch ? (() => {
const [, command, rest] = colonMatch;
const normalizedRest = rest.trimStart();
return normalizedRest ? `/${command} ${normalizedRest}` : `/${command}`;
})() : singleLine;
const normalizedBotUsername = options?.botUsername?.trim().toLowerCase();
const mentionMatch = normalizedBotUsername ? normalized.match(/^\/([^\s@]+)@([^\s]+)(.*)$/) : null;
const commandBody = mentionMatch && mentionMatch[2].toLowerCase() === normalizedBotUsername ? `/${mentionMatch[1]}${mentionMatch[3] ?? ""}` : normalized;
const lowered = commandBody.toLowerCase();
const textAliasMap = getTextAliasMap();
const exact = textAliasMap.get(lowered);
if (exact) return exact.canonical;
const tokenMatch = commandBody.match(/^\/([^\s]+)(?:\s+([\s\S]+))?$/);
if (!tokenMatch) return commandBody;
const [, token, rest] = tokenMatch;
const tokenKey = `/${token.toLowerCase()}`;
const tokenSpec = textAliasMap.get(tokenKey);
if (!tokenSpec) return commandBody;
if (rest && !tokenSpec.acceptsArgs) return commandBody;
const normalizedRest = rest?.trimStart();
return normalizedRest ? `${tokenSpec.canonical} ${normalizedRest}` : tokenSpec.canonical;
}
function isCommandMessage(raw) {
return normalizeCommandBody(raw).startsWith("/");
}
function getCommandDetection(_cfg) {
const commands = getChatCommands();
if (cachedDetection && cachedDetectionCommands === commands) return cachedDetection;
const exact = /* @__PURE__ */ new Set();
const patterns = [];
for (const cmd of commands) for (const alias of cmd.textAliases) {
const normalized = alias.trim().toLowerCase();
if (!normalized) continue;
exact.add(normalized);
const escaped = escapeRegExp(normalized);
if (!escaped) continue;
if (cmd.acceptsArgs) patterns.push(`${escaped}(?:\\s+.+|\\s*:\\s*.*)?`);
else patterns.push(`${escaped}(?:\\s*:\\s*)?`);
}
cachedDetection = {
exact,
regex: patterns.length ? new RegExp(`^(?:${patterns.join("|")})$`, "i") : /$^/
};
cachedDetectionCommands = commands;
return cachedDetection;
}
function maybeResolveTextAlias(raw, cfg) {
const trimmed = normalizeCommandBody(raw).trim();
if (!trimmed.startsWith("/")) return null;
const detection = getCommandDetection(cfg);
const normalized = trimmed.toLowerCase();
if (detection.exact.has(normalized)) return normalized;
if (!detection.regex.test(normalized)) return null;
const tokenMatch = normalized.match(/^\/([^\s:]+)(?:\s|$)/);
if (!tokenMatch) return null;
const tokenKey = `/${tokenMatch[1]}`;
return getTextAliasMap().has(tokenKey) ? tokenKey : null;
}
function resolveTextCommand(raw, cfg) {
const trimmed = normalizeCommandBody(raw).trim();
const alias = maybeResolveTextAlias(trimmed, cfg);
if (!alias) return null;
const spec = getTextAliasMap().get(alias);
if (!spec) return null;
const command = getChatCommands().find((entry) => entry.key === spec.key);
if (!command) return null;
if (!spec.acceptsArgs) return { command };
return {
command,
args: trimmed.slice(alias.length).trim() || void 0
};
}
function isNativeCommandSurface(surface) {
if (!surface) return false;
return getNativeCommandSurfaces().has(surface.toLowerCase());
}
function shouldHandleTextCommands(params) {
if (params.commandSource === "native") return true;
if (params.cfg.commands?.text !== false) return true;
return !isNativeCommandSurface(params.surface);
}
//#endregion
export { listChatCommandsForConfig as a, normalizeCommandBody as c, resolveCommandArgMenu as d, serializeCommandArgs as f, listChatCommands as i, parseCommandArgs as l, commands_registry_exports as n, listNativeCommandSpecs as o, shouldHandleTextCommands as p, findCommandByNativeName as r, listNativeCommandSpecsForConfig as s, buildCommandTextFromArgs as t, resolveCommandArgChoices as u };