mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
refactor: hide qa channels with exposure metadata
This commit is contained in:
@@ -1454,7 +1454,10 @@ Useful `openclaw.channel` fields beyond the minimal example:
|
||||
- `preferOver`: lower-priority plugin/channel ids this catalog entry should outrank
|
||||
- `selectionDocsPrefix`, `selectionDocsOmitLabel`, `selectionExtras`: selection-surface copy controls
|
||||
- `markdownCapable`: marks the channel as markdown-capable for outbound formatting decisions
|
||||
- `showConfigured`: hide the channel from configured-channel listing surfaces when set to `false`
|
||||
- `exposure.configured`: hide the channel from configured-channel listing surfaces when set to `false`
|
||||
- `exposure.setup`: hide the channel from interactive setup/configure pickers when set to `false`
|
||||
- `exposure.docs`: mark the channel as internal/private for docs navigation surfaces
|
||||
- `showConfigured` / `showInSetup`: legacy aliases still accepted for compatibility; prefer `exposure`
|
||||
- `quickstartAllowFrom`: opt the channel into the standard quickstart `allowFrom` flow
|
||||
- `forceAccountBinding`: require explicit account binding even when only one account exists
|
||||
- `preferSessionLookupForAnnounceTarget`: prefer session lookup when resolving announce targets
|
||||
|
||||
@@ -101,7 +101,7 @@ surfaces before runtime loads.
|
||||
| `selectionDocsOmitLabel` | `boolean` | Show the docs path directly instead of a labeled docs link in selection copy. |
|
||||
| `selectionExtras` | `string[]` | Extra short strings appended in selection copy. |
|
||||
| `markdownCapable` | `boolean` | Marks the channel as markdown-capable for outbound formatting decisions. |
|
||||
| `showConfigured` | `boolean` | Controls whether configured-channel listing surfaces show this channel. |
|
||||
| `exposure` | `object` | Channel visibility controls for setup, configured lists, and docs surfaces. |
|
||||
| `quickstartAllowFrom` | `boolean` | Opt this channel into the standard quickstart `allowFrom` setup flow. |
|
||||
| `forceAccountBinding` | `boolean` | Require explicit account binding even when only one account exists. |
|
||||
| `preferSessionLookupForAnnounceTarget` | `boolean` | Prefer session lookup when resolving announce targets for this channel. |
|
||||
@@ -125,12 +125,26 @@ Example:
|
||||
"selectionDocsPrefix": "Guide:",
|
||||
"selectionExtras": ["Markdown"],
|
||||
"markdownCapable": true,
|
||||
"exposure": {
|
||||
"configured": true,
|
||||
"setup": true,
|
||||
"docs": true
|
||||
},
|
||||
"quickstartAllowFrom": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`exposure` supports:
|
||||
|
||||
- `configured`: include the channel in configured/status-style listing surfaces
|
||||
- `setup`: include the channel in interactive setup/configure pickers
|
||||
- `docs`: mark the channel as public-facing in docs/navigation surfaces
|
||||
|
||||
`showConfigured` and `showInSetup` remain supported as legacy aliases. Prefer
|
||||
`exposure`.
|
||||
|
||||
### `openclaw.install`
|
||||
|
||||
`openclaw.install` is package metadata, not manifest metadata.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { listChannelCatalogEntries } from "../plugins/channel-catalog-registry.js";
|
||||
import type { PluginPackageChannel } from "../plugins/manifest.js";
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "./ids.js";
|
||||
import { resolveChannelExposure } from "./plugins/exposure.js";
|
||||
import type { ChannelMeta } from "./plugins/types.js";
|
||||
|
||||
export type ChatChannelMeta = ChannelMeta;
|
||||
@@ -15,6 +16,7 @@ function toChatChannelMeta(params: {
|
||||
if (!label) {
|
||||
throw new Error(`Missing label for bundled chat channel "${params.id}"`);
|
||||
}
|
||||
const exposure = resolveChannelExposure(params.channel);
|
||||
|
||||
return {
|
||||
id: params.id,
|
||||
@@ -43,9 +45,7 @@ function toChatChannelMeta(params: {
|
||||
...(params.channel.markdownCapable !== undefined
|
||||
? { markdownCapable: params.channel.markdownCapable }
|
||||
: {}),
|
||||
...(params.channel.showConfigured !== undefined
|
||||
? { showConfigured: params.channel.showConfigured }
|
||||
: {}),
|
||||
exposure,
|
||||
...(params.channel.quickstartAllowFrom !== undefined
|
||||
? { quickstartAllowFrom: params.channel.quickstartAllowFrom }
|
||||
: {}),
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { OpenClawPackageManifest } from "../../plugins/manifest.js";
|
||||
import type { PluginPackageChannel, PluginPackageInstall } from "../../plugins/manifest.js";
|
||||
import type { PluginOrigin } from "../../plugins/types.js";
|
||||
import { isRecord, resolveConfigDir, resolveUserPath } from "../../utils.js";
|
||||
import { resolveChannelExposure } from "./exposure.js";
|
||||
import type { ChannelMeta } from "./types.js";
|
||||
|
||||
export type ChannelUiMetaEntry = {
|
||||
@@ -180,6 +181,7 @@ function toChannelMeta(params: {
|
||||
const docsPath = params.channel.docsPath?.trim() || `/channels/${params.id}`;
|
||||
const blurb = params.channel.blurb?.trim() || "";
|
||||
const systemImage = params.channel.systemImage?.trim();
|
||||
const exposure = resolveChannelExposure(params.channel);
|
||||
|
||||
return {
|
||||
id: params.id,
|
||||
@@ -203,9 +205,7 @@ function toChannelMeta(params: {
|
||||
...(params.channel.markdownCapable !== undefined
|
||||
? { markdownCapable: params.channel.markdownCapable }
|
||||
: {}),
|
||||
...(params.channel.showConfigured !== undefined
|
||||
? { showConfigured: params.channel.showConfigured }
|
||||
: {}),
|
||||
exposure,
|
||||
...(params.channel.quickstartAllowFrom !== undefined
|
||||
? { quickstartAllowFrom: params.channel.quickstartAllowFrom }
|
||||
: {}),
|
||||
|
||||
29
src/channels/plugins/exposure.ts
Normal file
29
src/channels/plugins/exposure.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { ChannelMeta } from "./types.js";
|
||||
|
||||
export function resolveChannelExposure(
|
||||
meta: Pick<ChannelMeta, "exposure" | "showConfigured" | "showInSetup">,
|
||||
) {
|
||||
return {
|
||||
configured: meta.exposure?.configured ?? meta.showConfigured ?? true,
|
||||
setup: meta.exposure?.setup ?? meta.showInSetup ?? true,
|
||||
docs: meta.exposure?.docs ?? true,
|
||||
};
|
||||
}
|
||||
|
||||
export function isChannelVisibleInConfiguredLists(
|
||||
meta: Pick<ChannelMeta, "exposure" | "showConfigured" | "showInSetup">,
|
||||
): boolean {
|
||||
return resolveChannelExposure(meta).configured;
|
||||
}
|
||||
|
||||
export function isChannelVisibleInSetup(
|
||||
meta: Pick<ChannelMeta, "exposure" | "showConfigured" | "showInSetup">,
|
||||
): boolean {
|
||||
return resolveChannelExposure(meta).setup;
|
||||
}
|
||||
|
||||
export function isChannelVisibleInDocs(
|
||||
meta: Pick<ChannelMeta, "exposure" | "showConfigured" | "showInSetup">,
|
||||
): boolean {
|
||||
return resolveChannelExposure(meta).docs;
|
||||
}
|
||||
@@ -14,6 +14,12 @@ import type { ChannelMessageCapability } from "./message-capabilities.js";
|
||||
|
||||
export type ChannelId = ChatChannelId | (string & {});
|
||||
|
||||
export type ChannelExposure = {
|
||||
configured?: boolean;
|
||||
setup?: boolean;
|
||||
docs?: boolean;
|
||||
};
|
||||
|
||||
export type ChannelOutboundTargetMode = "explicit" | "implicit" | "heartbeat";
|
||||
|
||||
/** Agent tool registered by a channel plugin. */
|
||||
@@ -147,7 +153,9 @@ export type ChannelMeta = {
|
||||
detailLabel?: string;
|
||||
systemImage?: string;
|
||||
markdownCapable?: boolean;
|
||||
exposure?: ChannelExposure;
|
||||
showConfigured?: boolean;
|
||||
showInSetup?: boolean;
|
||||
quickstartAllowFrom?: boolean;
|
||||
forceAccountBinding?: boolean;
|
||||
preferSessionLookupForAnnounceTarget?: boolean;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isChannelVisibleInConfiguredLists } from "../channels/plugins/exposure.js";
|
||||
import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
|
||||
import {
|
||||
getChannelPlugin,
|
||||
@@ -104,7 +105,7 @@ function shouldShowProviderEntry(entry: ProviderAccountStatus, cfg: OpenClawConf
|
||||
if (!plugin) {
|
||||
return Boolean(entry.configured);
|
||||
}
|
||||
if (plugin.meta.showConfigured === false) {
|
||||
if (!isChannelVisibleInConfiguredLists(plugin.meta)) {
|
||||
const providerConfig = (cfg as Record<string, unknown>)[plugin.id];
|
||||
return Boolean(entry.configured) || Boolean(providerConfig);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { PluginAutoEnableResult } from "../../config/plugin-auto-enable.js";
|
||||
|
||||
const loadPluginManifestRegistry = vi.hoisted(() => vi.fn());
|
||||
const listChannelPluginCatalogEntries = vi.hoisted(() => vi.fn((): unknown[] => []));
|
||||
const listChatChannels = vi.hoisted(() => vi.fn((): Array<Record<string, string>> => []));
|
||||
const applyPluginAutoEnable = vi.hoisted(() =>
|
||||
vi.fn<(args: { config: unknown; env?: NodeJS.ProcessEnv }) => PluginAutoEnableResult>(
|
||||
({ config }) => ({
|
||||
@@ -21,11 +23,24 @@ vi.mock("../../config/plugin-auto-enable.js", () => ({
|
||||
applyPluginAutoEnable(args as { config: unknown; env?: NodeJS.ProcessEnv }),
|
||||
}));
|
||||
|
||||
import { listManifestInstalledChannelIds } from "./discovery.js";
|
||||
vi.mock("../../channels/plugins/catalog.js", () => ({
|
||||
listChannelPluginCatalogEntries: (_args?: unknown) => listChannelPluginCatalogEntries(),
|
||||
}));
|
||||
|
||||
vi.mock("../../channels/registry.js", () => ({
|
||||
listChatChannels: () => listChatChannels(),
|
||||
}));
|
||||
|
||||
import { listManifestInstalledChannelIds, resolveChannelSetupEntries } from "./discovery.js";
|
||||
|
||||
describe("listManifestInstalledChannelIds", () => {
|
||||
beforeEach(() => {
|
||||
loadPluginManifestRegistry.mockReset();
|
||||
loadPluginManifestRegistry.mockReset().mockReturnValue({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
listChannelPluginCatalogEntries.mockReset().mockReturnValue([]);
|
||||
listChatChannels.mockReset().mockReturnValue([]);
|
||||
applyPluginAutoEnable.mockReset().mockImplementation(({ config }) => ({
|
||||
config: config as never,
|
||||
changes: [] as string[],
|
||||
@@ -68,4 +83,37 @@ describe("listManifestInstalledChannelIds", () => {
|
||||
});
|
||||
expect(installedIds).toEqual(new Set(["slack"]));
|
||||
});
|
||||
|
||||
it("filters channels hidden from setup out of interactive entries", () => {
|
||||
listChatChannels.mockReturnValue([
|
||||
{
|
||||
id: "telegram",
|
||||
label: "Telegram",
|
||||
selectionLabel: "Telegram",
|
||||
docsPath: "/channels/telegram",
|
||||
blurb: "bot token",
|
||||
},
|
||||
]);
|
||||
|
||||
const resolved = resolveChannelSetupEntries({
|
||||
cfg: {} as never,
|
||||
installedPlugins: [
|
||||
{
|
||||
id: "qa-channel",
|
||||
meta: {
|
||||
id: "qa-channel",
|
||||
label: "QA Channel",
|
||||
selectionLabel: "QA Channel",
|
||||
docsPath: "/channels/qa-channel",
|
||||
blurb: "synthetic",
|
||||
exposure: { setup: false },
|
||||
},
|
||||
} as never,
|
||||
],
|
||||
workspaceDir: "/tmp/workspace",
|
||||
env: { OPENCLAW_HOME: "/tmp/home" } as NodeJS.ProcessEnv,
|
||||
});
|
||||
|
||||
expect(resolved.entries.map((entry) => entry.id)).toEqual(["telegram"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
listChannelPluginCatalogEntries,
|
||||
type ChannelPluginCatalogEntry,
|
||||
} from "../../channels/plugins/catalog.js";
|
||||
import { isChannelVisibleInSetup } from "../../channels/plugins/exposure.js";
|
||||
import type { ChannelMeta, ChannelPlugin } from "../../channels/plugins/types.js";
|
||||
import { listChatChannels } from "../../channels/registry.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
@@ -15,6 +16,12 @@ type ChannelCatalogEntry = {
|
||||
meta: ChannelMeta;
|
||||
};
|
||||
|
||||
export function shouldShowChannelInSetup(
|
||||
meta: Pick<ChannelMeta, "exposure" | "showConfigured" | "showInSetup">,
|
||||
): boolean {
|
||||
return isChannelVisibleInSetup(meta);
|
||||
}
|
||||
|
||||
export type ResolvedChannelSetupEntries = {
|
||||
entries: ChannelCatalogEntry[];
|
||||
installedCatalogEntries: ChannelPluginCatalogEntry[];
|
||||
@@ -71,11 +78,15 @@ export function resolveChannelSetupEntries(params: {
|
||||
const catalogEntries = listChannelPluginCatalogEntries({ workspaceDir });
|
||||
const installedCatalogEntries = catalogEntries.filter(
|
||||
(entry) =>
|
||||
!installedPluginIds.has(entry.id) && manifestInstalledIds.has(entry.id as ChannelChoice),
|
||||
!installedPluginIds.has(entry.id) &&
|
||||
manifestInstalledIds.has(entry.id as ChannelChoice) &&
|
||||
shouldShowChannelInSetup(entry.meta),
|
||||
);
|
||||
const installableCatalogEntries = catalogEntries.filter(
|
||||
(entry) =>
|
||||
!installedPluginIds.has(entry.id) && !manifestInstalledIds.has(entry.id as ChannelChoice),
|
||||
!installedPluginIds.has(entry.id) &&
|
||||
!manifestInstalledIds.has(entry.id as ChannelChoice) &&
|
||||
shouldShowChannelInSetup(entry.meta),
|
||||
);
|
||||
|
||||
const metaById = new Map<string, ChannelMeta>();
|
||||
@@ -100,7 +111,7 @@ export function resolveChannelSetupEntries(params: {
|
||||
entries: Array.from(metaById, ([id, meta]) => ({
|
||||
id: id as ChannelChoice,
|
||||
meta,
|
||||
})),
|
||||
})).filter((entry) => shouldShowChannelInSetup(entry.meta)),
|
||||
installedCatalogEntries,
|
||||
installableCatalogEntries,
|
||||
installedCatalogById: new Map(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { loadAuthProfileStore } from "../../agents/auth-profiles.js";
|
||||
import { isChannelVisibleInConfiguredLists } from "../../channels/plugins/exposure.js";
|
||||
import { listChannelPlugins } from "../../channels/plugins/index.js";
|
||||
import { buildChannelAccountSnapshot } from "../../channels/plugins/status.js";
|
||||
import type { ChannelAccountSnapshot, ChannelPlugin } from "../../channels/plugins/types.js";
|
||||
@@ -47,7 +48,7 @@ function formatLinked(value: boolean): string {
|
||||
}
|
||||
|
||||
function shouldShowConfigured(channel: ChannelPlugin): boolean {
|
||||
return channel.meta.showConfigured !== false;
|
||||
return isChannelVisibleInConfiguredLists(channel.meta);
|
||||
}
|
||||
|
||||
function formatAccountLine(params: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { CONFIG_PATH } from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { note } from "../terminal/note.js";
|
||||
import { shortenHomePath } from "../utils.js";
|
||||
import { shouldShowChannelInSetup } from "./channel-setup/discovery.js";
|
||||
import { confirm, select } from "./configure.shared.js";
|
||||
import { guardCancel } from "./onboard-helpers.js";
|
||||
|
||||
@@ -17,6 +18,7 @@ export async function removeChannelConfigWizard(
|
||||
const listConfiguredChannels = () =>
|
||||
listChannelPlugins()
|
||||
.map((plugin) => plugin.meta)
|
||||
.filter((meta) => shouldShowChannelInSetup(meta))
|
||||
.filter((meta) => next.channels?.[meta.id] !== undefined);
|
||||
|
||||
while (true) {
|
||||
|
||||
@@ -818,6 +818,51 @@ describe("setupChannels", () => {
|
||||
expect(multiselect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("hides channels marked hidden from setup in the picker", async () => {
|
||||
const qaChannelBase = createChannelTestPluginBase({
|
||||
id: "qa-channel",
|
||||
label: "QA Channel",
|
||||
docsPath: "/channels/qa-channel",
|
||||
});
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
pluginId: "qa-channel",
|
||||
source: "test",
|
||||
plugin: {
|
||||
...qaChannelBase,
|
||||
meta: {
|
||||
...qaChannelBase.meta,
|
||||
showInSetup: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
const select = vi.fn(async ({ message, options }: { message: string; options: unknown[] }) => {
|
||||
if (message === "Select a channel") {
|
||||
expect(
|
||||
(options as Array<{ label?: string }>).some((option) =>
|
||||
option.label?.includes("QA Channel"),
|
||||
),
|
||||
).toBe(false);
|
||||
}
|
||||
return "__done__";
|
||||
});
|
||||
const { multiselect, text } = createUnexpectedPromptGuards();
|
||||
const prompter = createPrompter({
|
||||
select: select as unknown as WizardPrompter["select"],
|
||||
multiselect,
|
||||
text,
|
||||
});
|
||||
|
||||
await runSetupChannels({} as OpenClawConfig, prompter);
|
||||
|
||||
expect(select).toHaveBeenCalledWith(expect.objectContaining({ message: "Select a channel" }));
|
||||
expect(multiselect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("treats installed external plugin channels as installed without reinstall prompts", async () => {
|
||||
setActivePluginRegistry(createEmptyPluginRegistry());
|
||||
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([createMSTeamsCatalogEntry()]);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "../channels/registry.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveChannelSetupEntries } from "../commands/channel-setup/discovery.js";
|
||||
import { shouldShowChannelInSetup } from "../commands/channel-setup/discovery.js";
|
||||
import { resolveChannelSetupWizardAdapterForPlugin } from "../commands/channel-setup/registry.js";
|
||||
import type {
|
||||
ChannelSetupWizardAdapter,
|
||||
@@ -79,6 +80,9 @@ export async function collectChannelStatus(params: {
|
||||
));
|
||||
const statusEntries = await Promise.all(
|
||||
installedPlugins.flatMap((plugin) => {
|
||||
if (!shouldShowChannelInSetup(plugin.meta)) {
|
||||
return [];
|
||||
}
|
||||
const adapter = resolveAdapter(plugin.id);
|
||||
if (!adapter) {
|
||||
return [];
|
||||
@@ -92,6 +96,7 @@ export async function collectChannelStatus(params: {
|
||||
);
|
||||
const statusByChannel = new Map(statusEntries.map((entry) => [entry.channel, entry]));
|
||||
const fallbackStatuses = listChatChannels()
|
||||
.filter((meta) => shouldShowChannelInSetup(meta))
|
||||
.filter((meta) => !statusByChannel.has(meta.id))
|
||||
.map((meta) => {
|
||||
const configured = isChannelConfigured(params.cfg, meta.id);
|
||||
@@ -235,22 +240,31 @@ export function resolveChannelSelectionNoteLines(params: {
|
||||
export function resolveChannelSetupSelectionContributions(params: {
|
||||
entries: Array<{
|
||||
id: ChannelChoice;
|
||||
meta: { id: string; label: string; selectionLabel?: string };
|
||||
meta: {
|
||||
id: string;
|
||||
label: string;
|
||||
selectionLabel?: string;
|
||||
exposure?: { setup?: boolean };
|
||||
showConfigured?: boolean;
|
||||
showInSetup?: boolean;
|
||||
};
|
||||
}>;
|
||||
statusByChannel: Map<ChannelChoice, { selectionHint?: string }>;
|
||||
resolveDisabledHint: (channel: ChannelChoice) => string | undefined;
|
||||
}): ChannelSetupSelectionContribution[] {
|
||||
return params.entries.map((entry) => {
|
||||
const disabledHint = params.resolveDisabledHint(entry.id);
|
||||
const hint =
|
||||
[params.statusByChannel.get(entry.id)?.selectionHint, disabledHint]
|
||||
.filter(Boolean)
|
||||
.join(" · ") || undefined;
|
||||
return buildChannelSetupSelectionContribution({
|
||||
channel: entry.id,
|
||||
label: entry.meta.selectionLabel ?? entry.meta.label,
|
||||
hint,
|
||||
source: listChatChannels().some((channel) => channel.id === entry.id) ? "core" : "plugin",
|
||||
return params.entries
|
||||
.filter((entry) => shouldShowChannelInSetup(entry.meta))
|
||||
.map((entry) => {
|
||||
const disabledHint = params.resolveDisabledHint(entry.id);
|
||||
const hint =
|
||||
[params.statusByChannel.get(entry.id)?.selectionHint, disabledHint]
|
||||
.filter(Boolean)
|
||||
.join(" · ") || undefined;
|
||||
return buildChannelSetupSelectionContribution({
|
||||
channel: entry.id,
|
||||
label: entry.meta.selectionLabel ?? entry.meta.label,
|
||||
hint,
|
||||
source: listChatChannels().some((channel) => channel.id === entry.id) ? "core" : "plugin",
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ import {
|
||||
import type { ChannelSetupPlugin } from "../channels/plugins/setup-wizard-types.js";
|
||||
import { listChatChannels } from "../channels/registry.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveChannelSetupEntries } from "../commands/channel-setup/discovery.js";
|
||||
import {
|
||||
resolveChannelSetupEntries,
|
||||
shouldShowChannelInSetup,
|
||||
} from "../commands/channel-setup/discovery.js";
|
||||
import {
|
||||
ensureChannelSetupPluginInstalled,
|
||||
loadChannelSetupPluginRegistrySnapshotForChannel,
|
||||
@@ -98,10 +101,14 @@ export async function setupChannels(
|
||||
const listVisibleInstalledPlugins = (): ChannelSetupPlugin[] => {
|
||||
const merged = new Map<string, ChannelSetupPlugin>();
|
||||
for (const plugin of listChannelSetupPlugins()) {
|
||||
merged.set(plugin.id, plugin);
|
||||
if (shouldShowChannelInSetup(plugin.meta)) {
|
||||
merged.set(plugin.id, plugin);
|
||||
}
|
||||
}
|
||||
for (const plugin of scopedPluginsById.values()) {
|
||||
merged.set(plugin.id, plugin);
|
||||
if (shouldShowChannelInSetup(plugin.meta)) {
|
||||
merged.set(plugin.id, plugin);
|
||||
}
|
||||
}
|
||||
return Array.from(merged.values());
|
||||
};
|
||||
@@ -181,11 +188,13 @@ export async function setupChannels(
|
||||
return cfg;
|
||||
}
|
||||
|
||||
const corePrimer = listChatChannels().map((meta) => ({
|
||||
id: meta.id,
|
||||
label: meta.label,
|
||||
blurb: meta.blurb,
|
||||
}));
|
||||
const corePrimer = listChatChannels()
|
||||
.filter((meta) => shouldShowChannelInSetup(meta))
|
||||
.map((meta) => ({
|
||||
id: meta.id,
|
||||
label: meta.label,
|
||||
blurb: meta.blurb,
|
||||
}));
|
||||
const coreIds = new Set(corePrimer.map((entry) => entry.id));
|
||||
const primerChannels = [
|
||||
...corePrimer,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "../channels/ids.js";
|
||||
import { emptyChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
import { resolveChannelExposure } from "../channels/plugins/exposure.js";
|
||||
import { buildAccountScopedDmSecurityPolicy } from "../channels/plugins/helpers.js";
|
||||
import {
|
||||
createScopedAccountReplyToModeResolver,
|
||||
@@ -231,6 +232,7 @@ function toSdkChatChannelMeta(params: {
|
||||
if (!label) {
|
||||
throw new Error(`Missing label for bundled chat channel "${params.id}"`);
|
||||
}
|
||||
const exposure = resolveChannelExposure(params.channel);
|
||||
return {
|
||||
id: params.id,
|
||||
label,
|
||||
@@ -258,9 +260,7 @@ function toSdkChatChannelMeta(params: {
|
||||
...(params.channel.markdownCapable !== undefined
|
||||
? { markdownCapable: params.channel.markdownCapable }
|
||||
: {}),
|
||||
...(params.channel.showConfigured !== undefined
|
||||
? { showConfigured: params.channel.showConfigured }
|
||||
: {}),
|
||||
exposure,
|
||||
...(params.channel.quickstartAllowFrom !== undefined
|
||||
? { quickstartAllowFrom: params.channel.quickstartAllowFrom }
|
||||
: {}),
|
||||
|
||||
@@ -428,7 +428,13 @@ export type PluginPackageChannel = {
|
||||
selectionDocsOmitLabel?: boolean;
|
||||
selectionExtras?: readonly string[];
|
||||
markdownCapable?: boolean;
|
||||
exposure?: {
|
||||
configured?: boolean;
|
||||
setup?: boolean;
|
||||
docs?: boolean;
|
||||
};
|
||||
showConfigured?: boolean;
|
||||
showInSetup?: boolean;
|
||||
quickstartAllowFrom?: boolean;
|
||||
forceAccountBinding?: boolean;
|
||||
preferSessionLookupForAnnounceTarget?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user