mirror of https://github.com/openclaw/openclaw.git
refactor: derive channel metadata from plugin manifests
This commit is contained in:
parent
c14b169a1b
commit
02d4c1f2c3
|
|
@ -34,7 +34,8 @@
|
|||
"docsPath": "/channels/discord",
|
||||
"docsLabel": "discord",
|
||||
"blurb": "very well supported right now.",
|
||||
"systemImage": "bubble.left.and.bubble.right"
|
||||
"systemImage": "bubble.left.and.bubble.right",
|
||||
"markdownCapable": true
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/discord",
|
||||
|
|
|
|||
|
|
@ -30,12 +30,14 @@
|
|||
"detailLabel": "Google Chat",
|
||||
"docsPath": "/channels/googlechat",
|
||||
"docsLabel": "googlechat",
|
||||
"blurb": "Google Workspace Chat app via HTTP webhooks.",
|
||||
"blurb": "Google Workspace Chat app with HTTP webhook.",
|
||||
"aliases": [
|
||||
"gchat",
|
||||
"google-chat"
|
||||
],
|
||||
"order": 55
|
||||
"order": 55,
|
||||
"systemImage": "message.badge",
|
||||
"markdownCapable": true
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/googlechat",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
"docsPath": "/channels/irc",
|
||||
"docsLabel": "irc",
|
||||
"blurb": "classic IRC networks with DM/channel routing and pairing controls.",
|
||||
"aliases": [
|
||||
"internet-relay-chat"
|
||||
],
|
||||
"systemImage": "network"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@
|
|||
"id": "line",
|
||||
"label": "LINE",
|
||||
"selectionLabel": "LINE (Messaging API)",
|
||||
"detailLabel": "LINE Bot",
|
||||
"docsPath": "/channels/line",
|
||||
"docsLabel": "line",
|
||||
"blurb": "LINE Messaging API bot for Japan/Taiwan/Thailand markets.",
|
||||
"blurb": "LINE Messaging API webhook bot.",
|
||||
"systemImage": "message",
|
||||
"order": 75,
|
||||
"quickstartAllowFrom": true
|
||||
},
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
"docsPath": "/channels/signal",
|
||||
"docsLabel": "signal",
|
||||
"blurb": "signal-cli linked device; more setup (David Reagans: \"Hop on Discord.\").",
|
||||
"systemImage": "antenna.radiowaves.left.and.right"
|
||||
"systemImage": "antenna.radiowaves.left.and.right",
|
||||
"markdownCapable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@
|
|||
"docsPath": "/channels/slack",
|
||||
"docsLabel": "slack",
|
||||
"blurb": "supported (Socket Mode).",
|
||||
"systemImage": "number"
|
||||
"systemImage": "number",
|
||||
"markdownCapable": true
|
||||
},
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
|
|
|
|||
|
|
@ -22,7 +22,13 @@
|
|||
"docsPath": "/channels/telegram",
|
||||
"docsLabel": "telegram",
|
||||
"blurb": "simplest way to get started — register a bot with @BotFather and get going.",
|
||||
"systemImage": "paperplane"
|
||||
"systemImage": "paperplane",
|
||||
"selectionDocsPrefix": "",
|
||||
"selectionDocsOmitLabel": true,
|
||||
"selectionExtras": [
|
||||
"https://openclaw.ai"
|
||||
],
|
||||
"markdownCapable": true
|
||||
},
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
|
|
|
|||
|
|
@ -1,112 +1,111 @@
|
|||
import { GENERATED_BUNDLED_PLUGIN_METADATA } from "../plugins/bundled-plugin-metadata.generated.js";
|
||||
import type { PluginPackageChannel } from "../plugins/manifest.js";
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "./ids.js";
|
||||
import type { ChannelMeta } from "./plugins/types.js";
|
||||
|
||||
export type ChatChannelMeta = ChannelMeta;
|
||||
|
||||
const WEBSITE_URL = "https://openclaw.ai";
|
||||
const CHAT_CHANNEL_ID_SET = new Set<string>(CHAT_CHANNEL_ORDER);
|
||||
|
||||
const CHAT_CHANNEL_META: Record<ChatChannelId, ChannelMeta> = {
|
||||
telegram: {
|
||||
id: "telegram",
|
||||
label: "Telegram",
|
||||
selectionLabel: "Telegram (Bot API)",
|
||||
detailLabel: "Telegram Bot",
|
||||
docsPath: "/channels/telegram",
|
||||
docsLabel: "telegram",
|
||||
blurb: "simplest way to get started — register a bot with @BotFather and get going.",
|
||||
systemImage: "paperplane",
|
||||
selectionDocsPrefix: "",
|
||||
selectionDocsOmitLabel: true,
|
||||
selectionExtras: [WEBSITE_URL],
|
||||
},
|
||||
whatsapp: {
|
||||
id: "whatsapp",
|
||||
label: "WhatsApp",
|
||||
selectionLabel: "WhatsApp (QR link)",
|
||||
detailLabel: "WhatsApp Web",
|
||||
docsPath: "/channels/whatsapp",
|
||||
docsLabel: "whatsapp",
|
||||
blurb: "works with your own number; recommend a separate phone + eSIM.",
|
||||
systemImage: "message",
|
||||
},
|
||||
discord: {
|
||||
id: "discord",
|
||||
label: "Discord",
|
||||
selectionLabel: "Discord (Bot API)",
|
||||
detailLabel: "Discord Bot",
|
||||
docsPath: "/channels/discord",
|
||||
docsLabel: "discord",
|
||||
blurb: "very well supported right now.",
|
||||
systemImage: "bubble.left.and.bubble.right",
|
||||
},
|
||||
irc: {
|
||||
id: "irc",
|
||||
label: "IRC",
|
||||
selectionLabel: "IRC (Server + Nick)",
|
||||
detailLabel: "IRC",
|
||||
docsPath: "/channels/irc",
|
||||
docsLabel: "irc",
|
||||
blurb: "classic IRC networks with DM/channel routing and pairing controls.",
|
||||
systemImage: "network",
|
||||
},
|
||||
googlechat: {
|
||||
id: "googlechat",
|
||||
label: "Google Chat",
|
||||
selectionLabel: "Google Chat (Chat API)",
|
||||
detailLabel: "Google Chat",
|
||||
docsPath: "/channels/googlechat",
|
||||
docsLabel: "googlechat",
|
||||
blurb: "Google Workspace Chat app with HTTP webhook.",
|
||||
systemImage: "message.badge",
|
||||
},
|
||||
slack: {
|
||||
id: "slack",
|
||||
label: "Slack",
|
||||
selectionLabel: "Slack (Socket Mode)",
|
||||
detailLabel: "Slack Bot",
|
||||
docsPath: "/channels/slack",
|
||||
docsLabel: "slack",
|
||||
blurb: "supported (Socket Mode).",
|
||||
systemImage: "number",
|
||||
},
|
||||
signal: {
|
||||
id: "signal",
|
||||
label: "Signal",
|
||||
selectionLabel: "Signal (signal-cli)",
|
||||
detailLabel: "Signal REST",
|
||||
docsPath: "/channels/signal",
|
||||
docsLabel: "signal",
|
||||
blurb: 'signal-cli linked device; more setup (David Reagans: "Hop on Discord.").',
|
||||
systemImage: "antenna.radiowaves.left.and.right",
|
||||
},
|
||||
imessage: {
|
||||
id: "imessage",
|
||||
label: "iMessage",
|
||||
selectionLabel: "iMessage (imsg)",
|
||||
detailLabel: "iMessage",
|
||||
docsPath: "/channels/imessage",
|
||||
docsLabel: "imessage",
|
||||
blurb: "this is still a work in progress.",
|
||||
systemImage: "message.fill",
|
||||
},
|
||||
line: {
|
||||
id: "line",
|
||||
label: "LINE",
|
||||
selectionLabel: "LINE (Messaging API)",
|
||||
detailLabel: "LINE Bot",
|
||||
docsPath: "/channels/line",
|
||||
docsLabel: "line",
|
||||
blurb: "LINE Messaging API webhook bot.",
|
||||
systemImage: "message",
|
||||
},
|
||||
};
|
||||
function toChatChannelMeta(params: {
|
||||
id: ChatChannelId;
|
||||
channel: PluginPackageChannel;
|
||||
}): ChatChannelMeta {
|
||||
const label = params.channel.label?.trim();
|
||||
if (!label) {
|
||||
throw new Error(`Missing label for bundled chat channel "${params.id}"`);
|
||||
}
|
||||
|
||||
export const CHAT_CHANNEL_ALIASES: Record<string, ChatChannelId> = {
|
||||
imsg: "imessage",
|
||||
"internet-relay-chat": "irc",
|
||||
"google-chat": "googlechat",
|
||||
gchat: "googlechat",
|
||||
};
|
||||
return {
|
||||
id: params.id,
|
||||
label,
|
||||
selectionLabel: params.channel.selectionLabel?.trim() || label,
|
||||
docsPath: params.channel.docsPath?.trim() || `/channels/${params.id}`,
|
||||
docsLabel: params.channel.docsLabel?.trim() || undefined,
|
||||
blurb: params.channel.blurb?.trim() || "",
|
||||
...(params.channel.aliases?.length ? { aliases: params.channel.aliases } : {}),
|
||||
...(params.channel.order !== undefined ? { order: params.channel.order } : {}),
|
||||
...(params.channel.selectionDocsPrefix !== undefined
|
||||
? { selectionDocsPrefix: params.channel.selectionDocsPrefix }
|
||||
: {}),
|
||||
...(params.channel.selectionDocsOmitLabel !== undefined
|
||||
? { selectionDocsOmitLabel: params.channel.selectionDocsOmitLabel }
|
||||
: {}),
|
||||
...(params.channel.selectionExtras?.length
|
||||
? { selectionExtras: params.channel.selectionExtras }
|
||||
: {}),
|
||||
...(params.channel.detailLabel?.trim()
|
||||
? { detailLabel: params.channel.detailLabel.trim() }
|
||||
: {}),
|
||||
...(params.channel.systemImage?.trim()
|
||||
? { systemImage: params.channel.systemImage.trim() }
|
||||
: {}),
|
||||
...(params.channel.markdownCapable !== undefined
|
||||
? { markdownCapable: params.channel.markdownCapable }
|
||||
: {}),
|
||||
...(params.channel.showConfigured !== undefined
|
||||
? { showConfigured: params.channel.showConfigured }
|
||||
: {}),
|
||||
...(params.channel.quickstartAllowFrom !== undefined
|
||||
? { quickstartAllowFrom: params.channel.quickstartAllowFrom }
|
||||
: {}),
|
||||
...(params.channel.forceAccountBinding !== undefined
|
||||
? { forceAccountBinding: params.channel.forceAccountBinding }
|
||||
: {}),
|
||||
...(params.channel.preferSessionLookupForAnnounceTarget !== undefined
|
||||
? {
|
||||
preferSessionLookupForAnnounceTarget: params.channel.preferSessionLookupForAnnounceTarget,
|
||||
}
|
||||
: {}),
|
||||
...(params.channel.preferOver?.length ? { preferOver: params.channel.preferOver } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function buildChatChannelMetaById(): Record<ChatChannelId, ChatChannelMeta> {
|
||||
const entries = new Map<ChatChannelId, ChatChannelMeta>();
|
||||
|
||||
for (const entry of GENERATED_BUNDLED_PLUGIN_METADATA) {
|
||||
const channel =
|
||||
entry.packageManifest && "channel" in entry.packageManifest
|
||||
? entry.packageManifest.channel
|
||||
: undefined;
|
||||
if (!channel) {
|
||||
continue;
|
||||
}
|
||||
const rawId = channel?.id?.trim();
|
||||
if (!rawId || !CHAT_CHANNEL_ID_SET.has(rawId)) {
|
||||
continue;
|
||||
}
|
||||
const id = rawId as ChatChannelId;
|
||||
entries.set(
|
||||
id,
|
||||
toChatChannelMeta({
|
||||
id,
|
||||
channel,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const missingIds = CHAT_CHANNEL_ORDER.filter((id) => !entries.has(id));
|
||||
if (missingIds.length > 0) {
|
||||
throw new Error(`Missing bundled chat channel metadata for: ${missingIds.join(", ")}`);
|
||||
}
|
||||
|
||||
return Object.freeze(Object.fromEntries(entries)) as Record<ChatChannelId, ChatChannelMeta>;
|
||||
}
|
||||
|
||||
const CHAT_CHANNEL_META = buildChatChannelMetaById();
|
||||
|
||||
export const CHAT_CHANNEL_ALIASES: Record<string, ChatChannelId> = Object.freeze(
|
||||
Object.fromEntries(
|
||||
Object.values(CHAT_CHANNEL_META)
|
||||
.flatMap((meta) =>
|
||||
(meta.aliases ?? []).map((alias) => [alias.trim().toLowerCase(), meta.id] as const),
|
||||
)
|
||||
.filter(([alias]) => alias.length > 0)
|
||||
.toSorted(([left], [right]) => left.localeCompare(right)),
|
||||
),
|
||||
) as Record<string, ChatChannelId>;
|
||||
|
||||
function normalizeChannelKey(raw?: string | null): string | undefined {
|
||||
const normalized = raw?.trim().toLowerCase();
|
||||
|
|
|
|||
|
|
@ -200,6 +200,9 @@ function toChannelMeta(params: {
|
|||
: {}),
|
||||
...(params.channel.selectionExtras ? { selectionExtras: params.channel.selectionExtras } : {}),
|
||||
...(systemImage ? { systemImage } : {}),
|
||||
...(params.channel.markdownCapable !== undefined
|
||||
? { markdownCapable: params.channel.markdownCapable }
|
||||
: {}),
|
||||
...(params.channel.showConfigured !== undefined
|
||||
? { showConfigured: params.channel.showConfigured }
|
||||
: {}),
|
||||
|
|
|
|||
|
|
@ -127,17 +127,18 @@ export type ChannelMeta = {
|
|||
docsLabel?: string;
|
||||
blurb: string;
|
||||
order?: number;
|
||||
aliases?: string[];
|
||||
aliases?: readonly string[];
|
||||
selectionDocsPrefix?: string;
|
||||
selectionDocsOmitLabel?: boolean;
|
||||
selectionExtras?: string[];
|
||||
selectionExtras?: readonly string[];
|
||||
detailLabel?: string;
|
||||
systemImage?: string;
|
||||
markdownCapable?: boolean;
|
||||
showConfigured?: boolean;
|
||||
quickstartAllowFrom?: boolean;
|
||||
forceAccountBinding?: boolean;
|
||||
preferSessionLookupForAnnounceTarget?: boolean;
|
||||
preferOver?: string[];
|
||||
preferOver?: readonly string[];
|
||||
};
|
||||
|
||||
/** Snapshot row returned by channel status and lifecycle surfaces. */
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import {
|
|||
type ChatChannelMeta,
|
||||
} from "./chat-meta.js";
|
||||
import { CHANNEL_IDS, CHAT_CHANNEL_ORDER, type ChatChannelId } from "./ids.js";
|
||||
import type { ChannelId } from "./plugins/types.js";
|
||||
import type { ChannelId, ChannelMeta } from "./plugins/types.js";
|
||||
export { CHANNEL_IDS, CHAT_CHANNEL_ORDER } from "./ids.js";
|
||||
export type { ChatChannelId } from "./ids.js";
|
||||
|
||||
type RegisteredChannelPluginEntry = {
|
||||
plugin: {
|
||||
id?: string | null;
|
||||
meta?: { aliases?: string[] | null } | null;
|
||||
meta?: Pick<ChannelMeta, "aliases" | "markdownCapable"> | null;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -39,6 +39,18 @@ function findRegisteredChannelPluginEntry(
|
|||
});
|
||||
}
|
||||
|
||||
function findRegisteredChannelPluginEntryById(
|
||||
id: string,
|
||||
): RegisteredChannelPluginEntry | undefined {
|
||||
const normalizedId = normalizeChannelKey(id);
|
||||
if (!normalizedId) {
|
||||
return undefined;
|
||||
}
|
||||
return listRegisteredChannelPluginEntries().find(
|
||||
(entry) => normalizeChannelKey(entry.plugin.id) === normalizedId,
|
||||
);
|
||||
}
|
||||
|
||||
const normalizeChannelKey = (raw?: string | null): string | undefined => {
|
||||
const normalized = raw?.trim().toLowerCase();
|
||||
return normalized || undefined;
|
||||
|
|
@ -80,6 +92,12 @@ export function listRegisteredChannelPluginAliases(): string[] {
|
|||
return listRegisteredChannelPluginEntries().flatMap((entry) => entry.plugin.meta?.aliases ?? []);
|
||||
}
|
||||
|
||||
export function getRegisteredChannelPluginMeta(
|
||||
id: string,
|
||||
): Pick<ChannelMeta, "aliases" | "markdownCapable"> | null {
|
||||
return findRegisteredChannelPluginEntryById(id)?.plugin.meta ?? null;
|
||||
}
|
||||
|
||||
export function formatChannelPrimerLine(meta: ChatChannelMeta): string {
|
||||
return `${meta.label}: ${meta.blurb}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -349,16 +349,16 @@ function resolvePreferredOverIds(
|
|||
): string[] {
|
||||
const normalized = normalizeChatChannelId(pluginId);
|
||||
if (normalized) {
|
||||
return getChatChannelMeta(normalized).preferOver ?? [];
|
||||
return [...(getChatChannelMeta(normalized).preferOver ?? [])];
|
||||
}
|
||||
const installedPlugin = registry.plugins.find((record) => record.id === pluginId);
|
||||
const manifestChannelPreferOver = installedPlugin?.channelConfigs?.[pluginId]?.preferOver;
|
||||
if (manifestChannelPreferOver?.length) {
|
||||
return manifestChannelPreferOver;
|
||||
return [...manifestChannelPreferOver];
|
||||
}
|
||||
const installedChannelMeta = installedPlugin?.channelCatalogMeta;
|
||||
if (installedChannelMeta?.preferOver?.length) {
|
||||
return installedChannelMeta.preferOver;
|
||||
return [...installedChannelMeta.preferOver];
|
||||
}
|
||||
return resolveExternalCatalogPreferOver(pluginId, env);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ function resolveChannelSupportsCurrentConversationBinding(channel: string): bool
|
|||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
const matchesPluginId = (plugin: { id: string; meta?: { aliases?: string[] } }) =>
|
||||
const matchesPluginId = (plugin: { id: string; meta?: { aliases?: readonly string[] } }) =>
|
||||
plugin.id === normalized ||
|
||||
(plugin.meta?.aliases ?? []).some((alias) => alias.trim().toLowerCase() === normalized);
|
||||
const plugin =
|
||||
|
|
|
|||
|
|
@ -1369,6 +1369,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
docsLabel: "discord",
|
||||
blurb: "very well supported right now.",
|
||||
systemImage: "bubble.left.and.bubble.right",
|
||||
markdownCapable: true,
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@openclaw/discord",
|
||||
|
|
@ -5574,9 +5575,11 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
detailLabel: "Google Chat",
|
||||
docsPath: "/channels/googlechat",
|
||||
docsLabel: "googlechat",
|
||||
blurb: "Google Workspace Chat app via HTTP webhooks.",
|
||||
blurb: "Google Workspace Chat app with HTTP webhook.",
|
||||
aliases: ["gchat", "google-chat"],
|
||||
order: 55,
|
||||
systemImage: "message.badge",
|
||||
markdownCapable: true,
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@openclaw/googlechat",
|
||||
|
|
@ -6368,7 +6371,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
additionalProperties: false,
|
||||
},
|
||||
label: "Google Chat",
|
||||
description: "Google Workspace Chat app via HTTP webhooks.",
|
||||
description: "Google Workspace Chat app with HTTP webhook.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -7094,6 +7097,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
docsPath: "/channels/irc",
|
||||
docsLabel: "irc",
|
||||
blurb: "classic IRC networks with DM/channel routing and pairing controls.",
|
||||
aliases: ["internet-relay-chat"],
|
||||
systemImage: "network",
|
||||
},
|
||||
install: {
|
||||
|
|
@ -7851,9 +7855,11 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
id: "line",
|
||||
label: "LINE",
|
||||
selectionLabel: "LINE (Messaging API)",
|
||||
detailLabel: "LINE Bot",
|
||||
docsPath: "/channels/line",
|
||||
docsLabel: "line",
|
||||
blurb: "LINE Messaging API bot for Japan/Taiwan/Thailand markets.",
|
||||
blurb: "LINE Messaging API webhook bot.",
|
||||
systemImage: "message",
|
||||
order: 75,
|
||||
quickstartAllowFrom: true,
|
||||
},
|
||||
|
|
@ -8105,7 +8111,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
additionalProperties: false,
|
||||
},
|
||||
label: "LINE",
|
||||
description: "LINE Messaging API bot for Japan/Taiwan/Thailand markets.",
|
||||
description: "LINE Messaging API webhook bot.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -11938,6 +11944,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
docsLabel: "signal",
|
||||
blurb: 'signal-cli linked device; more setup (David Reagans: "Hop on Discord.").',
|
||||
systemImage: "antenna.radiowaves.left.and.right",
|
||||
markdownCapable: true,
|
||||
},
|
||||
},
|
||||
manifest: {
|
||||
|
|
@ -12634,6 +12641,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
docsLabel: "slack",
|
||||
blurb: "supported (Socket Mode).",
|
||||
systemImage: "number",
|
||||
markdownCapable: true,
|
||||
},
|
||||
},
|
||||
manifest: {
|
||||
|
|
@ -14549,6 +14557,10 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
|||
docsLabel: "telegram",
|
||||
blurb: "simplest way to get started — register a bot with @BotFather and get going.",
|
||||
systemImage: "paperplane",
|
||||
selectionDocsPrefix: "",
|
||||
selectionDocsOmitLabel: true,
|
||||
selectionExtras: ["https://openclaw.ai"],
|
||||
markdownCapable: true,
|
||||
},
|
||||
},
|
||||
manifest: {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export type PluginManifestRecord = {
|
|||
id: string;
|
||||
label?: string;
|
||||
blurb?: string;
|
||||
preferOver?: string[];
|
||||
preferOver?: readonly string[];
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -343,12 +343,13 @@ export type PluginPackageChannel = {
|
|||
docsLabel?: string;
|
||||
blurb?: string;
|
||||
order?: number;
|
||||
aliases?: string[];
|
||||
preferOver?: string[];
|
||||
aliases?: readonly string[];
|
||||
preferOver?: readonly string[];
|
||||
systemImage?: string;
|
||||
selectionDocsPrefix?: string;
|
||||
selectionDocsOmitLabel?: boolean;
|
||||
selectionExtras?: string[];
|
||||
selectionExtras?: readonly string[];
|
||||
markdownCapable?: boolean;
|
||||
showConfigured?: boolean;
|
||||
quickstartAllowFrom?: boolean;
|
||||
forceAccountBinding?: boolean;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const createChannelTestPluginBase = (params: {
|
|||
id: ChannelId;
|
||||
label?: string;
|
||||
docsPath?: string;
|
||||
markdownCapable?: boolean;
|
||||
capabilities?: ChannelCapabilities;
|
||||
config?: Partial<ChannelPlugin["config"]>;
|
||||
}): Pick<ChannelPlugin, "id" | "meta" | "capabilities" | "config"> => ({
|
||||
|
|
@ -53,6 +54,7 @@ export const createChannelTestPluginBase = (params: {
|
|||
selectionLabel: params.label ?? String(params.id),
|
||||
docsPath: params.docsPath ?? `/channels/${params.id}`,
|
||||
blurb: "test stub.",
|
||||
...(params.markdownCapable !== undefined ? { markdownCapable: params.markdownCapable } : {}),
|
||||
},
|
||||
capabilities: params.capabilities ?? { chatTypes: ["direct"] },
|
||||
config: {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|||
import type { ChannelPlugin } from "../channels/plugins/types.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import { resolveGatewayMessageChannel } from "./message-channel.js";
|
||||
import {
|
||||
isMarkdownCapableMessageChannel,
|
||||
resolveGatewayMessageChannel,
|
||||
} from "./message-channel.js";
|
||||
|
||||
const emptyRegistry = createTestRegistry([]);
|
||||
const demoAliasPlugin: ChannelPlugin = {
|
||||
|
|
@ -21,6 +24,15 @@ const demoAliasPlugin: ChannelPlugin = {
|
|||
},
|
||||
};
|
||||
|
||||
const demoMarkdownPlugin: ChannelPlugin = {
|
||||
...createChannelTestPluginBase({
|
||||
id: "demo-markdown-channel",
|
||||
label: "Demo Markdown Channel",
|
||||
docsPath: "/channels/demo-markdown-channel",
|
||||
markdownCapable: true,
|
||||
}),
|
||||
};
|
||||
|
||||
describe("message-channel", () => {
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(emptyRegistry);
|
||||
|
|
@ -45,4 +57,15 @@ describe("message-channel", () => {
|
|||
);
|
||||
expect(resolveGatewayMessageChannel("workspace-chat")).toBe("demo-alias-channel");
|
||||
});
|
||||
|
||||
it("reads markdown capability from channel metadata", () => {
|
||||
expect(isMarkdownCapableMessageChannel("telegram")).toBe(true);
|
||||
expect(isMarkdownCapableMessageChannel("whatsapp")).toBe(false);
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{ pluginId: "demo-markdown-channel", plugin: demoMarkdownPlugin, source: "test" },
|
||||
]),
|
||||
);
|
||||
expect(isMarkdownCapableMessageChannel("demo-markdown-channel")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import type { ChannelId } from "../channels/plugins/types.js";
|
||||
import {
|
||||
CHANNEL_IDS,
|
||||
getChatChannelMeta,
|
||||
getRegisteredChannelPluginMeta,
|
||||
listRegisteredChannelPluginAliases,
|
||||
listRegisteredChannelPluginIds,
|
||||
listChatChannelAliases,
|
||||
|
|
@ -19,16 +21,6 @@ import {
|
|||
export const INTERNAL_MESSAGE_CHANNEL = "webchat" as const;
|
||||
export type InternalMessageChannel = typeof INTERNAL_MESSAGE_CHANNEL;
|
||||
|
||||
const MARKDOWN_CAPABLE_CHANNELS = new Set<string>([
|
||||
"slack",
|
||||
"telegram",
|
||||
"signal",
|
||||
"discord",
|
||||
"googlechat",
|
||||
"tui",
|
||||
INTERNAL_MESSAGE_CHANNEL,
|
||||
]);
|
||||
|
||||
export { GATEWAY_CLIENT_NAMES, GATEWAY_CLIENT_MODES };
|
||||
export type { GatewayClientName, GatewayClientMode };
|
||||
export { normalizeGatewayClientName, normalizeGatewayClientMode };
|
||||
|
|
@ -139,5 +131,12 @@ export function isMarkdownCapableMessageChannel(raw?: string | null): boolean {
|
|||
if (!channel) {
|
||||
return false;
|
||||
}
|
||||
return MARKDOWN_CAPABLE_CHANNELS.has(channel);
|
||||
if (channel === INTERNAL_MESSAGE_CHANNEL || channel === "tui") {
|
||||
return true;
|
||||
}
|
||||
const builtInChannel = normalizeChatChannelId(channel);
|
||||
if (builtInChannel) {
|
||||
return getChatChannelMeta(builtInChannel).markdownCapable === true;
|
||||
}
|
||||
return getRegisteredChannelPluginMeta(channel)?.markdownCapable === true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue