feat: load channel plugins

This commit is contained in:
Peter Steinberger
2026-01-15 02:42:41 +00:00
parent b1e3d79eaa
commit 2b4a68e276
49 changed files with 494 additions and 159 deletions

View File

@@ -1,4 +1,4 @@
import { normalizeChannelId } from "../channels/registry.js";
import { normalizeChannelId } from "../channels/plugins/index.js";
import { normalizeAccountId } from "../routing/session-key.js";
import type { ClawdbotConfig } from "./config.js";

View File

@@ -1,5 +1,5 @@
import type { ChannelId } from "../channels/plugins/types.js";
import { normalizeChannelId } from "../channels/registry.js";
import { normalizeChannelId } from "../channels/plugins/index.js";
import type { NativeCommandsSetting } from "./types.js";
function resolveAutoDefault(providerId?: ChannelId): boolean {

View File

@@ -33,6 +33,12 @@ export type PluginUiMetadata = {
>;
};
export type ChannelUiMetadata = {
id: string;
label?: string;
description?: string;
};
const GROUP_LABELS: Record<string, string> = {
wizard: "Wizard",
logging: "Logging",
@@ -413,6 +419,24 @@ function applyPluginHints(hints: ConfigUiHints, plugins: PluginUiMetadata[]): Co
return next;
}
function applyChannelHints(hints: ConfigUiHints, channels: ChannelUiMetadata[]): ConfigUiHints {
const next: ConfigUiHints = { ...hints };
for (const channel of channels) {
const id = channel.id.trim();
if (!id) continue;
const basePath = `channels.${id}`;
const current = next[basePath] ?? {};
const label = channel.label?.trim();
const help = channel.description?.trim();
next[basePath] = {
...current,
...(label ? { label } : {}),
...(help ? { help } : {}),
};
}
return next;
}
let cachedBase: ConfigSchemaResponse | null = null;
function buildBaseConfigSchema(): ConfigSchemaResponse {
@@ -433,11 +457,17 @@ function buildBaseConfigSchema(): ConfigSchemaResponse {
return next;
}
export function buildConfigSchema(params?: { plugins?: PluginUiMetadata[] }): ConfigSchemaResponse {
export function buildConfigSchema(params?: {
plugins?: PluginUiMetadata[];
channels?: ChannelUiMetadata[];
}): ConfigSchemaResponse {
const base = buildBaseConfigSchema();
const plugins = params?.plugins ?? [];
if (plugins.length === 0) return base;
const merged = applySensitiveHints(applyPluginHints(base.uiHints, plugins));
const channels = params?.channels ?? [];
if (plugins.length === 0 && channels.length === 0) return base;
const merged = applySensitiveHints(
applyChannelHints(applyPluginHints(base.uiHints, plugins), channels),
);
return {
...base,
uiHints: merged,

View File

@@ -1,8 +1,8 @@
import type { MsgContext } from "../../auto-reply/templating.js";
import { CHANNEL_IDS } from "../../channels/registry.js";
import { listDeliverableMessageChannels } from "../../utils/message-channel.js";
import type { GroupKeyResolution } from "./types.js";
const GROUP_SURFACES = new Set<string>([...CHANNEL_IDS, "webchat"]);
const getGroupSurfaces = () => new Set<string>([...listDeliverableMessageChannels(), "webchat"]);
function normalizeGroupLabel(raw?: string) {
const trimmed = raw?.trim().toLowerCase() ?? "";
@@ -76,7 +76,7 @@ export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | nu
};
const parseParts = (parts: string[]) => {
if (parts.length >= 2 && GROUP_SURFACES.has(parts[0])) {
if (parts.length >= 2 && getGroupSurfaces().has(parts[0])) {
provider = parts[0];
if (parts.length >= 3) {
const kindCandidate = parts[1];

View File

@@ -14,4 +14,5 @@ export type ChannelsConfig = {
signal?: SignalConfig;
imessage?: IMessageConfig;
msteams?: MSTeamsConfig;
[key: string]: unknown;
};

View File

@@ -23,4 +23,5 @@ export const ChannelsSchema = z
imessage: IMessageConfigSchema.optional(),
msteams: MSTeamsConfigSchema.optional(),
})
.catchall(z.unknown())
.optional();