fix: resolve CI type and lint regressions

This commit is contained in:
Peter Steinberger
2026-03-30 04:51:33 +09:00
parent 855878b4f0
commit cce6d3bbb7
5 changed files with 31 additions and 30 deletions

View File

@@ -91,23 +91,20 @@ export function setTelegramBotRuntimeForTest(runtime?: TelegramBotRuntime): void
type TelegramFetchInput = Parameters<NonNullable<ApiClientOptions["fetch"]>>[0];
type TelegramFetchInit = Parameters<NonNullable<ApiClientOptions["fetch"]>>[1];
type GlobalFetchInput = Parameters<typeof globalThis.fetch>[0];
type GlobalFetchInit = Parameters<typeof globalThis.fetch>[1];
type TelegramClientFetch = NonNullable<ApiClientOptions["fetch"]>;
type TelegramCompatFetch = (
input: TelegramFetchInput,
init?: TelegramFetchInit,
) => ReturnType<typeof globalThis.fetch>;
) => ReturnType<TelegramClientFetch>;
function asTelegramClientFetch(
fetchImpl: TelegramCompatFetch,
): NonNullable<ApiClientOptions["fetch"]> {
return fetchImpl as NonNullable<ApiClientOptions["fetch"]>;
fetchImpl: TelegramCompatFetch | typeof globalThis.fetch,
): TelegramClientFetch {
return fetchImpl as unknown as TelegramClientFetch;
}
function asTelegramCompatFetch(
fetchImpl: NonNullable<ApiClientOptions["fetch"]>,
): 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<typeof setTimeout> | 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 } : {}),
}

View File

@@ -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<ApiClientOptions["fetch"]> {
return fetchImpl as NonNullable<ApiClientOptions["fetch"]>;
return fetchImpl as unknown as NonNullable<ApiClientOptions["fetch"]>;
}
export function resetTelegramClientOptionsCacheForTests(): void {

View File

@@ -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[] = [];

View File

@@ -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");

View File

@@ -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";