mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
refactor: share sampled entry summary formatting
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
addAllowlistUserEntriesFromConfigEntry,
|
||||
buildAllowlistResolutionSummary,
|
||||
canonicalizeAllowlistWithResolvedIds,
|
||||
patchAllowlistUsersInConfigEntries,
|
||||
summarizeMapping,
|
||||
} from "./resolve-utils.js";
|
||||
|
||||
describe("buildAllowlistResolutionSummary", () => {
|
||||
@@ -94,3 +95,23 @@ describe("patchAllowlistUsersInConfigEntries", () => {
|
||||
expect((patched.beta as { users: string[] }).users).toEqual(["*"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("summarizeMapping", () => {
|
||||
it("logs sampled resolved and unresolved entries", () => {
|
||||
const runtime = { log: vi.fn() };
|
||||
|
||||
summarizeMapping("discord allowlist", ["a", "b", "c", "d", "e", "f", "g"], ["x", "y"], runtime);
|
||||
|
||||
expect(runtime.log).toHaveBeenCalledWith(
|
||||
"discord allowlist resolved: a, b, c, d, e, f (+1)\ndiscord allowlist unresolved: x, y",
|
||||
);
|
||||
});
|
||||
|
||||
it("skips logging when both lists are empty", () => {
|
||||
const runtime = { log: vi.fn() };
|
||||
|
||||
summarizeMapping("discord allowlist", [], [], runtime);
|
||||
|
||||
expect(runtime.log).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { mapAllowFromEntries } from "../../plugin-sdk/channel-config-helpers.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { summarizeStringEntries } from "../../shared/string-sample.js";
|
||||
|
||||
export type AllowlistUserResolutionLike = {
|
||||
input: string;
|
||||
@@ -150,15 +151,10 @@ export function summarizeMapping(
|
||||
): void {
|
||||
const lines: string[] = [];
|
||||
if (mapping.length > 0) {
|
||||
const sample = mapping.slice(0, 6);
|
||||
const suffix = mapping.length > sample.length ? ` (+${mapping.length - sample.length})` : "";
|
||||
lines.push(`${label} resolved: ${sample.join(", ")}${suffix}`);
|
||||
lines.push(`${label} resolved: ${summarizeStringEntries({ entries: mapping, limit: 6 })}`);
|
||||
}
|
||||
if (unresolved.length > 0) {
|
||||
const sample = unresolved.slice(0, 6);
|
||||
const suffix =
|
||||
unresolved.length > sample.length ? ` (+${unresolved.length - sample.length})` : "";
|
||||
lines.push(`${label} unresolved: ${sample.join(", ")}${suffix}`);
|
||||
lines.push(`${label} unresolved: ${summarizeStringEntries({ entries: unresolved, limit: 6 })}`);
|
||||
}
|
||||
if (lines.length > 0) {
|
||||
runtime.log?.(lines.join("\n"));
|
||||
|
||||
@@ -43,6 +43,7 @@ import { createDiscordRetryRunner } from "../../infra/retry-policy.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import { getPluginCommandSpecs } from "../../plugins/commands.js";
|
||||
import { createNonExitingRuntime, type RuntimeEnv } from "../../runtime.js";
|
||||
import { summarizeStringEntries } from "../../shared/string-sample.js";
|
||||
import { resolveDiscordAccount } from "../accounts.js";
|
||||
import { fetchDiscordApplicationId } from "../probe.js";
|
||||
import { normalizeDiscordToken } from "../token.js";
|
||||
@@ -103,25 +104,6 @@ export type MonitorDiscordOpts = {
|
||||
setStatus?: DiscordMonitorStatusSink;
|
||||
};
|
||||
|
||||
function summarizeAllowList(list?: string[]) {
|
||||
if (!list || list.length === 0) {
|
||||
return "any";
|
||||
}
|
||||
const sample = list.slice(0, 4).map((entry) => String(entry));
|
||||
const suffix = list.length > sample.length ? ` (+${list.length - sample.length})` : "";
|
||||
return `${sample.join(", ")}${suffix}`;
|
||||
}
|
||||
|
||||
function summarizeGuilds(entries?: Record<string, unknown>) {
|
||||
if (!entries || Object.keys(entries).length === 0) {
|
||||
return "any";
|
||||
}
|
||||
const keys = Object.keys(entries);
|
||||
const sample = keys.slice(0, 4);
|
||||
const suffix = keys.length > sample.length ? ` (+${keys.length - sample.length})` : "";
|
||||
return `${sample.join(", ")}${suffix}`;
|
||||
}
|
||||
|
||||
function formatThreadBindingDurationForConfigLabel(durationMs: number): string {
|
||||
const label = formatThreadBindingDurationLabel(durationMs);
|
||||
return label === "disabled" ? "off" : label;
|
||||
@@ -402,8 +384,23 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
||||
allowFrom = allowlistResolved.allowFrom;
|
||||
|
||||
if (shouldLogVerbose()) {
|
||||
const allowFromSummary = summarizeStringEntries({
|
||||
entries: allowFrom ?? [],
|
||||
limit: 4,
|
||||
emptyText: "any",
|
||||
});
|
||||
const groupDmChannelSummary = summarizeStringEntries({
|
||||
entries: groupDmChannels ?? [],
|
||||
limit: 4,
|
||||
emptyText: "any",
|
||||
});
|
||||
const guildSummary = summarizeStringEntries({
|
||||
entries: Object.keys(guildEntries ?? {}),
|
||||
limit: 4,
|
||||
emptyText: "any",
|
||||
});
|
||||
logVerbose(
|
||||
`discord: config dm=${dmEnabled ? "on" : "off"} dmPolicy=${dmPolicy} allowFrom=${summarizeAllowList(allowFrom)} groupDm=${groupDmEnabled ? "on" : "off"} groupDmChannels=${summarizeAllowList(groupDmChannels)} groupPolicy=${groupPolicy} guilds=${summarizeGuilds(guildEntries)} historyLimit=${historyLimit} mediaMaxMb=${Math.round(mediaMaxBytes / (1024 * 1024))} native=${nativeEnabled ? "on" : "off"} nativeSkills=${nativeSkillsEnabled ? "on" : "off"} accessGroups=${useAccessGroups ? "on" : "off"} threadBindings=${threadBindingsEnabled ? "on" : "off"} threadIdleTimeout=${formatThreadBindingDurationForConfigLabel(threadBindingIdleTimeoutMs)} threadMaxAge=${formatThreadBindingDurationForConfigLabel(threadBindingMaxAgeMs)}`,
|
||||
`discord: config dm=${dmEnabled ? "on" : "off"} dmPolicy=${dmPolicy} allowFrom=${allowFromSummary} groupDm=${groupDmEnabled ? "on" : "off"} groupDmChannels=${groupDmChannelSummary} groupPolicy=${groupPolicy} guilds=${guildSummary} historyLimit=${historyLimit} mediaMaxMb=${Math.round(mediaMaxBytes / (1024 * 1024))} native=${nativeEnabled ? "on" : "off"} nativeSkills=${nativeSkillsEnabled ? "on" : "off"} accessGroups=${useAccessGroups ? "on" : "off"} threadBindings=${threadBindingsEnabled ? "on" : "off"} threadIdleTimeout=${formatThreadBindingDurationForConfigLabel(threadBindingIdleTimeoutMs)} threadMaxAge=${formatThreadBindingDurationForConfigLabel(threadBindingMaxAgeMs)}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
21
src/shared/string-sample.test.ts
Normal file
21
src/shared/string-sample.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { summarizeStringEntries } from "./string-sample.js";
|
||||
|
||||
describe("summarizeStringEntries", () => {
|
||||
it("returns emptyText for empty lists", () => {
|
||||
expect(summarizeStringEntries({ entries: [], emptyText: "any" })).toBe("any");
|
||||
});
|
||||
|
||||
it("joins short lists without a suffix", () => {
|
||||
expect(summarizeStringEntries({ entries: ["a", "b"], limit: 4 })).toBe("a, b");
|
||||
});
|
||||
|
||||
it("adds a remainder suffix when truncating", () => {
|
||||
expect(
|
||||
summarizeStringEntries({
|
||||
entries: ["a", "b", "c", "d", "e"],
|
||||
limit: 4,
|
||||
}),
|
||||
).toBe("a, b, c, d (+1)");
|
||||
});
|
||||
});
|
||||
14
src/shared/string-sample.ts
Normal file
14
src/shared/string-sample.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export function summarizeStringEntries(params: {
|
||||
entries?: ReadonlyArray<string> | null;
|
||||
limit?: number;
|
||||
emptyText?: string;
|
||||
}): string {
|
||||
const entries = params.entries ?? [];
|
||||
if (entries.length === 0) {
|
||||
return params.emptyText ?? "";
|
||||
}
|
||||
const limit = Math.max(1, Math.floor(params.limit ?? 6));
|
||||
const sample = entries.slice(0, limit);
|
||||
const suffix = entries.length > sample.length ? ` (+${entries.length - sample.length})` : "";
|
||||
return `${sample.join(", ")}${suffix}`;
|
||||
}
|
||||
Reference in New Issue
Block a user