This adds a new `api.registerCommand()` method to the plugin API, allowing plugins to register slash commands that execute without invoking the AI agent. Features: - Plugin commands are processed before built-in commands and the agent - Commands can optionally require authorization - Commands can accept arguments - Async handlers are supported Use case: plugins can implement toggle commands (like /tts_on, /tts_off) that respond immediately without consuming LLM API calls. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
98 lines
2.6 KiB
TypeScript
98 lines
2.6 KiB
TypeScript
import { imessageOutbound } from "../channels/plugins/outbound/imessage.js";
|
|
import type {
|
|
ChannelCapabilities,
|
|
ChannelId,
|
|
ChannelOutboundAdapter,
|
|
ChannelPlugin,
|
|
} from "../channels/plugins/types.js";
|
|
import type { PluginRegistry } from "../plugins/registry.js";
|
|
import { normalizeIMessageHandle } from "../imessage/targets.js";
|
|
|
|
export const createTestRegistry = (channels: PluginRegistry["channels"] = []): PluginRegistry => ({
|
|
plugins: [],
|
|
tools: [],
|
|
hooks: [],
|
|
typedHooks: [],
|
|
channels,
|
|
providers: [],
|
|
gatewayHandlers: {},
|
|
httpHandlers: [],
|
|
cliRegistrars: [],
|
|
services: [],
|
|
commands: [],
|
|
diagnostics: [],
|
|
});
|
|
|
|
export const createIMessageTestPlugin = (params?: {
|
|
outbound?: ChannelOutboundAdapter;
|
|
}): ChannelPlugin => ({
|
|
id: "imessage",
|
|
meta: {
|
|
id: "imessage",
|
|
label: "iMessage",
|
|
selectionLabel: "iMessage (imsg)",
|
|
docsPath: "/channels/imessage",
|
|
blurb: "iMessage test stub.",
|
|
aliases: ["imsg"],
|
|
},
|
|
capabilities: { chatTypes: ["direct", "group"], media: true },
|
|
config: {
|
|
listAccountIds: () => [],
|
|
resolveAccount: () => ({}),
|
|
},
|
|
status: {
|
|
collectStatusIssues: (accounts) =>
|
|
accounts.flatMap((account) => {
|
|
const lastError = typeof account.lastError === "string" ? account.lastError.trim() : "";
|
|
if (!lastError) return [];
|
|
return [
|
|
{
|
|
channel: "imessage",
|
|
accountId: account.accountId,
|
|
kind: "runtime",
|
|
message: `Channel error: ${lastError}`,
|
|
},
|
|
];
|
|
}),
|
|
},
|
|
outbound: params?.outbound ?? imessageOutbound,
|
|
messaging: {
|
|
targetResolver: {
|
|
looksLikeId: (raw) => {
|
|
const trimmed = raw.trim();
|
|
if (!trimmed) return false;
|
|
if (/^(imessage:|sms:|auto:|chat_id:|chat_guid:|chat_identifier:)/i.test(trimmed)) {
|
|
return true;
|
|
}
|
|
if (trimmed.includes("@")) return true;
|
|
return /^\+?\d{3,}$/.test(trimmed);
|
|
},
|
|
hint: "<handle|chat_id:ID>",
|
|
},
|
|
normalizeTarget: (raw) => normalizeIMessageHandle(raw),
|
|
},
|
|
});
|
|
|
|
export const createOutboundTestPlugin = (params: {
|
|
id: ChannelId;
|
|
outbound: ChannelOutboundAdapter;
|
|
label?: string;
|
|
docsPath?: string;
|
|
capabilities?: ChannelCapabilities;
|
|
}): ChannelPlugin => ({
|
|
id: params.id,
|
|
meta: {
|
|
id: params.id,
|
|
label: params.label ?? String(params.id),
|
|
selectionLabel: params.label ?? String(params.id),
|
|
docsPath: params.docsPath ?? `/channels/${params.id}`,
|
|
blurb: "test stub.",
|
|
},
|
|
capabilities: params.capabilities ?? { chatTypes: ["direct"] },
|
|
config: {
|
|
listAccountIds: () => [],
|
|
resolveAccount: () => ({}),
|
|
},
|
|
outbound: params.outbound,
|
|
});
|