mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 04:31:10 +00:00
fix(boundary): restore warm support shard checks
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user