fix(boundary): restore warm support shard checks

This commit is contained in:
Peter Steinberger
2026-04-07 13:07:17 +01:00
parent 9d6c874d50
commit 3a07d664a8
2 changed files with 139 additions and 103 deletions

View File

@@ -3,7 +3,6 @@ import type {
ChannelDoctorLegacyConfigRule,
} from "openclaw/plugin-sdk/channel-contract";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { hasLegacyAccountStreamingAliases } from "openclaw/plugin-sdk/runtime-doctor";
import { resolveTelegramPreviewStreamMode } from "./preview-streaming.js";
function asObjectRecord(value: unknown): Record<string, unknown> | null {
@@ -30,6 +29,17 @@ function hasLegacyTelegramStreamingAliases(value: unknown): boolean {
return typeof streaming === "string" || typeof streaming === "boolean";
}
function hasLegacyAccountStreamingAliases(
value: unknown,
match: (entry: unknown) => boolean,
): boolean {
const accounts = asObjectRecord(value);
if (!accounts) {
return false;
}
return Object.values(accounts).some((account) => match(account));
}
function ensureNestedRecord(owner: Record<string, unknown>, key: string): Record<string, unknown> {
const existing = asObjectRecord(owner[key]);
if (existing) {

View File

@@ -1,9 +1,4 @@
import { describe, expect, it } from "vitest";
import { IMessageConfigSchema } from "../../extensions/imessage/config-api.js";
import { SignalConfigSchema } from "../../extensions/signal/config-api.js";
import { TelegramConfigSchema } from "../../extensions/telegram/config-api.js";
import { WhatsAppConfigSchema } from "../../extensions/whatsapp/config-api.js";
import { findLegacyConfigIssues } from "./legacy.js";
import { validateConfigObject } from "./validation.js";
import {
DiscordConfigSchema,
@@ -11,24 +6,6 @@ import {
SlackConfigSchema,
} from "./zod-schema.providers-core.js";
function expectSchemaInvalidIssuePath(
schema: {
safeParse: (
value: unknown,
) =>
| { success: true }
| { success: false; error: { issues: Array<{ path?: Array<PropertyKey> }> } };
},
config: unknown,
expectedPath: string,
) {
const res = schema.safeParse(config);
expect(res.success).toBe(false);
if (!res.success) {
expect(res.error.issues[0]?.path?.join(".")).toBe(expectedPath);
}
}
function expectSchemaConfigValue(params: {
schema: { safeParse: (value: unknown) => { success: true; data: unknown } | { success: false } };
config: unknown;
@@ -43,6 +20,40 @@ function expectSchemaConfigValue(params: {
expect(params.readValue(res.data)).toBe(params.expectedValue);
}
function expectProviderValidationIssuePath(params: {
provider: string;
config: unknown;
expectedPath: string;
}) {
const res = validateConfigObject({
channels: {
[params.provider]: params.config,
},
});
expect(res.ok, params.provider).toBe(false);
if (!res.ok) {
expect(res.issues[0]?.path, params.provider).toBe(params.expectedPath);
}
}
function expectProviderConfigValue(params: {
provider: string;
config: unknown;
readValue: (config: unknown) => unknown;
expectedValue: unknown;
}) {
const res = validateConfigObject({
channels: {
[params.provider]: params.config,
},
});
expect(res.ok, params.provider).toBe(true);
if (!res.ok) {
throw new Error(`expected ${params.provider} config to be valid`);
}
expect(params.readValue(res.config)).toBe(params.expectedValue);
}
describe("legacy config detection", () => {
it.each([
{
@@ -109,12 +120,6 @@ describe("legacy config detection", () => {
expect(res.issues[0]?.message).toContain('"telegram"');
}
});
it("rejects channels.telegram.groupMentionsOnly", async () => {
const issues = findLegacyConfigIssues({
channels: { telegram: { groupMentionsOnly: true } },
});
expect(issues.some((issue) => issue.path === "channels.telegram.groupMentionsOnly")).toBe(true);
});
it("rejects gateway.token", async () => {
const res = validateConfigObject({
gateway: { token: "legacy-token" },
@@ -140,105 +145,120 @@ describe("legacy config detection", () => {
it.each([
{
name: "telegram",
schema: TelegramConfigSchema,
allowFrom: ["123456789"],
expectedIssuePath: "allowFrom",
expectedIssuePath: "channels.telegram.allowFrom",
},
{
name: "whatsapp",
schema: WhatsAppConfigSchema,
allowFrom: ["+15555550123"],
expectedIssuePath: "allowFrom",
expectedIssuePath: "channels.whatsapp.allowFrom",
},
{
name: "signal",
schema: SignalConfigSchema,
allowFrom: ["+15555550123"],
expectedIssuePath: "allowFrom",
expectedIssuePath: "channels.signal.allowFrom",
},
{
name: "imessage",
schema: IMessageConfigSchema,
allowFrom: ["+15555550123"],
expectedIssuePath: "allowFrom",
expectedIssuePath: "channels.imessage.allowFrom",
},
] as const)(
'enforces dmPolicy="open" allowFrom wildcard for $name',
({ name, schema, allowFrom, expectedIssuePath }) => {
if (schema) {
expectSchemaInvalidIssuePath(schema, { dmPolicy: "open", allowFrom }, expectedIssuePath);
return;
}
const res = validateConfigObject({
channels: {
[name]: { dmPolicy: "open", allowFrom },
},
({ name, allowFrom, expectedIssuePath }) => {
expectProviderValidationIssuePath({
provider: name,
config: { dmPolicy: "open", allowFrom },
expectedPath: expectedIssuePath,
});
expect(res.ok, name).toBe(false);
if (!res.ok) {
expect(res.issues[0]?.path, name).toBe(expectedIssuePath);
}
},
180_000,
);
it.each([
["telegram", TelegramConfigSchema],
["whatsapp", WhatsAppConfigSchema],
["signal", SignalConfigSchema],
] as const)('accepts dmPolicy="open" with wildcard for %s', (provider, schema) => {
expectSchemaConfigValue({
schema,
config: { dmPolicy: "open", allowFrom: ["*"] },
readValue: (config) => (config as { dmPolicy?: string }).dmPolicy,
expectedValue: "open",
});
});
it.each(["telegram", "whatsapp", "signal"] as const)(
'accepts dmPolicy="open" with wildcard for %s',
(provider) => {
expectProviderConfigValue({
provider,
config: { dmPolicy: "open", allowFrom: ["*"] },
readValue: (config) =>
(
config as {
channels?: Record<string, { dmPolicy?: string } | undefined>;
}
).channels?.[provider]?.dmPolicy,
expectedValue: "open",
});
},
);
it.each(["telegram", "whatsapp", "signal"] as const)(
"defaults dm/group policy for configured provider %s",
(provider) => {
expectProviderConfigValue({
provider,
config: {},
readValue: (config) =>
(
config as {
channels?: Record<string, { dmPolicy?: string } | undefined>;
}
).channels?.[provider]?.dmPolicy,
expectedValue: "pairing",
});
expectProviderConfigValue({
provider,
config: {},
readValue: (config) =>
(
config as {
channels?: Record<string, { groupPolicy?: string } | undefined>;
}
).channels?.[provider]?.groupPolicy,
expectedValue: "allowlist",
});
},
);
it.each([
["telegram", TelegramConfigSchema],
["whatsapp", WhatsAppConfigSchema],
["signal", SignalConfigSchema],
] as const)("defaults dm/group policy for configured provider %s", (provider, schema) => {
expectSchemaConfigValue({
schema,
config: {},
readValue: (config) => (config as { dmPolicy?: string }).dmPolicy,
expectedValue: "pairing",
});
expectSchemaConfigValue({
schema,
config: {},
readValue: (config) => (config as { groupPolicy?: string }).groupPolicy,
expectedValue: "allowlist",
});
});
it("accepts historyLimit overrides per provider and account", async () => {
expectSchemaConfigValue({
schema: WhatsAppConfigSchema,
expectProviderConfigValue({
provider: "whatsapp",
config: { historyLimit: 9, accounts: { work: { historyLimit: 4 } } },
readValue: (config) => (config as { historyLimit?: number }).historyLimit,
readValue: (config) =>
(
config as { channels?: { whatsapp?: { historyLimit?: number } } }
).channels?.whatsapp?.historyLimit,
expectedValue: 9,
});
expectSchemaConfigValue({
schema: WhatsAppConfigSchema,
expectProviderConfigValue({
provider: "whatsapp",
config: { historyLimit: 9, accounts: { work: { historyLimit: 4 } } },
readValue: (config) =>
(config as { accounts?: { work?: { historyLimit?: number } } }).accounts?.work
?.historyLimit,
(
config as {
channels?: { whatsapp?: { accounts?: { work?: { historyLimit?: number } } } };
}
).channels?.whatsapp?.accounts?.work?.historyLimit,
expectedValue: 4,
});
expectSchemaConfigValue({
schema: TelegramConfigSchema,
config: { historyLimit: 8, accounts: { ops: { historyLimit: 3 } } },
readValue: (config) => (config as { historyLimit?: number }).historyLimit,
expectedValue: 8,
});
expectSchemaConfigValue({
schema: TelegramConfigSchema,
expectProviderConfigValue({
provider: "telegram",
config: { historyLimit: 8, accounts: { ops: { historyLimit: 3 } } },
readValue: (config) =>
(config as { accounts?: { ops?: { historyLimit?: number } } }).accounts?.ops?.historyLimit,
(
config as { channels?: { telegram?: { historyLimit?: number } } }
).channels?.telegram?.historyLimit,
expectedValue: 8,
});
expectProviderConfigValue({
provider: "telegram",
config: { historyLimit: 8, accounts: { ops: { historyLimit: 3 } } },
readValue: (config) =>
(
config as {
channels?: { telegram?: { accounts?: { ops?: { historyLimit?: number } } } };
}
).channels?.telegram?.accounts?.ops?.historyLimit,
expectedValue: 3,
});
expectSchemaConfigValue({
@@ -254,16 +274,22 @@ describe("legacy config detection", () => {
(config as { accounts?: { ops?: { historyLimit?: number } } }).accounts?.ops?.historyLimit,
expectedValue: 2,
});
expectSchemaConfigValue({
schema: SignalConfigSchema,
expectProviderConfigValue({
provider: "signal",
config: { historyLimit: 6 },
readValue: (config) => (config as { historyLimit?: number }).historyLimit,
readValue: (config) =>
(
config as { channels?: { signal?: { historyLimit?: number } } }
).channels?.signal?.historyLimit,
expectedValue: 6,
});
expectSchemaConfigValue({
schema: IMessageConfigSchema,
expectProviderConfigValue({
provider: "imessage",
config: { historyLimit: 5 },
readValue: (config) => (config as { historyLimit?: number }).historyLimit,
readValue: (config) =>
(
config as { channels?: { imessage?: { historyLimit?: number } } }
).channels?.imessage?.historyLimit,
expectedValue: 5,
});
expectSchemaConfigValue({