diff --git a/extensions/telegram/src/bot.ts b/extensions/telegram/src/bot.ts index c6517e0611a..32866c03ba1 100644 --- a/extensions/telegram/src/bot.ts +++ b/extensions/telegram/src/bot.ts @@ -91,23 +91,20 @@ export function setTelegramBotRuntimeForTest(runtime?: TelegramBotRuntime): void type TelegramFetchInput = Parameters>[0]; type TelegramFetchInit = Parameters>[1]; -type GlobalFetchInput = Parameters[0]; -type GlobalFetchInit = Parameters[1]; +type TelegramClientFetch = NonNullable; type TelegramCompatFetch = ( input: TelegramFetchInput, init?: TelegramFetchInit, -) => ReturnType; +) => ReturnType; function asTelegramClientFetch( - fetchImpl: TelegramCompatFetch, -): NonNullable { - return fetchImpl as NonNullable; + fetchImpl: TelegramCompatFetch | typeof globalThis.fetch, +): TelegramClientFetch { + return fetchImpl as unknown as TelegramClientFetch; } -function asTelegramCompatFetch( - fetchImpl: NonNullable, -): TelegramCompatFetch { - return fetchImpl as TelegramCompatFetch; +function asTelegramCompatFetch(fetchImpl: TelegramClientFetch): TelegramCompatFetch { + return fetchImpl as unknown as TelegramCompatFetch; } function readRequestUrl(input: TelegramFetchInput): string | null { @@ -179,17 +176,17 @@ export function createTelegramBot(opts: TelegramBotOptions) { // grammY's ApiClientOptions types still track `node-fetch` types; Node 22+ global fetch // (undici) is structurally compatible at runtime but not assignable in TS. const fetchForClient = telegramTransport.fetch - ? asTelegramClientFetch(telegramTransport.fetch) + ? asTelegramCompatFetch(asTelegramClientFetch(telegramTransport.fetch)) : undefined; // Wrap fetch so polling requests cannot hang indefinitely on a wedged network path, // and so shutdown still aborts in-flight Telegram API requests immediately. - let finalFetch = shouldProvideFetch ? fetchForClient : undefined; + let finalFetch: TelegramCompatFetch | undefined = shouldProvideFetch ? fetchForClient : undefined; if (finalFetch || opts.fetchAbortSignal) { - const baseFetch = finalFetch ?? asTelegramClientFetch(globalThis.fetch); + const baseFetch = finalFetch ?? asTelegramCompatFetch(asTelegramClientFetch(globalThis.fetch)); // Cast baseFetch to global fetch to avoid node-fetch ↔ global-fetch type divergence; // they are runtime-compatible (the codebase already casts at every fetch boundary). - const callFetch = asTelegramCompatFetch(baseFetch); + const callFetch = baseFetch; // Use manual event forwarding instead of AbortSignal.any() to avoid the cross-realm // AbortSignal issue in Node.js (grammY's signal may come from a different module context, // causing "signals[0] must be an instance of AbortSignal" errors). @@ -197,23 +194,28 @@ export function createTelegramBot(opts: TelegramBotOptions) { const controller = new AbortController(); const abortWith = (signal: AbortSignal) => controller.abort(signal.reason); const shutdownSignal = opts.fetchAbortSignal; - const onShutdown = () => abortWith(shutdownSignal as AbortSignal); + const onShutdown = () => { + if (shutdownSignal) { + abortWith(shutdownSignal); + } + }; const method = extractTelegramApiMethod(input); const requestTimeoutMs = method === "getupdates" ? TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS : undefined; let requestTimeout: ReturnType | undefined; let onRequestAbort: (() => void) | undefined; + const requestSignal = init?.signal; if (shutdownSignal?.aborted) { abortWith(shutdownSignal); } else if (shutdownSignal) { shutdownSignal.addEventListener("abort", onShutdown, { once: true }); } - if (init?.signal) { - if (init.signal.aborted) { - abortWith(init.signal); + if (requestSignal) { + if (requestSignal.aborted) { + abortWith(requestSignal); } else { - onRequestAbort = () => abortWith(init.signal); - init.signal.addEventListener("abort", onRequestAbort); + onRequestAbort = () => abortWith(requestSignal); + requestSignal.addEventListener("abort", onRequestAbort); } } if (requestTimeoutMs) { @@ -222,16 +224,16 @@ export function createTelegramBot(opts: TelegramBotOptions) { }, requestTimeoutMs); requestTimeout.unref?.(); } - return callFetch(input as GlobalFetchInput, { - ...(init as GlobalFetchInit), + return callFetch(input, { + ...init, signal: controller.signal, }).finally(() => { if (requestTimeout) { clearTimeout(requestTimeout); } shutdownSignal?.removeEventListener("abort", onShutdown); - if (init?.signal && onRequestAbort) { - init.signal.removeEventListener("abort", onRequestAbort); + if (requestSignal && onRequestAbort) { + requestSignal.removeEventListener("abort", onRequestAbort); } }); }; @@ -262,7 +264,7 @@ export function createTelegramBot(opts: TelegramBotOptions) { const client: ApiClientOptions | undefined = finalFetch || timeoutSeconds || apiRoot ? { - ...(finalFetch ? { fetch: finalFetch } : {}), + ...(finalFetch ? { fetch: asTelegramClientFetch(finalFetch) } : {}), ...(timeoutSeconds ? { timeoutSeconds } : {}), ...(apiRoot ? { apiRoot } : {}), } diff --git a/extensions/telegram/src/send.ts b/extensions/telegram/src/send.ts index fa0f1607bde..08d9f58c49d 100644 --- a/extensions/telegram/src/send.ts +++ b/extensions/telegram/src/send.ts @@ -34,6 +34,7 @@ import { isSafeToRetrySendError, isTelegramServerError, } from "./network-errors.js"; +import { normalizeTelegramReplyToMessageId } from "./outbound-params.js"; import { makeProxyFetch } from "./proxy.js"; import { recordSentMessage } from "./sent-message-cache.js"; import { maybePersistResolvedTelegramTarget } from "./target-writeback.js"; @@ -42,7 +43,6 @@ import { normalizeTelegramLookupTarget, parseTelegramTarget, } from "./targets.js"; -import { normalizeTelegramReplyToMessageId } from "./outbound-params.js"; import { resolveTelegramVoiceSend } from "./voice.js"; type TelegramApi = Bot["api"]; @@ -188,7 +188,7 @@ const MAX_TELEGRAM_CLIENT_OPTIONS_CACHE_SIZE = 64; function asTelegramClientFetch( fetchImpl: typeof globalThis.fetch, ): NonNullable { - return fetchImpl as NonNullable; + return fetchImpl as unknown as NonNullable; } export function resetTelegramClientOptionsCacheForTests(): void { diff --git a/src/agents/pi-bundle-mcp-tools.test.ts b/src/agents/pi-bundle-mcp-tools.test.ts index d3b6c4a9868..d03b5e049dd 100644 --- a/src/agents/pi-bundle-mcp-tools.test.ts +++ b/src/agents/pi-bundle-mcp-tools.test.ts @@ -9,7 +9,6 @@ import { createBundleMcpToolRuntime } from "./pi-bundle-mcp-tools.js"; const require = createRequire(import.meta.url); const SDK_SERVER_MCP_PATH = require.resolve("@modelcontextprotocol/sdk/server/mcp.js"); -const SDK_SERVER_STDIO_PATH = require.resolve("@modelcontextprotocol/sdk/server/stdio.js"); const SDK_SERVER_SSE_PATH = require.resolve("@modelcontextprotocol/sdk/server/sse.js"); const tempDirs: string[] = []; diff --git a/src/config/redact-snapshot.test.ts b/src/config/redact-snapshot.test.ts index 9123640b7de..cb5bbc866fe 100644 --- a/src/config/redact-snapshot.test.ts +++ b/src/config/redact-snapshot.test.ts @@ -213,7 +213,7 @@ describe("redactConfigSnapshot", () => { ); const result = redactConfigSnapshot(snapshot, hints); - expect(result.config.mcp.servers.remote.url).toBe(REDACTED_SENTINEL); + expect(result.config.mcp?.servers?.remote?.url).toBe(REDACTED_SENTINEL); expect(result.raw).toContain(REDACTED_SENTINEL); expect(result.raw).not.toContain("user:pass@"); expect(result.raw).not.toContain("secret123"); diff --git a/src/infra/clawhub.ts b/src/infra/clawhub.ts index 31e7af6900f..af80722c29c 100644 --- a/src/infra/clawhub.ts +++ b/src/infra/clawhub.ts @@ -2,7 +2,7 @@ import { createHash } from "node:crypto"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { ExternalPluginCompatibility } from "@openclaw/plugin-package-contract"; +import type { ExternalPluginCompatibility } from "../../packages/plugin-package-contract/src/index.js"; import { isAtLeast, parseSemver } from "./runtime-guard.js"; import { compareComparableSemver, parseComparableSemver } from "./semver-compare.js"; import { createTempDownloadTarget } from "./temp-download.js";