mirror of https://github.com/openclaw/openclaw.git
fix: propagate webhook mode to health monitor snapshot
Webhook channels (LINE, Zalo, Nextcloud Talk, BlueBubbles) are incorrectly flagged as stale-socket during quiet periods because snapshot.mode is always undefined, making the mode !== "webhook" guard in evaluateChannelHealth dead code. Add mode: "webhook" to each webhook plugin's describeAccount and propagate described.mode in getRuntimeSnapshot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
acca306665
commit
acbdafc4f4
|
|
@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Memory/QMD: resolve slugified `memory_search` file hints back to the indexed filesystem path before returning search hits, so `memory_get` works again for mixed-case and spaced paths. (#50313) Thanks @erra9x.
|
||||
- Security/LINE: make webhook signature validation run the timing-safe compare even when the supplied signature length is wrong, closing a small timing side-channel. (#55663) Thanks @gavyngong.
|
||||
- LINE/status: stop `openclaw status` from warning about missing credentials when sanitized LINE snapshots are already configured, while still surfacing whether the missing field is the token or secret. (#45701) Thanks @tamaosamu.
|
||||
- Gateway/health: carry webhook-vs-polling account mode from channel descriptors into runtime snapshots so passive channels like LINE and BlueBubbles skip false stale-socket health failures. (#47488) Thanks @karesansui-u.
|
||||
|
||||
## 2026.3.28-beta.1
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ export function describeBlueBubblesAccount(account: ResolvedBlueBubblesAccount)
|
|||
configured: account.configured,
|
||||
extra: {
|
||||
baseUrl: account.baseUrl,
|
||||
mode: "webhook",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const lineChannelPluginCommon = {
|
|||
enabled: account.enabled,
|
||||
configured: hasLineCredentials(account),
|
||||
tokenSource: account.tokenSource ?? undefined,
|
||||
mode: "webhook",
|
||||
}),
|
||||
},
|
||||
} satisfies Pick<
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ export const zaloPlugin: ChannelPlugin<ResolvedZaloAccount, ZaloProbeResult> =
|
|||
configured: Boolean(account.token?.trim()),
|
||||
extra: {
|
||||
tokenSource: account.tokenSource,
|
||||
mode: account.config.webhookUrl ? "webhook" : "polling",
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ function createTestPlugin(params?: {
|
|||
account?: TestAccount;
|
||||
startAccount?: NonNullable<ChannelPlugin<TestAccount>["gateway"]>["startAccount"];
|
||||
includeDescribeAccount?: boolean;
|
||||
describeAccount?: ChannelPlugin<TestAccount>["config"]["describeAccount"];
|
||||
resolveAccount?: ChannelPlugin<TestAccount>["config"]["resolveAccount"];
|
||||
isConfigured?: ChannelPlugin<TestAccount>["config"]["isConfigured"];
|
||||
}): ChannelPlugin<TestAccount> {
|
||||
|
|
@ -59,11 +60,13 @@ function createTestPlugin(params?: {
|
|||
...(params?.isConfigured ? { isConfigured: params.isConfigured } : {}),
|
||||
};
|
||||
if (includeDescribeAccount) {
|
||||
config.describeAccount = (resolved) => ({
|
||||
accountId: DEFAULT_ACCOUNT_ID,
|
||||
enabled: resolved.enabled !== false,
|
||||
configured: resolved.configured !== false,
|
||||
});
|
||||
config.describeAccount =
|
||||
params?.describeAccount ??
|
||||
((resolved) => ({
|
||||
accountId: DEFAULT_ACCOUNT_ID,
|
||||
enabled: resolved.enabled !== false,
|
||||
configured: resolved.configured !== false,
|
||||
}));
|
||||
}
|
||||
const gateway: NonNullable<ChannelPlugin<TestAccount>["gateway"]> = {};
|
||||
if (params?.startAccount) {
|
||||
|
|
@ -198,6 +201,23 @@ describe("server-channels auto restart", () => {
|
|||
expect(account?.configured).toBe(true);
|
||||
});
|
||||
|
||||
it("forwards described mode into runtime snapshots", () => {
|
||||
installTestRegistry(
|
||||
createTestPlugin({
|
||||
describeAccount: (resolved) => ({
|
||||
accountId: DEFAULT_ACCOUNT_ID,
|
||||
enabled: resolved.enabled !== false,
|
||||
configured: resolved.configured !== false,
|
||||
mode: "webhook",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const manager = createManager();
|
||||
const snapshot = manager.getRuntimeSnapshot();
|
||||
const account = snapshot.channelAccounts.discord?.[DEFAULT_ACCOUNT_ID];
|
||||
expect(account?.mode).toBe("webhook");
|
||||
});
|
||||
|
||||
it("passes channelRuntime through channel gateway context when provided", async () => {
|
||||
const channelRuntime = { marker: "channel-runtime" } as unknown as PluginRuntime["channel"];
|
||||
const startAccount = vi.fn(async (ctx) => {
|
||||
|
|
|
|||
|
|
@ -555,6 +555,9 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
|
|||
const next = { ...current, accountId: id };
|
||||
next.enabled = enabled;
|
||||
next.configured = typeof configured === "boolean" ? configured : (next.configured ?? true);
|
||||
if (described?.mode !== undefined) {
|
||||
next.mode = described.mode;
|
||||
}
|
||||
if (!next.running) {
|
||||
if (!enabled) {
|
||||
next.lastError ??= plugin.config.disabledReason?.(account, cfg) ?? "disabled";
|
||||
|
|
|
|||
Loading…
Reference in New Issue