Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/channels/base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ export type {
BlockStreamingChunkConfig,
BlockStreamingCoalesceConfig,
ChannelConfig,
ChannelConfigFieldDescriptor,
ChannelConfigFieldKind,
ChannelIdentityConfig,
ChannelManagementDescriptor,
ChannelMemoryIntentClassifier,
ChannelMemoryIntentClassifierResult,
ChannelMemoryScopeConfig,
Expand Down
24 changes: 24 additions & 0 deletions packages/channels/base/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,27 @@ export interface ChannelMemoryIntentClassifier {
): Promise<ChannelMemoryIntentClassifierResult>;
}

export type ChannelConfigFieldKind =
| 'string'
| 'secret'
| 'boolean'
| 'number'
| 'enum';

export interface ChannelConfigFieldDescriptor {
key: string;
label: string;
kind: ChannelConfigFieldKind;
required?: boolean;
envResolvable?: boolean;
options?: ReadonlyArray<{ value: string; label: string }>;
description?: string;
}

export interface ChannelManagementDescriptor {
fields: readonly ChannelConfigFieldDescriptor[];
}

/**
* A channel plugin registers a channel type and provides a factory
* to create adapter instances. Both built-in adapters and external
Expand All @@ -322,6 +343,9 @@ export interface ChannelPlugin {
/** Optional config fields whose string values may reference environment vars. */
envResolvableConfigFields?: string[];

/** Serializable metadata for safe configuration management. */
management?: ChannelManagementDescriptor;

/** Create a channel adapter instance. */
createChannel(
name: string,
Expand Down
18 changes: 18 additions & 0 deletions packages/channels/dingtalk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ export const plugin: ChannelPlugin = {
channelType: 'dingtalk',
displayName: 'DingTalk',
requiredConfigFields: ['clientId', 'clientSecret'],
management: {
fields: [
{
key: 'clientId',
label: 'Client ID',
kind: 'string',
required: true,
envResolvable: true,
},
{
key: 'clientSecret',
label: 'Client Secret',
kind: 'secret',
required: true,
envResolvable: true,
},
],
},
createChannel: (name, config, bridge, options) =>
new DingtalkChannel(name, config, bridge, options),
};
18 changes: 18 additions & 0 deletions packages/channels/feishu/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ export const plugin: ChannelPlugin = {
channelType: 'feishu',
displayName: 'Feishu',
requiredConfigFields: ['clientId', 'clientSecret'],
management: {
fields: [
{
key: 'clientId',
label: 'App ID',
kind: 'string',
required: true,
envResolvable: true,
},
{
key: 'clientSecret',
label: 'App Secret',
kind: 'secret',
required: true,
envResolvable: true,
},
],
},
createChannel: (name, config, bridge, options) =>
new FeishuChannel(name, config, bridge, options),
};
24 changes: 24 additions & 0 deletions packages/channels/wecom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@ export const plugin: ChannelPlugin = {
displayName: 'WeCom',
requiredConfigFields: ['botId', 'secret'],
envResolvableConfigFields: ['wsUrl'],
management: {
fields: [
{
key: 'botId',
label: 'Bot ID',
kind: 'string',
required: true,
envResolvable: true,
},
{
key: 'secret',
label: 'Bot Secret',
kind: 'secret',
required: true,
envResolvable: true,
},
{
key: 'wsUrl',
label: 'WebSocket URL',
kind: 'string',
envResolvable: true,
},
],
},
createChannel: (name, config, bridge, options) =>
new WeComChannel(name, config, bridge, options),
};
29 changes: 29 additions & 0 deletions packages/cli/src/commands/channel/channel-registry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, expect, it } from 'vitest';
import { supportedChannelCatalog } from './channel-registry.js';

describe('channel registry', () => {
it('only marks the manually configurable built-in types as manageable', async () => {
const catalog = await supportedChannelCatalog();
expect(catalog.map((entry) => entry.type)).toEqual([
'telegram',
'weixin',
'dingtalk',
'wecom',
'feishu',
'qq',
]);
expect(
catalog.filter((entry) => entry.manageable).map((entry) => entry.type),
).toEqual(['dingtalk', 'wecom', 'feishu']);
expect(
catalog.find((entry) => entry.type === 'dingtalk')?.fields,
).toContainEqual(
expect.objectContaining({
key: 'clientSecret',
kind: 'secret',
required: true,
}),
);
expect(JSON.stringify(catalog)).not.toContain('createChannel');
});
});
26 changes: 25 additions & 1 deletion packages/cli/src/commands/channel/channel-registry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import type { ChannelPlugin } from '@qwen-code/channel-base';
import type {
ChannelConfigFieldDescriptor,
ChannelPlugin,
} from '@qwen-code/channel-base';

export interface ChannelTypeDescriptor {
type: string;
displayName: string;
manageable: boolean;
fields: readonly ChannelConfigFieldDescriptor[];
}

const registry = new Map<string, ChannelPlugin>();
let builtinsPromise: Promise<void> | null = null;
Expand Down Expand Up @@ -52,3 +62,17 @@ export async function supportedTypes(): Promise<string[]> {
await ensureBuiltins();
return [...registry.keys()];
}

export async function supportedChannelCatalog(): Promise<
ChannelTypeDescriptor[]
> {
await ensureBuiltins();
return [...registry.values()].map(
({ channelType, displayName, management }) => ({
type: channelType,
displayName,
manageable: management !== undefined,
fields: management?.fields ?? [],
}),
);
}
11 changes: 11 additions & 0 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,17 @@ const SETTINGS_SCHEMA = {
mergeStrategy: MergeStrategy.SHALLOW_MERGE,
},

serve: {
type: 'object',
label: 'Serve',
category: 'Advanced',
requiresRestart: true,
default: {} as { channels?: string[] },
description: 'Persistent qwen serve settings.',
showInDialog: false,
mergeStrategy: MergeStrategy.SHALLOW_MERGE,
},

// Model providers configuration grouped by authType
modelProviders: {
type: 'object',
Expand Down
Loading
Loading