From f1e7e28a8a2b638d308d2cebd66144ae437da2f3 Mon Sep 17 00:00:00 2001 From: Shadow Date: Sun, 15 Feb 2026 11:34:21 -0600 Subject: [PATCH] fix: dedupe probe/token base types (#16986) (thanks @iyoda) --- CHANGELOG.md | 1 + extensions/bluebubbles/src/probe.ts | 5 +- extensions/feishu/src/types.ts | 5 +- extensions/irc/src/types.ts | 5 +- extensions/matrix/src/matrix/probe.ts | 5 +- extensions/mattermost/src/mattermost/probe.ts | 5 +- extensions/msteams/src/probe.ts | 6 +-- extensions/twitch/src/probe.ts | 5 +- extensions/zalo/src/probe.ts | 5 +- extensions/zalo/src/token.ts | 5 +- extensions/zalouser/src/probe.ts | 7 ++- .../plugins/base-types-assignability.test.ts | 46 +++++++++++++++++++ src/channels/plugins/types.core.ts | 12 +++++ src/channels/plugins/types.ts | 2 + src/discord/probe.ts | 5 +- src/discord/token.ts | 4 +- src/imessage/probe.ts | 5 +- src/line/types.ts | 7 ++- src/plugin-sdk/index.ts | 2 + src/signal/probe.ts | 5 +- src/slack/probe.ts | 5 +- src/telegram/probe.ts | 5 +- src/telegram/token.ts | 4 +- 23 files changed, 101 insertions(+), 55 deletions(-) create mode 100644 src/channels/plugins/base-types-assignability.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5332b5a0..bd1fa37af9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai - Subagents: nested sub-agents (sub-sub-agents) with configurable depth. Set `agents.defaults.subagents.maxSpawnDepth: 2` to allow sub-agents to spawn their own children. Includes `maxChildrenPerAgent` limit (default 5), depth-aware tool policy, and proper announce chain routing. (#14447) Thanks @tyler6204. - Discord: components v2 UI + embeds passthrough + exec approval UX refinements (CV2 containers, button layout, Discord-forwarding skip). Thanks @thewilloftheshadow. - Slack/Discord/Telegram: add per-channel ack reaction overrides (account/channel-level) to support platform-specific emoji formats. (#17092) Thanks @zerone0x. +- Channels: deduplicate probe/token resolution base types across core + extensions while preserving per-channel error typing. (#16986) Thanks @iyoda and @thewilloftheshadow. ### Fixes diff --git a/extensions/bluebubbles/src/probe.ts b/extensions/bluebubbles/src/probe.ts index 7b49ae698e..e60c47dc64 100644 --- a/extensions/bluebubbles/src/probe.ts +++ b/extensions/bluebubbles/src/probe.ts @@ -1,9 +1,8 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import { buildBlueBubblesApiUrl, blueBubblesFetchWithTimeout } from "./types.js"; -export type BlueBubblesProbe = { - ok: boolean; +export type BlueBubblesProbe = BaseProbeResult & { status?: number | null; - error?: string | null; }; export type BlueBubblesServerInfo = { diff --git a/extensions/feishu/src/types.ts b/extensions/feishu/src/types.ts index dbfde80780..dad248aa9f 100644 --- a/extensions/feishu/src/types.ts +++ b/extensions/feishu/src/types.ts @@ -1,3 +1,4 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import type { FeishuConfigSchema, FeishuGroupSchema, @@ -52,9 +53,7 @@ export type FeishuSendResult = { chatId: string; }; -export type FeishuProbeResult = { - ok: boolean; - error?: string; +export type FeishuProbeResult = BaseProbeResult & { appId?: string; botName?: string; botOpenId?: string; diff --git a/extensions/irc/src/types.ts b/extensions/irc/src/types.ts index 5446649aad..ac6a5c9cb7 100644 --- a/extensions/irc/src/types.ts +++ b/extensions/irc/src/types.ts @@ -1,3 +1,4 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import type { BlockStreamingCoalesceConfig, DmConfig, @@ -83,12 +84,10 @@ export type IrcInboundMessage = { isGroup: boolean; }; -export type IrcProbe = { - ok: boolean; +export type IrcProbe = BaseProbeResult & { host: string; port: number; tls: boolean; nick: string; latencyMs?: number; - error?: string; }; diff --git a/extensions/matrix/src/matrix/probe.ts b/extensions/matrix/src/matrix/probe.ts index 7bd54bdc40..5681b242c2 100644 --- a/extensions/matrix/src/matrix/probe.ts +++ b/extensions/matrix/src/matrix/probe.ts @@ -1,9 +1,8 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import { createMatrixClient, isBunRuntime } from "./client.js"; -export type MatrixProbe = { - ok: boolean; +export type MatrixProbe = BaseProbeResult & { status?: number | null; - error?: string | null; elapsedMs: number; userId?: string | null; }; diff --git a/extensions/mattermost/src/mattermost/probe.ts b/extensions/mattermost/src/mattermost/probe.ts index a02ca4935f..cb468ec14d 100644 --- a/extensions/mattermost/src/mattermost/probe.ts +++ b/extensions/mattermost/src/mattermost/probe.ts @@ -1,9 +1,8 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import { normalizeMattermostBaseUrl, type MattermostUser } from "./client.js"; -export type MattermostProbe = { - ok: boolean; +export type MattermostProbe = BaseProbeResult & { status?: number | null; - error?: string | null; elapsedMs?: number | null; bot?: MattermostUser; }; diff --git a/extensions/msteams/src/probe.ts b/extensions/msteams/src/probe.ts index 6bbcc0b3c3..b6732c658c 100644 --- a/extensions/msteams/src/probe.ts +++ b/extensions/msteams/src/probe.ts @@ -1,11 +1,9 @@ -import type { MSTeamsConfig } from "openclaw/plugin-sdk"; +import type { BaseProbeResult, MSTeamsConfig } from "openclaw/plugin-sdk"; import { formatUnknownError } from "./errors.js"; import { loadMSTeamsSdkWithAuth } from "./sdk.js"; import { resolveMSTeamsCredentials } from "./token.js"; -export type ProbeMSTeamsResult = { - ok: boolean; - error?: string; +export type ProbeMSTeamsResult = BaseProbeResult & { appId?: string; graph?: { ok: boolean; diff --git a/extensions/twitch/src/probe.ts b/extensions/twitch/src/probe.ts index 56ea99146d..41321103a4 100644 --- a/extensions/twitch/src/probe.ts +++ b/extensions/twitch/src/probe.ts @@ -1,3 +1,4 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import { StaticAuthProvider } from "@twurple/auth"; import { ChatClient } from "@twurple/chat"; import type { TwitchAccountConfig } from "./types.js"; @@ -6,9 +7,7 @@ import { normalizeToken } from "./utils/twitch.js"; /** * Result of probing a Twitch account */ -export type ProbeTwitchResult = { - ok: boolean; - error?: string; +export type ProbeTwitchResult = BaseProbeResult & { username?: string; elapsedMs: number; connected?: boolean; diff --git a/extensions/zalo/src/probe.ts b/extensions/zalo/src/probe.ts index ebdb37a34f..c2d95fa1d2 100644 --- a/extensions/zalo/src/probe.ts +++ b/extensions/zalo/src/probe.ts @@ -1,9 +1,8 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import { getMe, ZaloApiError, type ZaloBotInfo, type ZaloFetch } from "./api.js"; -export type ZaloProbeResult = { - ok: boolean; +export type ZaloProbeResult = BaseProbeResult & { bot?: ZaloBotInfo; - error?: string; elapsedMs: number; }; diff --git a/extensions/zalo/src/token.ts b/extensions/zalo/src/token.ts index 480f66c8fa..b335f57a3c 100644 --- a/extensions/zalo/src/token.ts +++ b/extensions/zalo/src/token.ts @@ -1,9 +1,8 @@ import { readFileSync } from "node:fs"; -import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk"; +import { type BaseTokenResolution, DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk"; import type { ZaloConfig } from "./types.js"; -export type ZaloTokenResolution = { - token: string; +export type ZaloTokenResolution = BaseTokenResolution & { source: "env" | "config" | "configFile" | "none"; }; diff --git a/extensions/zalouser/src/probe.ts b/extensions/zalouser/src/probe.ts index bfeb92ec58..6bdc962052 100644 --- a/extensions/zalouser/src/probe.ts +++ b/extensions/zalouser/src/probe.ts @@ -1,11 +1,10 @@ +import type { BaseProbeResult } from "openclaw/plugin-sdk"; import type { ZcaUserInfo } from "./types.js"; import { runZca, parseJsonOutput } from "./zca.js"; -export interface ZalouserProbeResult { - ok: boolean; +export type ZalouserProbeResult = BaseProbeResult & { user?: ZcaUserInfo; - error?: string; -} +}; export async function probeZalouser( profile: string, diff --git a/src/channels/plugins/base-types-assignability.test.ts b/src/channels/plugins/base-types-assignability.test.ts new file mode 100644 index 0000000000..839146018f --- /dev/null +++ b/src/channels/plugins/base-types-assignability.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expectTypeOf } from "vitest"; +import type { DiscordProbe } from "../../discord/probe.js"; +import type { DiscordTokenResolution } from "../../discord/token.js"; +import type { IMessageProbe } from "../../imessage/probe.js"; +import type { LineProbeResult } from "../../line/types.js"; +import type { SignalProbe } from "../../signal/probe.js"; +import type { SlackProbe } from "../../slack/probe.js"; +import type { TelegramProbe } from "../../telegram/probe.js"; +import type { TelegramTokenResolution } from "../../telegram/token.js"; +import type { BaseProbeResult, BaseTokenResolution } from "./types.js"; + +describe("BaseProbeResult assignability", () => { + it("TelegramProbe satisfies BaseProbeResult", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("DiscordProbe satisfies BaseProbeResult", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("SlackProbe satisfies BaseProbeResult", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("SignalProbe satisfies BaseProbeResult", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("IMessageProbe satisfies BaseProbeResult", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("LineProbeResult satisfies BaseProbeResult", () => { + expectTypeOf().toMatchTypeOf(); + }); +}); + +describe("BaseTokenResolution assignability", () => { + it("TelegramTokenResolution satisfies BaseTokenResolution", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("DiscordTokenResolution satisfies BaseTokenResolution", () => { + expectTypeOf().toMatchTypeOf(); + }); +}); diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index a2195597e0..2178acd5ee 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -347,3 +347,15 @@ export type ChannelPollContext = { silent?: boolean; isAnonymous?: boolean; }; + +/** Minimal base for all channel probe results. Channel-specific probes extend this. */ +export type BaseProbeResult = { + ok: boolean; + error?: TError; +}; + +/** Minimal base for token resolution results. */ +export type BaseTokenResolution = { + token: string; + source: string; +}; diff --git a/src/channels/plugins/types.ts b/src/channels/plugins/types.ts index d7175f1765..d3028e9970 100644 --- a/src/channels/plugins/types.ts +++ b/src/channels/plugins/types.ts @@ -58,6 +58,8 @@ export type { ChannelThreadingContext, ChannelThreadingToolContext, ChannelToolSend, + BaseProbeResult, + BaseTokenResolution, } from "./types.core.js"; export type { ChannelPlugin } from "./types.plugin.js"; diff --git a/src/discord/probe.ts b/src/discord/probe.ts index fd01cdd544..b199e89fdd 100644 --- a/src/discord/probe.ts +++ b/src/discord/probe.ts @@ -1,13 +1,12 @@ +import type { BaseProbeResult } from "../channels/plugins/types.js"; import { resolveFetch } from "../infra/fetch.js"; import { fetchWithTimeout } from "../utils/fetch-timeout.js"; import { normalizeDiscordToken } from "./token.js"; const DISCORD_API_BASE = "https://discord.com/api/v10"; -export type DiscordProbe = { - ok: boolean; +export type DiscordProbe = BaseProbeResult & { status?: number | null; - error?: string | null; elapsedMs: number; bot?: { id?: string | null; username?: string | null }; application?: DiscordApplicationSummary; diff --git a/src/discord/token.ts b/src/discord/token.ts index 2187fbc32b..5f26599404 100644 --- a/src/discord/token.ts +++ b/src/discord/token.ts @@ -1,10 +1,10 @@ +import type { BaseTokenResolution } from "../channels/plugins/types.js"; import type { OpenClawConfig } from "../config/config.js"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js"; export type DiscordTokenSource = "env" | "config" | "none"; -export type DiscordTokenResolution = { - token: string; +export type DiscordTokenResolution = BaseTokenResolution & { source: DiscordTokenSource; }; diff --git a/src/imessage/probe.ts b/src/imessage/probe.ts index 9226d48b1e..27b228e8d5 100644 --- a/src/imessage/probe.ts +++ b/src/imessage/probe.ts @@ -1,3 +1,4 @@ +import type { BaseProbeResult } from "../channels/plugins/types.js"; import type { RuntimeEnv } from "../runtime.js"; import { detectBinary } from "../commands/onboard-helpers.js"; import { loadConfig } from "../config/config.js"; @@ -8,9 +9,7 @@ import { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS } from "./constants.js"; // Re-export for backwards compatibility export { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS } from "./constants.js"; -export type IMessageProbe = { - ok: boolean; - error?: string | null; +export type IMessageProbe = BaseProbeResult & { fatal?: boolean; }; diff --git a/src/line/types.ts b/src/line/types.ts index dbd157cad7..8a79701625 100644 --- a/src/line/types.ts +++ b/src/line/types.ts @@ -7,6 +7,7 @@ import type { StickerMessage, LocationMessage, } from "@line/bot-sdk"; +import type { BaseProbeResult } from "../channels/plugins/types.js"; export type LineTokenSource = "config" | "env" | "file" | "none"; @@ -86,16 +87,14 @@ export interface LineSendResult { chatId: string; } -export interface LineProbeResult { - ok: boolean; +export type LineProbeResult = BaseProbeResult & { bot?: { displayName?: string; userId?: string; basicId?: string; pictureUrl?: string; }; - error?: string; -} +}; export type LineFlexMessagePayload = { altText: string; diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index 6a8d967244..48ad88aacc 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -56,6 +56,8 @@ export type { ChannelThreadingContext, ChannelThreadingToolContext, ChannelToolSend, + BaseProbeResult, + BaseTokenResolution, } from "../channels/plugins/types.js"; export type { ChannelConfigSchema, ChannelPlugin } from "../channels/plugins/types.plugin.js"; export type { diff --git a/src/signal/probe.ts b/src/signal/probe.ts index 9a6238048a..924f997015 100644 --- a/src/signal/probe.ts +++ b/src/signal/probe.ts @@ -1,9 +1,8 @@ +import type { BaseProbeResult } from "../channels/plugins/types.js"; import { signalCheck, signalRpcRequest } from "./client.js"; -export type SignalProbe = { - ok: boolean; +export type SignalProbe = BaseProbeResult & { status?: number | null; - error?: string | null; elapsedMs: number; version?: string | null; }; diff --git a/src/slack/probe.ts b/src/slack/probe.ts index cde5e51573..fa67b1f191 100644 --- a/src/slack/probe.ts +++ b/src/slack/probe.ts @@ -1,9 +1,8 @@ +import type { BaseProbeResult } from "../channels/plugins/types.js"; import { createSlackWebClient } from "./client.js"; -export type SlackProbe = { - ok: boolean; +export type SlackProbe = BaseProbeResult & { status?: number | null; - error?: string | null; elapsedMs?: number | null; bot?: { id?: string; name?: string }; team?: { id?: string; name?: string }; diff --git a/src/telegram/probe.ts b/src/telegram/probe.ts index cc65f987f5..f988733f0e 100644 --- a/src/telegram/probe.ts +++ b/src/telegram/probe.ts @@ -1,12 +1,11 @@ +import type { BaseProbeResult } from "../channels/plugins/types.js"; import { fetchWithTimeout } from "../utils/fetch-timeout.js"; import { makeProxyFetch } from "./proxy.js"; const TELEGRAM_API_BASE = "https://api.telegram.org"; -export type TelegramProbe = { - ok: boolean; +export type TelegramProbe = BaseProbeResult & { status?: number | null; - error?: string | null; elapsedMs: number; bot?: { id?: number | null; diff --git a/src/telegram/token.ts b/src/telegram/token.ts index ed11d3f747..461fcf5259 100644 --- a/src/telegram/token.ts +++ b/src/telegram/token.ts @@ -1,12 +1,12 @@ import fs from "node:fs"; +import type { BaseTokenResolution } from "../channels/plugins/types.js"; import type { OpenClawConfig } from "../config/config.js"; import type { TelegramAccountConfig } from "../config/types.telegram.js"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js"; export type TelegramTokenSource = "env" | "tokenFile" | "config" | "none"; -export type TelegramTokenResolution = { - token: string; +export type TelegramTokenResolution = BaseTokenResolution & { source: TelegramTokenSource; };