diff --git a/src/commands/doctor/repair-sequencing.ts b/src/commands/doctor/repair-sequencing.ts index abb87d5bc1d..ad0913a4ccf 100644 --- a/src/commands/doctor/repair-sequencing.ts +++ b/src/commands/doctor/repair-sequencing.ts @@ -62,7 +62,6 @@ export async function runDoctorRepairSequence(params: { const emptyAllowlistWarnings = scanEmptyAllowlistPolicyWarnings(state.candidate, { doctorFixCommand: params.doctorFixCommand, - env, ...createChannelDoctorEmptyAllowlistPolicyHooks({ cfg: state.candidate, env }), }); if (emptyAllowlistWarnings.length > 0) { diff --git a/src/commands/doctor/shared/channel-doctor.ts b/src/commands/doctor/shared/channel-doctor.ts index a5b2f94f151..0f591f3d26e 100644 --- a/src/commands/doctor/shared/channel-doctor.ts +++ b/src/commands/doctor/shared/channel-doctor.ts @@ -41,9 +41,7 @@ const channelDoctorBooleanKeys = new Set([ "warnOnEmptyGroupSenderAllowlist", ]); -const channelDoctorStringEnumValues: Partial< - Record> -> = { +const channelDoctorEnumValues: Partial>> = { dmAllowFromMode: new Set(["topOnly", "topOrNested", "nestedOnly"]), groupModel: new Set(["sender", "route", "hybrid"]), }; @@ -106,36 +104,44 @@ function safeListReadOnlyChannelPlugins(context: ChannelDoctorLookupContext) { function mergeDoctorAdapters( adapters: Array, ): ChannelDoctorAdapter | undefined { - const merged: Record = {}; + const merged: Partial> = {}; for (const adapter of adapters) { if (!adapter) { continue; } - for (const [key, value] of Object.entries(adapter)) { - if (merged[key] === undefined && isValidChannelDoctorAdapterValue(key, value)) { - merged[key] = value; + for (const [key, value] of Object.entries(adapter) as Array< + [keyof ChannelDoctorAdapter, unknown] + >) { + if (merged[key] !== undefined) { + continue; } + if (!isValidChannelDoctorAdapterValue(key, value)) { + continue; + } + merged[key] = value; } } return Object.keys(merged).length > 0 ? (merged as ChannelDoctorAdapter) : undefined; } -function isValidChannelDoctorAdapterValue(key: string, value: unknown): boolean { +function isValidChannelDoctorAdapterValue( + key: keyof ChannelDoctorAdapter, + value: unknown, +): boolean { if (value == null) { return false; } - const typedKey = key as keyof ChannelDoctorAdapter; - if (channelDoctorFunctionKeys.has(typedKey)) { + if (channelDoctorFunctionKeys.has(key)) { return typeof value === "function"; } - if (channelDoctorBooleanKeys.has(typedKey)) { + if (channelDoctorBooleanKeys.has(key)) { return typeof value === "boolean"; } - const enumValues = channelDoctorStringEnumValues[typedKey]; + const enumValues = channelDoctorEnumValues[key]; if (enumValues) { return typeof value === "string" && enumValues.has(value); } - if (typedKey === "legacyConfigRules") { + if (key === "legacyConfigRules") { return Array.isArray(value); } return false; @@ -148,13 +154,14 @@ function listChannelDoctorEntries( if (channelIds.length === 0) { return []; } - const byId = new Map(); const selectedIds = new Set(channelIds); - const readOnlyPlugins = safeListReadOnlyChannelPlugins(context).filter((plugin) => - selectedIds.has(plugin.id), + const readOnlyPluginsById = new Map( + safeListReadOnlyChannelPlugins(context) + .filter((plugin) => selectedIds.has(plugin.id)) + .map((plugin) => [plugin.id, plugin]), ); - const readOnlyPluginsById = new Map(readOnlyPlugins.map((plugin) => [plugin.id, plugin])); + const entries: ChannelDoctorEntry[] = []; for (const id of selectedIds) { const doctor = mergeDoctorAdapters([ readOnlyPluginsById.get(id)?.doctor, @@ -165,12 +172,9 @@ function listChannelDoctorEntries( if (!doctor) { continue; } - const existing = byId.get(id); - if (!existing) { - byId.set(id, { doctor }); - } + entries.push({ doctor }); } - return [...byId.values()]; + return entries; } function toPluginEmptyAllowlistContext({ diff --git a/src/commands/doctor/shared/empty-allowlist-scan.ts b/src/commands/doctor/shared/empty-allowlist-scan.ts index 29635b8545a..1c71d106333 100644 --- a/src/commands/doctor/shared/empty-allowlist-scan.ts +++ b/src/commands/doctor/shared/empty-allowlist-scan.ts @@ -4,19 +4,8 @@ import type { DoctorAccountRecord, DoctorAllowFromList } from "../types.js"; import { collectEmptyAllowlistPolicyWarningsForAccount } from "./empty-allowlist-policy.js"; import { asObjectRecord } from "./object.js"; -export type EmptyAllowlistAccountScanParams = { - account: DoctorAccountRecord; - channelName: string; - cfg: OpenClawConfig; - dmPolicy?: string; - effectiveAllowFrom?: DoctorAllowFromList; - parent?: DoctorAccountRecord; - prefix: string; -}; - type ScanEmptyAllowlistPolicyWarningsParams = { doctorFixCommand: string; - env?: NodeJS.ProcessEnv; extraWarningsForAccount?: (params: ChannelDoctorEmptyAllowlistAccountContext) => string[]; shouldSkipDefaultEmptyGroupAllowlistWarning?: ( params: ChannelDoctorEmptyAllowlistAccountContext, diff --git a/src/commands/doctor/shared/preview-warnings.ts b/src/commands/doctor/shared/preview-warnings.ts index c0de5830623..8899304ec64 100644 --- a/src/commands/doctor/shared/preview-warnings.ts +++ b/src/commands/doctor/shared/preview-warnings.ts @@ -164,7 +164,6 @@ export async function collectDoctorPreviewWarnings(params: { }); const emptyAllowlistWarnings = scanEmptyAllowlistPolicyWarnings(params.cfg, { doctorFixCommand: params.doctorFixCommand, - env, extraWarningsForAccount: emptyAllowlistHooks.extraWarningsForAccount, shouldSkipDefaultEmptyGroupAllowlistWarning: emptyAllowlistHooks.shouldSkipDefaultEmptyGroupAllowlistWarning,