From ccc1920068dea2d342c3a25b4ce207367ff5bf9a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 13 Aug 2026 01:55:47 -0700 Subject: [PATCH] improve(telegram): cut cold channel setup import latency (#122955) * perf(telegram): keep setup entry on light graph * fix(plugin-sdk): complete private UI hint boundaries --- extensions/telegram/src/account-inspect.ts | 4 +- extensions/telegram/src/channel.setup.ts | 4 +- extensions/telegram/src/channel.ts | 14 +- extensions/telegram/src/config-adapter.ts | 179 +++++++++++++ extensions/telegram/src/config-ui-hints.ts | 2 +- extensions/telegram/src/setup-plugin.ts | 57 ++++ extensions/telegram/src/shared.test.ts | 3 +- extensions/telegram/src/shared.ts | 244 +----------------- .../tsconfig.package-boundary.paths.json | 3 + extensions/xai/tsconfig.json | 3 + package.json | 4 + scripts/lib/plugin-sdk-entrypoints.json | 1 + ...lugin-sdk-private-local-only-subpaths.json | 1 + 13 files changed, 269 insertions(+), 250 deletions(-) create mode 100644 extensions/telegram/src/config-adapter.ts create mode 100644 extensions/telegram/src/setup-plugin.ts diff --git a/extensions/telegram/src/account-inspect.ts b/extensions/telegram/src/account-inspect.ts index 10f677beaed9..f3778796112d 100644 --- a/extensions/telegram/src/account-inspect.ts +++ b/extensions/telegram/src/account-inspect.ts @@ -2,14 +2,14 @@ import { resolveAccountWithDefaultFallback } from "openclaw/plugin-sdk/account-core"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import type { TelegramAccountConfig } from "openclaw/plugin-sdk/config-contracts"; -import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-auth"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/routing"; import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime"; import { + coerceSecretRef, hasConfiguredSecretInput, normalizeSecretInputString, } from "openclaw/plugin-sdk/secret-input"; -import { coerceSecretRef } from "openclaw/plugin-sdk/secret-input-runtime"; +import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/secret-provider-alias"; import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime"; import { mergeTelegramAccountConfig, diff --git a/extensions/telegram/src/channel.setup.ts b/extensions/telegram/src/channel.setup.ts index 150909bc02df..df5760f75ecf 100644 --- a/extensions/telegram/src/channel.setup.ts +++ b/extensions/telegram/src/channel.setup.ts @@ -3,11 +3,11 @@ import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core"; import type { ResolvedTelegramAccount } from "./accounts.js"; import type { TelegramProbe } from "./probe.js"; import { telegramSetupContract } from "./setup-core.js"; +import { createTelegramSetupPluginBase } from "./setup-plugin.js"; import { telegramSetupWizard } from "./setup-surface.js"; -import { createTelegramPluginBase } from "./shared.js"; export const telegramSetupPlugin: ChannelPlugin = { - ...createTelegramPluginBase({ + ...createTelegramSetupPluginBase({ setupWizard: telegramSetupWizard, setupContract: telegramSetupContract, }), diff --git a/extensions/telegram/src/channel.ts b/extensions/telegram/src/channel.ts index cb2014435800..62a238972d65 100644 --- a/extensions/telegram/src/channel.ts +++ b/extensions/telegram/src/channel.ts @@ -56,6 +56,12 @@ import { import type { TelegramBotInfo } from "./bot-info.js"; import { buildTelegramGroupPeerId } from "./bot/helpers.js"; import { telegramMessageActions as telegramMessageActionsImpl } from "./channel-actions.js"; +import { + findTelegramTokenOwnerAccountId, + formatDuplicateTelegramTokenReason, + resolveTelegramConfigAccessorAccount, + telegramConfigAdapter, +} from "./config-adapter.js"; import { resolveTelegramConversationBaseSessionKey } from "./conversation-route.js"; import { listTelegramDirectoryGroupsFromConfig, @@ -86,13 +92,7 @@ import { } from "./session-conversation.js"; import { telegramSetupContract } from "./setup-core.js"; import { telegramSetupWizard } from "./setup-surface.js"; -import { - createTelegramPluginBase, - findTelegramTokenOwnerAccountId, - formatDuplicateTelegramTokenReason, - resolveTelegramConfigAccessorAccount, - telegramConfigAdapter, -} from "./shared.js"; +import { createTelegramPluginBase } from "./shared.js"; import { withTelegramStartupProbeSlot } from "./startup-probe-limiter.js"; import { collectTelegramStatusIssues } from "./status-issues.js"; import { parseTelegramTarget } from "./targets.js"; diff --git a/extensions/telegram/src/config-adapter.ts b/extensions/telegram/src/config-adapter.ts new file mode 100644 index 000000000000..82307fd5077a --- /dev/null +++ b/extensions/telegram/src/config-adapter.ts @@ -0,0 +1,179 @@ +// Telegram plugin module implements shared config adapter behavior. +import { resolveNormalizedAccountEntry } from "openclaw/plugin-sdk/account-core"; +import { normalizeAccountId } from "openclaw/plugin-sdk/account-id"; +import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from"; +import { + adaptScopedAccountAccessor, + createScopedChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; +import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core"; +import type { OpenClawConfig, TelegramAccountConfig } from "openclaw/plugin-sdk/config-contracts"; +import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing"; +import { inspectTelegramAccount } from "./account-inspect.js"; +import { + listTelegramAccountIds, + mergeTelegramAccountConfig, + resolveDefaultTelegramAccountId, + resolveTelegramAccount, + type ResolvedTelegramAccount, +} from "./accounts.js"; + +const TELEGRAM_CHANNEL = "telegram" as const; + +type TelegramConfigAccessorAccount = { + config: TelegramAccountConfig; +}; + +export function findTelegramTokenOwnerAccountId(params: { + cfg: OpenClawConfig; + accountId: string; +}): string | null { + const normalizedAccountId = normalizeAccountId(params.accountId); + const tokenOwners = new Map(); + for (const id of listTelegramAccountIds(params.cfg)) { + const account = inspectTelegramAccount({ cfg: params.cfg, accountId: id }); + const token = (account.token ?? "").trim(); + if (!token) { + continue; + } + const ownerAccountId = tokenOwners.get(token); + if (!ownerAccountId) { + tokenOwners.set(token, account.accountId); + continue; + } + if (account.accountId === normalizedAccountId) { + return ownerAccountId; + } + } + return null; +} + +export function formatDuplicateTelegramTokenReason(params: { + accountId: string; + ownerAccountId: string; +}): string { + return ( + `Duplicate Telegram bot token: account "${params.accountId}" shares a token with ` + + `account "${params.ownerAccountId}". Keep one owner account per bot token.` + ); +} + +/** + * Returns true when the runtime token resolver (`resolveTelegramToken`) would + * block channel-level fallthrough for the given accountId. This mirrors the + * guard in `token.ts` so that status-check functions (`isConfigured`, + * `unconfiguredReason`, `describeAccount`) stay consistent with the gateway + * runtime behavior. + * + * The guard fires when: + * 1. The accountId is not the default account, AND + * 2. The config has an explicit `accounts` section with entries, AND + * 3. The accountId is not found in that `accounts` section. + * + * See: https://github.com/openclaw/openclaw/issues/53876 + */ +function isBlockedByMultiBotGuard(cfg: OpenClawConfig, accountId: string): boolean { + if (normalizeAccountId(accountId) === DEFAULT_ACCOUNT_ID) { + return false; + } + const accounts = cfg.channels?.telegram?.accounts; + const hasConfiguredAccounts = + Boolean(accounts) && + typeof accounts === "object" && + !Array.isArray(accounts) && + Object.keys(accounts).length > 0; + if (!hasConfiguredAccounts) { + return false; + } + // Use resolveNormalizedAccountEntry (same as resolveTelegramToken in token.ts) + // so keys such as "Carey Notifications" match "carey-notifications". + return !resolveNormalizedAccountEntry(accounts, accountId, normalizeAccountId); +} + +export function resolveTelegramConfigAccessorAccount(params: { + cfg: OpenClawConfig; + accountId?: string | null; +}): TelegramConfigAccessorAccount { + const accountId = normalizeAccountId( + params.accountId ?? resolveDefaultTelegramAccountId(params.cfg), + ); + return { config: mergeTelegramAccountConfig(params.cfg, accountId) }; +} + +export const telegramConfigAdapter = createScopedChannelConfigAdapter< + ResolvedTelegramAccount, + TelegramConfigAccessorAccount +>({ + sectionKey: TELEGRAM_CHANNEL, + listAccountIds: listTelegramAccountIds, + resolveAccount: adaptScopedAccountAccessor(resolveTelegramAccount), + resolveAccessorAccount: resolveTelegramConfigAccessorAccount, + inspectAccount: adaptScopedAccountAccessor(inspectTelegramAccount), + defaultAccountId: resolveDefaultTelegramAccountId, + clearBaseFields: ["botToken", "tokenFile", "name"], + resolveAllowFrom: (account) => account.config.allowFrom, + formatAllowFrom: (allowFrom) => + formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(telegram|tg):/i }), + resolveDefaultTo: (account) => account.config.defaultTo, +}); + +export function createTelegramPluginConfig(): ChannelPlugin["config"] { + return { + ...telegramConfigAdapter, + hasConfiguredState: ({ env }) => + typeof env?.TELEGRAM_BOT_TOKEN === "string" && env.TELEGRAM_BOT_TOKEN.trim().length > 0, + isConfigured: (account, cfg) => { + // Inspect the complete token resolution, including channel-level fallbacks used by + // binding-created account IDs in a single-bot setup. + if (isBlockedByMultiBotGuard(cfg, account.accountId)) { + return false; + } + const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); + // "configured_unavailable" is configured state, but cannot start the runtime. + if (!inspected.token?.trim()) { + return false; + } + return !findTelegramTokenOwnerAccountId({ cfg, accountId: account.accountId }); + }, + unconfiguredReason: (account, cfg) => { + if (isBlockedByMultiBotGuard(cfg, account.accountId)) { + return `not configured: unknown accountId "${account.accountId}" in multi-bot setup`; + } + const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); + if (!inspected.token?.trim()) { + return inspected.tokenStatus === "configured_unavailable" + ? `not configured: token ${inspected.tokenSource} is configured but unavailable` + : "not configured"; + } + const ownerAccountId = findTelegramTokenOwnerAccountId({ + cfg, + accountId: account.accountId, + }); + return ownerAccountId + ? formatDuplicateTelegramTokenReason({ accountId: account.accountId, ownerAccountId }) + : "not configured"; + }, + describeAccount: (account, cfg) => { + if (isBlockedByMultiBotGuard(cfg, account.accountId)) { + return { + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: false, + tokenSource: "none" as const, + }; + } + const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); + return { + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: + inspected.tokenStatus !== "missing" && + !findTelegramTokenOwnerAccountId({ cfg, accountId: account.accountId }), + tokenSource: inspected.tokenSource, + tokenStatus: inspected.tokenStatus, + }; + }, + }; +} diff --git a/extensions/telegram/src/config-ui-hints.ts b/extensions/telegram/src/config-ui-hints.ts index aa4e372364fe..f89b60c77356 100644 --- a/extensions/telegram/src/config-ui-hints.ts +++ b/extensions/telegram/src/config-ui-hints.ts @@ -1,4 +1,4 @@ -import { createChannelConfigUiHints } from "openclaw/plugin-sdk/channel-core"; +import { createChannelConfigUiHints } from "openclaw/plugin-sdk/channel-config-ui-hints"; import type { ChannelConfigUiHint } from "openclaw/plugin-sdk/channel-core"; export const telegramChannelConfigUiHints = { diff --git a/extensions/telegram/src/setup-plugin.ts b/extensions/telegram/src/setup-plugin.ts new file mode 100644 index 000000000000..25007c43456c --- /dev/null +++ b/extensions/telegram/src/setup-plugin.ts @@ -0,0 +1,57 @@ +// Telegram plugin module composes the setup-safe channel surface. +import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core"; +import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common"; +import type { ResolvedTelegramAccount } from "./accounts.js"; +import { createTelegramPluginConfig } from "./config-adapter.js"; +import { TelegramChannelConfigSchema } from "./config-schema.js"; +import { collectRuntimeConfigAssignments, secretTargetRegistryEntries } from "./secret-contract.js"; + +const TELEGRAM_CHANNEL = "telegram" as const; + +export function createTelegramSetupPluginBase(params: { + setupWizard: NonNullable["setupWizard"]>; + setupContract: NonNullable["setupContract"]>; +}): Pick< + ChannelPlugin, + | "id" + | "meta" + | "setupWizard" + | "capabilities" + | "reload" + | "configSchema" + | "config" + | "setupContract" + | "secrets" +> { + return { + id: TELEGRAM_CHANNEL, + setupContract: params.setupContract, + meta: { + ...getChatChannelMeta(TELEGRAM_CHANNEL), + quickstartAllowFrom: true, + }, + setupWizard: params.setupWizard, + capabilities: { + chatTypes: ["direct", "group", "channel", "thread"], + reactions: true, + threads: true, + media: true, + tts: { + voice: { + synthesisTarget: "voice-note", + captionedFinalText: true, + }, + }, + polls: true, + nativeCommands: true, + blockStreaming: true, + }, + reload: { configPrefixes: ["channels.telegram"] }, + configSchema: TelegramChannelConfigSchema, + config: createTelegramPluginConfig(), + secrets: { + secretTargetRegistryEntries, + collectRuntimeConfigAssignments, + }, + }; +} diff --git a/extensions/telegram/src/shared.test.ts b/extensions/telegram/src/shared.test.ts index 114ab2abd104..e062ee03d16c 100644 --- a/extensions/telegram/src/shared.test.ts +++ b/extensions/telegram/src/shared.test.ts @@ -2,7 +2,8 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { describe, expect, it } from "vitest"; import type { ResolvedTelegramAccount } from "./accounts.js"; -import { createTelegramPluginBase, telegramConfigAdapter } from "./shared.js"; +import { telegramConfigAdapter } from "./config-adapter.js"; +import { createTelegramPluginBase } from "./shared.js"; const telegramPluginBase = createTelegramPluginBase({ setupWizard: {} as never, diff --git a/extensions/telegram/src/shared.ts b/extensions/telegram/src/shared.ts index a01fd4d2730e..5df0e7625ac6 100644 --- a/extensions/telegram/src/shared.ts +++ b/extensions/telegram/src/shared.ts @@ -1,23 +1,6 @@ -// Telegram plugin module implements shared behavior. -import { resolveNormalizedAccountEntry } from "openclaw/plugin-sdk/account-core"; -import { normalizeAccountId } from "openclaw/plugin-sdk/account-id"; -import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from"; -import { - adaptScopedAccountAccessor, - createScopedChannelConfigAdapter, -} from "openclaw/plugin-sdk/channel-config-helpers"; -import { createChannelPluginBase, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core"; -import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common"; -import type { OpenClawConfig, TelegramAccountConfig } from "openclaw/plugin-sdk/config-contracts"; -import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing"; -import { inspectTelegramAccount } from "./account-inspect.js"; -import { - listTelegramAccountIds, - mergeTelegramAccountConfig, - resolveDefaultTelegramAccountId, - resolveTelegramAccount, - type ResolvedTelegramAccount, -} from "./accounts.js"; +// Telegram plugin module implements shared runtime behavior. +import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core"; +import type { ResolvedTelegramAccount } from "./accounts.js"; import { buildTelegramCommandsListChannelData, buildTelegramModelBrowseChannelData, @@ -26,110 +9,9 @@ import { buildTelegramModelsMenuChannelData, buildTelegramModelsProviderChannelData, } from "./command-ui.js"; -import { TelegramChannelConfigSchema } from "./config-schema.js"; import { telegramDoctor } from "./doctor.js"; -import { collectRuntimeConfigAssignments, secretTargetRegistryEntries } from "./secret-contract.js"; import { telegramSecurityAdapter } from "./security.js"; - -const TELEGRAM_CHANNEL = "telegram" as const; - -type TelegramConfigAccessorAccount = { - config: TelegramAccountConfig; -}; - -export function findTelegramTokenOwnerAccountId(params: { - cfg: OpenClawConfig; - accountId: string; -}): string | null { - const normalizedAccountId = normalizeAccountId(params.accountId); - const tokenOwners = new Map(); - for (const id of listTelegramAccountIds(params.cfg)) { - const account = inspectTelegramAccount({ cfg: params.cfg, accountId: id }); - const token = (account.token ?? "").trim(); - if (!token) { - continue; - } - const ownerAccountId = tokenOwners.get(token); - if (!ownerAccountId) { - tokenOwners.set(token, account.accountId); - continue; - } - if (account.accountId === normalizedAccountId) { - return ownerAccountId; - } - } - return null; -} - -export function formatDuplicateTelegramTokenReason(params: { - accountId: string; - ownerAccountId: string; -}): string { - return ( - `Duplicate Telegram bot token: account "${params.accountId}" shares a token with ` + - `account "${params.ownerAccountId}". Keep one owner account per bot token.` - ); -} - -/** - * Returns true when the runtime token resolver (`resolveTelegramToken`) would - * block channel-level fallthrough for the given accountId. This mirrors the - * guard in `token.ts` so that status-check functions (`isConfigured`, - * `unconfiguredReason`, `describeAccount`) stay consistent with the gateway - * runtime behaviour. - * - * The guard fires when: - * 1. The accountId is not the default account, AND - * 2. The config has an explicit `accounts` section with entries, AND - * 3. The accountId is not found in that `accounts` section. - * - * See: https://github.com/openclaw/openclaw/issues/53876 - */ -function isBlockedByMultiBotGuard(cfg: OpenClawConfig, accountId: string): boolean { - if (normalizeAccountId(accountId) === DEFAULT_ACCOUNT_ID) { - return false; - } - const accounts = cfg.channels?.telegram?.accounts; - const hasConfiguredAccounts = - Boolean(accounts) && - typeof accounts === "object" && - !Array.isArray(accounts) && - Object.keys(accounts).length > 0; - if (!hasConfiguredAccounts) { - return false; - } - // Use resolveNormalizedAccountEntry (same as resolveTelegramToken in token.ts) - // instead of resolveAccountEntry to handle keys that require full normalization - // (e.g. "Carey Notifications" → "carey-notifications"). - return !resolveNormalizedAccountEntry(accounts, accountId, normalizeAccountId); -} - -export function resolveTelegramConfigAccessorAccount(params: { - cfg: OpenClawConfig; - accountId?: string | null; -}): TelegramConfigAccessorAccount { - const accountId = normalizeAccountId( - params.accountId ?? resolveDefaultTelegramAccountId(params.cfg), - ); - return { config: mergeTelegramAccountConfig(params.cfg, accountId) }; -} - -export const telegramConfigAdapter = createScopedChannelConfigAdapter< - ResolvedTelegramAccount, - TelegramConfigAccessorAccount ->({ - sectionKey: TELEGRAM_CHANNEL, - listAccountIds: listTelegramAccountIds, - resolveAccount: adaptScopedAccountAccessor(resolveTelegramAccount), - resolveAccessorAccount: resolveTelegramConfigAccessorAccount, - inspectAccount: adaptScopedAccountAccessor(inspectTelegramAccount), - defaultAccountId: resolveDefaultTelegramAccountId, - clearBaseFields: ["botToken", "tokenFile", "name"], - resolveAllowFrom: (account) => account.config.allowFrom, - formatAllowFrom: (allowFrom) => - formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(telegram|tg):/i }), - resolveDefaultTo: (account) => account.config.defaultTo, -}); +import { createTelegramSetupPluginBase } from "./setup-plugin.js"; export function createTelegramPluginBase(params: { setupWizard: NonNullable["setupWizard"]>; @@ -149,29 +31,8 @@ export function createTelegramPluginBase(params: { | "setupContract" | "secrets" > { - const base = createChannelPluginBase({ - id: TELEGRAM_CHANNEL, - setupContract: params.setupContract, - meta: { - ...getChatChannelMeta(TELEGRAM_CHANNEL), - quickstartAllowFrom: true, - }, - setupWizard: params.setupWizard, - capabilities: { - chatTypes: ["direct", "group", "channel", "thread"], - reactions: true, - threads: true, - media: true, - tts: { - voice: { - synthesisTarget: "voice-note", - captionedFinalText: true, - }, - }, - polls: true, - nativeCommands: true, - blockStreaming: true, - }, + return { + ...createTelegramSetupPluginBase(params), commands: { nativeCommandsAutoEnabled: true, nativeSkillsAutoEnabled: true, @@ -184,96 +45,5 @@ export function createTelegramPluginBase(params: { }, doctor: telegramDoctor, security: telegramSecurityAdapter, - reload: { configPrefixes: ["channels.telegram"] }, - configSchema: TelegramChannelConfigSchema, - config: { - ...telegramConfigAdapter, - hasConfiguredState: ({ env }) => - typeof env?.TELEGRAM_BOT_TOKEN === "string" && env.TELEGRAM_BOT_TOKEN.trim().length > 0, - isConfigured: (account, cfg) => { - // Use inspectTelegramAccount for a complete token resolution that includes - // channel-level fallback paths not available in resolveTelegramAccount. - // This ensures binding-created accountIds that inherit the channel-level - // token are correctly detected as configured. - // See: https://github.com/openclaw/openclaw/issues/53876 - if (isBlockedByMultiBotGuard(cfg, account.accountId)) { - return false; - } - const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); - // Gate on actually available token, not just "configured" — the latter - // includes "configured_unavailable" (unreadable tokenFile, unresolved - // SecretRef) which would pass here but fail at runtime. - if (!inspected.token?.trim()) { - return false; - } - return !findTelegramTokenOwnerAccountId({ cfg, accountId: account.accountId }); - }, - unconfiguredReason: (account, cfg) => { - if (isBlockedByMultiBotGuard(cfg, account.accountId)) { - return `not configured: unknown accountId "${account.accountId}" in multi-bot setup`; - } - const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); - if (!inspected.token?.trim()) { - if (inspected.tokenStatus === "configured_unavailable") { - return `not configured: token ${inspected.tokenSource} is configured but unavailable`; - } - return "not configured"; - } - const ownerAccountId = findTelegramTokenOwnerAccountId({ - cfg, - accountId: account.accountId, - }); - if (!ownerAccountId) { - return "not configured"; - } - return formatDuplicateTelegramTokenReason({ - accountId: account.accountId, - ownerAccountId, - }); - }, - describeAccount: (account, cfg) => { - if (isBlockedByMultiBotGuard(cfg, account.accountId)) { - return { - accountId: account.accountId, - name: account.name, - enabled: account.enabled, - configured: false, - tokenSource: "none" as const, - }; - } - const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); - return { - accountId: account.accountId, - name: account.name, - enabled: account.enabled, - configured: - inspected.tokenStatus !== "missing" && - !findTelegramTokenOwnerAccountId({ cfg, accountId: account.accountId }), - tokenSource: inspected.tokenSource, - tokenStatus: inspected.tokenStatus, - }; - }, - }, - }); - return { - ...base, - secrets: { - secretTargetRegistryEntries, - collectRuntimeConfigAssignments, - }, - } as Pick< - ChannelPlugin, - | "id" - | "meta" - | "setupWizard" - | "capabilities" - | "commands" - | "doctor" - | "security" - | "reload" - | "configSchema" - | "config" - | "setupContract" - | "secrets" - >; + }; } diff --git a/extensions/tsconfig.package-boundary.paths.json b/extensions/tsconfig.package-boundary.paths.json index 543bb4380d47..607bc50dd507 100644 --- a/extensions/tsconfig.package-boundary.paths.json +++ b/extensions/tsconfig.package-boundary.paths.json @@ -257,6 +257,9 @@ "openclaw/plugin-sdk/direct-dm-guard-policy": [ "../packages/plugin-sdk/dist/src/plugin-sdk/direct-dm-guard-policy.d.ts" ], + "openclaw/plugin-sdk/channel-config-ui-hints": [ + "../packages/plugin-sdk/dist/src/plugin-sdk/channel-config-ui-hints.d.ts" + ], "openclaw/plugin-sdk/channel-config-writes": [ "../packages/plugin-sdk/dist/src/plugin-sdk/channel-config-writes.d.ts" ], diff --git a/extensions/xai/tsconfig.json b/extensions/xai/tsconfig.json index 37b860b2ad7f..8f8756fbcbd0 100644 --- a/extensions/xai/tsconfig.json +++ b/extensions/xai/tsconfig.json @@ -254,6 +254,9 @@ "openclaw/plugin-sdk/direct-dm-guard-policy": [ "../../packages/plugin-sdk/dist/src/plugin-sdk/direct-dm-guard-policy.d.ts" ], + "openclaw/plugin-sdk/channel-config-ui-hints": [ + "../../packages/plugin-sdk/dist/src/plugin-sdk/channel-config-ui-hints.d.ts" + ], "openclaw/plugin-sdk/channel-config-writes": [ "../../packages/plugin-sdk/dist/src/plugin-sdk/channel-config-writes.d.ts" ], diff --git a/package.json b/package.json index f3193061db30..e9b0f25d1640 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "!dist/plugin-sdk/browser-config.d.ts", "!dist/plugin-sdk/bundled-channel-config-schema.d.ts", "!dist/plugin-sdk/channel-activity-runtime.d.ts", + "!dist/plugin-sdk/channel-config-ui-hints.d.ts", "!dist/plugin-sdk/channel-config-writes.d.ts", "!dist/plugin-sdk/channel-contract-testing.js", "!dist/plugin-sdk/channel-contract-testing.d.ts", @@ -951,6 +952,9 @@ "types": "./dist/plugin-sdk/channel-config-helpers.d.ts", "default": "./dist/plugin-sdk/channel-config-helpers.js" }, + "./plugin-sdk/channel-config-ui-hints": { + "default": "./dist/plugin-sdk/channel-config-ui-hints.js" + }, "./plugin-sdk/channel-config-writes": { "default": "./dist/plugin-sdk/channel-config-writes.js" }, diff --git a/scripts/lib/plugin-sdk-entrypoints.json b/scripts/lib/plugin-sdk-entrypoints.json index 9a3da0c736e1..ad5b1c7e5125 100644 --- a/scripts/lib/plugin-sdk-entrypoints.json +++ b/scripts/lib/plugin-sdk-entrypoints.json @@ -178,6 +178,7 @@ "error-runtime", "extension-shared", "channel-config-helpers", + "channel-config-ui-hints", "channel-config-writes", "channel-config-primitives", "channel-config-schema", diff --git a/scripts/lib/plugin-sdk-private-local-only-subpaths.json b/scripts/lib/plugin-sdk-private-local-only-subpaths.json index 5b4a0eeb0e95..6eb57f9a4fcf 100644 --- a/scripts/lib/plugin-sdk-private-local-only-subpaths.json +++ b/scripts/lib/plugin-sdk-private-local-only-subpaths.json @@ -17,6 +17,7 @@ "browser-config", "bundled-channel-config-schema", "channel-activity-runtime", + "channel-config-ui-hints", "channel-config-writes", "channel-contract-testing", "channel-mention-gating",