From 6a61d5504c5fb483289362de16896921930ebd2f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Mar 2026 02:16:19 +0000 Subject: [PATCH] refactor: share extension deferred and runtime helpers --- extensions/irc/src/onboarding.test.ts | 9 ++------- extensions/matrix/src/channel.directory.test.ts | 9 ++------- extensions/matrix/src/channel.ts | 4 ++-- extensions/matrix/src/matrix/send-queue.test.ts | 15 +++------------ extensions/nostr/src/channel.ts | 8 +++++--- extensions/shared/channel-status-summary.ts | 14 ++++++++++++++ extensions/shared/deferred.ts | 9 +++++++++ extensions/zalouser/src/monitor.ts | 11 +---------- 8 files changed, 38 insertions(+), 41 deletions(-) create mode 100644 extensions/shared/deferred.ts diff --git a/extensions/irc/src/onboarding.test.ts b/extensions/irc/src/onboarding.test.ts index 21f3e978c1a..613503700f3 100644 --- a/extensions/irc/src/onboarding.test.ts +++ b/extensions/irc/src/onboarding.test.ts @@ -1,5 +1,6 @@ import type { RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/irc"; import { describe, expect, it, vi } from "vitest"; +import { createRuntimeEnv } from "../../test-utils/runtime-env.js"; import { ircOnboardingAdapter } from "./onboarding.js"; import type { CoreConfig } from "./types.js"; @@ -63,13 +64,7 @@ describe("irc onboarding", () => { }), }); - const runtime: RuntimeEnv = { - log: vi.fn(), - error: vi.fn(), - exit: vi.fn((code: number): never => { - throw new Error(`exit ${code}`); - }), - }; + const runtime: RuntimeEnv = createRuntimeEnv(); const result = await ircOnboardingAdapter.configure({ cfg: {} as CoreConfig, diff --git a/extensions/matrix/src/channel.directory.test.ts b/extensions/matrix/src/channel.directory.test.ts index 71c9f1c31b1..2c5bc9533f3 100644 --- a/extensions/matrix/src/channel.directory.test.ts +++ b/extensions/matrix/src/channel.directory.test.ts @@ -1,5 +1,6 @@ import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/matrix"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createRuntimeEnv } from "../../test-utils/runtime-env.js"; import { matrixPlugin } from "./channel.js"; import { setMatrixRuntime } from "./runtime.js"; import { createMatrixBotSdkMock } from "./test-mocks.js"; @@ -10,13 +11,7 @@ vi.mock("@vector-im/matrix-bot-sdk", () => ); describe("matrix directory", () => { - const runtimeEnv: RuntimeEnv = { - log: vi.fn(), - error: vi.fn(), - exit: vi.fn((code: number): never => { - throw new Error(`exit ${code}`); - }), - }; + const runtimeEnv: RuntimeEnv = createRuntimeEnv(); beforeEach(() => { setMatrixRuntime({ diff --git a/extensions/matrix/src/channel.ts b/extensions/matrix/src/channel.ts index a024b3f3e8a..bad3322f8d0 100644 --- a/extensions/matrix/src/channel.ts +++ b/extensions/matrix/src/channel.ts @@ -15,6 +15,7 @@ import { PAIRING_APPROVED_MESSAGE, type ChannelPlugin, } from "openclaw/plugin-sdk/matrix"; +import { buildTrafficStatusSummary } from "../../shared/channel-status-summary.js"; import { matrixMessageActions } from "./actions.js"; import { MatrixConfigSchema } from "./config-schema.js"; import { listMatrixDirectoryGroupsLive, listMatrixDirectoryPeersLive } from "./directory-live.js"; @@ -410,8 +411,7 @@ export const matrixPlugin: ChannelPlugin = { lastError: runtime?.lastError ?? null, probe, lastProbeAt: runtime?.lastProbeAt ?? null, - lastInboundAt: runtime?.lastInboundAt ?? null, - lastOutboundAt: runtime?.lastOutboundAt ?? null, + ...buildTrafficStatusSummary(runtime), }), }, gateway: { diff --git a/extensions/matrix/src/matrix/send-queue.test.ts b/extensions/matrix/src/matrix/send-queue.test.ts index aa4765eaab3..240dd8ee71d 100644 --- a/extensions/matrix/src/matrix/send-queue.test.ts +++ b/extensions/matrix/src/matrix/send-queue.test.ts @@ -1,16 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { createDeferred } from "../../../shared/deferred.js"; import { DEFAULT_SEND_GAP_MS, enqueueSend } from "./send-queue.js"; -function deferred() { - let resolve!: (value: T | PromiseLike) => void; - let reject!: (reason?: unknown) => void; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { promise, resolve, reject }; -} - describe("enqueueSend", () => { beforeEach(() => { vi.useFakeTimers(); @@ -21,7 +12,7 @@ describe("enqueueSend", () => { }); it("serializes sends per room", async () => { - const gate = deferred(); + const gate = createDeferred(); const events: string[] = []; const first = enqueueSend("!room:example.org", async () => { @@ -91,7 +82,7 @@ describe("enqueueSend", () => { }); it("continues queued work when the head task fails", async () => { - const gate = deferred(); + const gate = createDeferred(); const events: string[] = []; const first = enqueueSend("!room:example.org", async () => { diff --git a/extensions/nostr/src/channel.ts b/extensions/nostr/src/channel.ts index 43137b23827..937c698bd47 100644 --- a/extensions/nostr/src/channel.ts +++ b/extensions/nostr/src/channel.ts @@ -7,7 +7,10 @@ import { mapAllowFromEntries, type ChannelPlugin, } from "openclaw/plugin-sdk/nostr"; -import { buildPassiveChannelStatusSummary } from "../../shared/channel-status-summary.js"; +import { + buildPassiveChannelStatusSummary, + buildTrafficStatusSummary, +} from "../../shared/channel-status-summary.js"; import type { NostrProfile } from "./config-schema.js"; import { NostrConfigSchema } from "./config-schema.js"; import type { MetricEvent, MetricsSnapshot } from "./metrics.js"; @@ -176,8 +179,7 @@ export const nostrPlugin: ChannelPlugin = { lastStartAt: runtime?.lastStartAt ?? null, lastStopAt: runtime?.lastStopAt ?? null, lastError: runtime?.lastError ?? null, - lastInboundAt: runtime?.lastInboundAt ?? null, - lastOutboundAt: runtime?.lastOutboundAt ?? null, + ...buildTrafficStatusSummary(runtime), }), }, diff --git a/extensions/shared/channel-status-summary.ts b/extensions/shared/channel-status-summary.ts index f2671704ac5..5ebdb067596 100644 --- a/extensions/shared/channel-status-summary.ts +++ b/extensions/shared/channel-status-summary.ts @@ -8,6 +8,11 @@ type PassiveChannelStatusSnapshot = { lastProbeAt?: number | null; }; +type TrafficStatusSnapshot = { + lastInboundAt?: number | null; + lastOutboundAt?: number | null; +}; + export function buildPassiveChannelStatusSummary( snapshot: PassiveChannelStatusSnapshot, extra?: TExtra, @@ -32,3 +37,12 @@ export function buildPassiveProbedChannelStatusSummary( lastProbeAt: snapshot.lastProbeAt ?? null, }; } + +export function buildTrafficStatusSummary( + snapshot?: TSnapshot | null, +) { + return { + lastInboundAt: snapshot?.lastInboundAt ?? null, + lastOutboundAt: snapshot?.lastOutboundAt ?? null, + }; +} diff --git a/extensions/shared/deferred.ts b/extensions/shared/deferred.ts new file mode 100644 index 00000000000..1a874100916 --- /dev/null +++ b/extensions/shared/deferred.ts @@ -0,0 +1,9 @@ +export function createDeferred() { + let resolve!: (value: T | PromiseLike) => void; + let reject!: (reason?: unknown) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +} diff --git a/extensions/zalouser/src/monitor.ts b/extensions/zalouser/src/monitor.ts index 3ba7e80d2b9..2bfa1be8aa4 100644 --- a/extensions/zalouser/src/monitor.ts +++ b/extensions/zalouser/src/monitor.ts @@ -31,6 +31,7 @@ import { summarizeMapping, warnMissingProviderGroupPolicyFallbackOnce, } from "openclaw/plugin-sdk/zalouser"; +import { createDeferred } from "../../shared/deferred.js"; import { buildZalouserGroupCandidates, findZalouserGroupEntry, @@ -129,16 +130,6 @@ function resolveInboundQueueKey(message: ZaloInboundMessage): string { return `direct:${senderId || threadId}`; } -function createDeferred() { - let resolve!: (value: T | PromiseLike) => void; - let reject!: (reason?: unknown) => void; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { promise, resolve, reject }; -} - function resolveZalouserDmSessionScope(config: OpenClawConfig) { const configured = config.session?.dmScope; return configured === "main" || !configured ? "per-channel-peer" : configured;