mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 01:40:23 +00:00
refactor: deduplicate channel runtime helpers
This commit is contained in:
247
src/plugin-sdk/allowlist-config-edit.test.ts
Normal file
247
src/plugin-sdk/allowlist-config-edit.test.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildDmGroupAccountAllowlistAdapter,
|
||||
buildLegacyDmAccountAllowlistAdapter,
|
||||
collectAllowlistOverridesFromRecord,
|
||||
collectNestedAllowlistOverridesFromRecord,
|
||||
createAccountScopedAllowlistNameResolver,
|
||||
createFlatAllowlistOverrideResolver,
|
||||
createNestedAllowlistOverrideResolver,
|
||||
readConfiguredAllowlistEntries,
|
||||
} from "./allowlist-config-edit.js";
|
||||
|
||||
describe("readConfiguredAllowlistEntries", () => {
|
||||
it("coerces mixed entries to non-empty strings", () => {
|
||||
expect(readConfiguredAllowlistEntries(["owner", 42, ""])).toEqual(["owner", "42"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("collectAllowlistOverridesFromRecord", () => {
|
||||
it("collects only non-empty overrides from a flat record", () => {
|
||||
expect(
|
||||
collectAllowlistOverridesFromRecord({
|
||||
record: {
|
||||
room1: { users: ["a", "b"] },
|
||||
room2: { users: [] },
|
||||
},
|
||||
label: (key) => key,
|
||||
resolveEntries: (value) => value.users,
|
||||
}),
|
||||
).toEqual([{ label: "room1", entries: ["a", "b"] }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("collectNestedAllowlistOverridesFromRecord", () => {
|
||||
it("collects outer and nested overrides from a hierarchical record", () => {
|
||||
expect(
|
||||
collectNestedAllowlistOverridesFromRecord({
|
||||
record: {
|
||||
guild1: {
|
||||
users: ["owner"],
|
||||
channels: {
|
||||
chan1: { users: ["member"] },
|
||||
},
|
||||
},
|
||||
},
|
||||
outerLabel: (key) => `guild ${key}`,
|
||||
resolveOuterEntries: (value) => value.users,
|
||||
resolveChildren: (value) => value.channels,
|
||||
innerLabel: (outerKey, innerKey) => `guild ${outerKey} / channel ${innerKey}`,
|
||||
resolveInnerEntries: (value) => value.users,
|
||||
}),
|
||||
).toEqual([
|
||||
{ label: "guild guild1", entries: ["owner"] },
|
||||
{ label: "guild guild1 / channel chan1", entries: ["member"] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createFlatAllowlistOverrideResolver", () => {
|
||||
it("builds an account-scoped flat override resolver", () => {
|
||||
const resolveOverrides = createFlatAllowlistOverrideResolver({
|
||||
resolveRecord: (account: { channels?: Record<string, { users: string[] }> }) =>
|
||||
account.channels,
|
||||
label: (key) => key,
|
||||
resolveEntries: (value) => value.users,
|
||||
});
|
||||
|
||||
expect(resolveOverrides({ channels: { room1: { users: ["a"] } } })).toEqual([
|
||||
{ label: "room1", entries: ["a"] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createNestedAllowlistOverrideResolver", () => {
|
||||
it("builds an account-scoped nested override resolver", () => {
|
||||
const resolveOverrides = createNestedAllowlistOverrideResolver({
|
||||
resolveRecord: (account: {
|
||||
groups?: Record<
|
||||
string,
|
||||
{ allowFrom?: string[]; topics?: Record<string, { allowFrom?: string[] }> }
|
||||
>;
|
||||
}) => account.groups,
|
||||
outerLabel: (groupId) => groupId,
|
||||
resolveOuterEntries: (group) => group.allowFrom,
|
||||
resolveChildren: (group) => group.topics,
|
||||
innerLabel: (groupId, topicId) => `${groupId} topic ${topicId}`,
|
||||
resolveInnerEntries: (topic) => topic.allowFrom,
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveOverrides({
|
||||
groups: {
|
||||
g1: { allowFrom: ["owner"], topics: { t1: { allowFrom: ["member"] } } },
|
||||
},
|
||||
}),
|
||||
).toEqual([
|
||||
{ label: "g1", entries: ["owner"] },
|
||||
{ label: "g1 topic t1", entries: ["member"] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createAccountScopedAllowlistNameResolver", () => {
|
||||
it("returns empty results when the resolved account has no token", async () => {
|
||||
const resolveNames = createAccountScopedAllowlistNameResolver({
|
||||
resolveAccount: () => ({ token: "" }),
|
||||
resolveToken: (account) => account.token,
|
||||
resolveNames: async ({ token, entries }) =>
|
||||
entries.map((entry) => ({ input: `${token}:${entry}`, resolved: true })),
|
||||
});
|
||||
|
||||
expect(await resolveNames({ cfg: {}, accountId: "alt", scope: "dm", entries: ["a"] })).toEqual(
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
it("delegates to the resolver when a token is present", async () => {
|
||||
const resolveNames = createAccountScopedAllowlistNameResolver({
|
||||
resolveAccount: () => ({ token: " secret " }),
|
||||
resolveToken: (account) => account.token,
|
||||
resolveNames: async ({ token, entries }) =>
|
||||
entries.map((entry) => ({ input: entry, resolved: true, name: `${token}:${entry}` })),
|
||||
});
|
||||
|
||||
expect(await resolveNames({ cfg: {}, accountId: "alt", scope: "dm", entries: ["a"] })).toEqual([
|
||||
{ input: "a", resolved: true, name: "secret:a" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildDmGroupAccountAllowlistAdapter", () => {
|
||||
const adapter = buildDmGroupAccountAllowlistAdapter({
|
||||
channelId: "demo",
|
||||
resolveAccount: ({ accountId }) => ({
|
||||
accountId: accountId ?? "default",
|
||||
dmAllowFrom: ["dm-owner"],
|
||||
groupAllowFrom: ["group-owner"],
|
||||
dmPolicy: "allowlist",
|
||||
groupPolicy: "allowlist",
|
||||
groupOverrides: [{ label: "room-1", entries: ["member-1"] }],
|
||||
}),
|
||||
normalize: ({ values }) => values.map((entry) => String(entry).trim().toLowerCase()),
|
||||
resolveDmAllowFrom: (account) => account.dmAllowFrom,
|
||||
resolveGroupAllowFrom: (account) => account.groupAllowFrom,
|
||||
resolveDmPolicy: (account) => account.dmPolicy,
|
||||
resolveGroupPolicy: (account) => account.groupPolicy,
|
||||
resolveGroupOverrides: (account) => account.groupOverrides,
|
||||
});
|
||||
|
||||
it("supports dm, group, and all scopes", () => {
|
||||
expect(adapter.supportsScope?.({ scope: "dm" })).toBe(true);
|
||||
expect(adapter.supportsScope?.({ scope: "group" })).toBe(true);
|
||||
expect(adapter.supportsScope?.({ scope: "all" })).toBe(true);
|
||||
});
|
||||
|
||||
it("reads dm/group config from the resolved account", () => {
|
||||
expect(adapter.readConfig?.({ cfg: {}, accountId: "alt" })).toEqual({
|
||||
dmAllowFrom: ["dm-owner"],
|
||||
groupAllowFrom: ["group-owner"],
|
||||
dmPolicy: "allowlist",
|
||||
groupPolicy: "allowlist",
|
||||
groupOverrides: [{ label: "room-1", entries: ["member-1"] }],
|
||||
});
|
||||
});
|
||||
|
||||
it("writes group allowlist entries to groupAllowFrom", () => {
|
||||
expect(
|
||||
adapter.applyConfigEdit?.({
|
||||
cfg: {},
|
||||
parsedConfig: {},
|
||||
accountId: "alt",
|
||||
scope: "group",
|
||||
action: "add",
|
||||
entry: " Member-2 ",
|
||||
}),
|
||||
).toEqual({
|
||||
kind: "ok",
|
||||
changed: true,
|
||||
pathLabel: "channels.demo.accounts.alt.groupAllowFrom",
|
||||
writeTarget: {
|
||||
kind: "account",
|
||||
scope: { channelId: "demo", accountId: "alt" },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildLegacyDmAccountAllowlistAdapter", () => {
|
||||
const adapter = buildLegacyDmAccountAllowlistAdapter({
|
||||
channelId: "demo",
|
||||
resolveAccount: ({ accountId }) => ({
|
||||
accountId: accountId ?? "default",
|
||||
dmAllowFrom: ["owner"],
|
||||
groupPolicy: "allowlist",
|
||||
groupOverrides: [{ label: "group-1", entries: ["member-1"] }],
|
||||
}),
|
||||
normalize: ({ values }) => values.map((entry) => String(entry).trim().toLowerCase()),
|
||||
resolveDmAllowFrom: (account) => account.dmAllowFrom,
|
||||
resolveGroupPolicy: (account) => account.groupPolicy,
|
||||
resolveGroupOverrides: (account) => account.groupOverrides,
|
||||
});
|
||||
|
||||
it("supports only dm scope", () => {
|
||||
expect(adapter.supportsScope?.({ scope: "dm" })).toBe(true);
|
||||
expect(adapter.supportsScope?.({ scope: "group" })).toBe(false);
|
||||
expect(adapter.supportsScope?.({ scope: "all" })).toBe(false);
|
||||
});
|
||||
|
||||
it("reads legacy dm config from the resolved account", () => {
|
||||
expect(adapter.readConfig?.({ cfg: {}, accountId: "alt" })).toEqual({
|
||||
dmAllowFrom: ["owner"],
|
||||
groupPolicy: "allowlist",
|
||||
groupOverrides: [{ label: "group-1", entries: ["member-1"] }],
|
||||
});
|
||||
});
|
||||
|
||||
it("writes dm allowlist entries and keeps legacy cleanup behavior", () => {
|
||||
expect(
|
||||
adapter.applyConfigEdit?.({
|
||||
cfg: {},
|
||||
parsedConfig: {
|
||||
channels: {
|
||||
demo: {
|
||||
accounts: {
|
||||
alt: {
|
||||
dm: { allowFrom: ["owner"] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
accountId: "alt",
|
||||
scope: "dm",
|
||||
action: "add",
|
||||
entry: "admin",
|
||||
}),
|
||||
).toEqual({
|
||||
kind: "ok",
|
||||
changed: true,
|
||||
pathLabel: "channels.demo.accounts.alt.allowFrom",
|
||||
writeTarget: {
|
||||
kind: "account",
|
||||
scope: { channelId: "demo", accountId: "alt" },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -11,16 +11,152 @@ type AllowlistConfigPaths = {
|
||||
cleanupPaths?: string[][];
|
||||
};
|
||||
|
||||
export type AllowlistGroupOverride = { label: string; entries: string[] };
|
||||
export type AllowlistNameResolution = Array<{
|
||||
input: string;
|
||||
resolved: boolean;
|
||||
name?: string | null;
|
||||
}>;
|
||||
type AllowlistNormalizer = (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
values: Array<string | number>;
|
||||
}) => string[];
|
||||
type AllowlistAccountResolver<ResolvedAccount> = (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
}) => ResolvedAccount;
|
||||
|
||||
const DM_ALLOWLIST_CONFIG_PATHS: AllowlistConfigPaths = {
|
||||
readPaths: [["allowFrom"]],
|
||||
writePath: ["allowFrom"],
|
||||
};
|
||||
|
||||
const GROUP_ALLOWLIST_CONFIG_PATHS: AllowlistConfigPaths = {
|
||||
readPaths: [["groupAllowFrom"]],
|
||||
writePath: ["groupAllowFrom"],
|
||||
};
|
||||
|
||||
const LEGACY_DM_ALLOWLIST_CONFIG_PATHS: AllowlistConfigPaths = {
|
||||
readPaths: [["allowFrom"], ["dm", "allowFrom"]],
|
||||
writePath: ["allowFrom"],
|
||||
cleanupPaths: [["dm", "allowFrom"]],
|
||||
};
|
||||
|
||||
export function resolveDmGroupAllowlistConfigPaths(scope: "dm" | "group") {
|
||||
return scope === "dm" ? DM_ALLOWLIST_CONFIG_PATHS : GROUP_ALLOWLIST_CONFIG_PATHS;
|
||||
}
|
||||
|
||||
export function resolveLegacyDmAllowlistConfigPaths(scope: "dm" | "group") {
|
||||
return scope === "dm" ? LEGACY_DM_ALLOWLIST_CONFIG_PATHS : null;
|
||||
}
|
||||
|
||||
/** Coerce stored allowlist entries into presentable non-empty strings. */
|
||||
export function readConfiguredAllowlistEntries(
|
||||
entries: Array<string | number> | null | undefined,
|
||||
): string[] {
|
||||
return (entries ?? []).map(String).filter(Boolean);
|
||||
}
|
||||
|
||||
/** Collect labeled allowlist overrides from a flat keyed record. */
|
||||
export function collectAllowlistOverridesFromRecord<T>(params: {
|
||||
record: Record<string, T | undefined> | null | undefined;
|
||||
label: (key: string, value: T) => string;
|
||||
resolveEntries: (value: T) => Array<string | number> | null | undefined;
|
||||
}): AllowlistGroupOverride[] {
|
||||
const overrides: AllowlistGroupOverride[] = [];
|
||||
for (const [key, value] of Object.entries(params.record ?? {})) {
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
const entries = readConfiguredAllowlistEntries(params.resolveEntries(value));
|
||||
if (entries.length === 0) {
|
||||
continue;
|
||||
}
|
||||
overrides.push({ label: params.label(key, value), entries });
|
||||
}
|
||||
return overrides;
|
||||
}
|
||||
|
||||
/** Collect labeled allowlist overrides from an outer record with nested child records. */
|
||||
export function collectNestedAllowlistOverridesFromRecord<Outer, Inner>(params: {
|
||||
record: Record<string, Outer | undefined> | null | undefined;
|
||||
outerLabel: (key: string, value: Outer) => string;
|
||||
resolveOuterEntries: (value: Outer) => Array<string | number> | null | undefined;
|
||||
resolveChildren: (value: Outer) => Record<string, Inner | undefined> | null | undefined;
|
||||
innerLabel: (outerKey: string, innerKey: string, inner: Inner) => string;
|
||||
resolveInnerEntries: (value: Inner) => Array<string | number> | null | undefined;
|
||||
}): AllowlistGroupOverride[] {
|
||||
const overrides: AllowlistGroupOverride[] = [];
|
||||
for (const [outerKey, outerValue] of Object.entries(params.record ?? {})) {
|
||||
if (!outerValue) {
|
||||
continue;
|
||||
}
|
||||
const outerEntries = readConfiguredAllowlistEntries(params.resolveOuterEntries(outerValue));
|
||||
if (outerEntries.length > 0) {
|
||||
overrides.push({ label: params.outerLabel(outerKey, outerValue), entries: outerEntries });
|
||||
}
|
||||
overrides.push(
|
||||
...collectAllowlistOverridesFromRecord({
|
||||
record: params.resolveChildren(outerValue),
|
||||
label: (innerKey, innerValue) => params.innerLabel(outerKey, innerKey, innerValue),
|
||||
resolveEntries: params.resolveInnerEntries,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return overrides;
|
||||
}
|
||||
|
||||
/** Build an account-scoped flat override resolver from a keyed allowlist record. */
|
||||
export function createFlatAllowlistOverrideResolver<ResolvedAccount, Entry>(params: {
|
||||
resolveRecord: (account: ResolvedAccount) => Record<string, Entry | undefined> | null | undefined;
|
||||
label: (key: string, value: Entry) => string;
|
||||
resolveEntries: (value: Entry) => Array<string | number> | null | undefined;
|
||||
}): (account: ResolvedAccount) => AllowlistGroupOverride[] {
|
||||
return (account) =>
|
||||
collectAllowlistOverridesFromRecord({
|
||||
record: params.resolveRecord(account),
|
||||
label: params.label,
|
||||
resolveEntries: params.resolveEntries,
|
||||
});
|
||||
}
|
||||
|
||||
/** Build an account-scoped nested override resolver from hierarchical allowlist records. */
|
||||
export function createNestedAllowlistOverrideResolver<ResolvedAccount, Outer, Inner>(params: {
|
||||
resolveRecord: (account: ResolvedAccount) => Record<string, Outer | undefined> | null | undefined;
|
||||
outerLabel: (key: string, value: Outer) => string;
|
||||
resolveOuterEntries: (value: Outer) => Array<string | number> | null | undefined;
|
||||
resolveChildren: (value: Outer) => Record<string, Inner | undefined> | null | undefined;
|
||||
innerLabel: (outerKey: string, innerKey: string, inner: Inner) => string;
|
||||
resolveInnerEntries: (value: Inner) => Array<string | number> | null | undefined;
|
||||
}): (account: ResolvedAccount) => AllowlistGroupOverride[] {
|
||||
return (account) =>
|
||||
collectNestedAllowlistOverridesFromRecord({
|
||||
record: params.resolveRecord(account),
|
||||
outerLabel: params.outerLabel,
|
||||
resolveOuterEntries: params.resolveOuterEntries,
|
||||
resolveChildren: params.resolveChildren,
|
||||
innerLabel: params.innerLabel,
|
||||
resolveInnerEntries: params.resolveInnerEntries,
|
||||
});
|
||||
}
|
||||
|
||||
/** Build the common account-scoped token-gated allowlist name resolver. */
|
||||
export function createAccountScopedAllowlistNameResolver<ResolvedAccount>(params: {
|
||||
resolveAccount: (params: { cfg: OpenClawConfig; accountId?: string | null }) => ResolvedAccount;
|
||||
resolveToken: (account: ResolvedAccount) => string | null | undefined;
|
||||
resolveNames: (params: { token: string; entries: string[] }) => Promise<AllowlistNameResolution>;
|
||||
}): NonNullable<ChannelAllowlistAdapter["resolveNames"]> {
|
||||
return async ({ cfg, accountId, entries }) => {
|
||||
const account = params.resolveAccount({ cfg, accountId });
|
||||
const token = params.resolveToken(account)?.trim();
|
||||
if (!token) {
|
||||
return [];
|
||||
}
|
||||
return await params.resolveNames({ token, entries });
|
||||
};
|
||||
}
|
||||
|
||||
function resolveAccountScopedWriteTarget(
|
||||
parsed: Record<string, unknown>,
|
||||
channelId: ChannelId,
|
||||
@@ -196,11 +332,7 @@ function applyAccountScopedAllowlistConfigEdit(params: {
|
||||
/** Build the default account-scoped allowlist editor used by channel plugins with config-backed lists. */
|
||||
export function buildAccountScopedAllowlistConfigEditor(params: {
|
||||
channelId: ChannelId;
|
||||
normalize: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
values: Array<string | number>;
|
||||
}) => string[];
|
||||
normalize: AllowlistNormalizer;
|
||||
resolvePaths: (scope: "dm" | "group") => AllowlistConfigPaths | null;
|
||||
}): NonNullable<ChannelAllowlistAdapter["applyConfigEdit"]> {
|
||||
return ({ cfg, parsedConfig, accountId, scope, action, entry }) => {
|
||||
@@ -219,3 +351,75 @@ export function buildAccountScopedAllowlistConfigEditor(params: {
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function buildAccountAllowlistAdapter<ResolvedAccount>(params: {
|
||||
channelId: ChannelId;
|
||||
resolveAccount: AllowlistAccountResolver<ResolvedAccount>;
|
||||
normalize: AllowlistNormalizer;
|
||||
supportsScope: NonNullable<ChannelAllowlistAdapter["supportsScope"]>;
|
||||
resolvePaths: (scope: "dm" | "group") => AllowlistConfigPaths | null;
|
||||
readConfig: (
|
||||
account: ResolvedAccount,
|
||||
) => Awaited<ReturnType<NonNullable<ChannelAllowlistAdapter["readConfig"]>>>;
|
||||
}): Pick<ChannelAllowlistAdapter, "supportsScope" | "readConfig" | "applyConfigEdit"> {
|
||||
return {
|
||||
supportsScope: params.supportsScope,
|
||||
readConfig: ({ cfg, accountId }) =>
|
||||
params.readConfig(params.resolveAccount({ cfg, accountId })),
|
||||
applyConfigEdit: buildAccountScopedAllowlistConfigEditor({
|
||||
channelId: params.channelId,
|
||||
normalize: params.normalize,
|
||||
resolvePaths: params.resolvePaths,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/** Build the common DM/group allowlist adapter used by channels that store both lists in config. */
|
||||
export function buildDmGroupAccountAllowlistAdapter<ResolvedAccount>(params: {
|
||||
channelId: ChannelId;
|
||||
resolveAccount: AllowlistAccountResolver<ResolvedAccount>;
|
||||
normalize: AllowlistNormalizer;
|
||||
resolveDmAllowFrom: (account: ResolvedAccount) => Array<string | number> | null | undefined;
|
||||
resolveGroupAllowFrom: (account: ResolvedAccount) => Array<string | number> | null | undefined;
|
||||
resolveDmPolicy?: (account: ResolvedAccount) => string | null | undefined;
|
||||
resolveGroupPolicy?: (account: ResolvedAccount) => string | null | undefined;
|
||||
resolveGroupOverrides?: (account: ResolvedAccount) => AllowlistGroupOverride[] | undefined;
|
||||
}): Pick<ChannelAllowlistAdapter, "supportsScope" | "readConfig" | "applyConfigEdit"> {
|
||||
return buildAccountAllowlistAdapter({
|
||||
channelId: params.channelId,
|
||||
resolveAccount: params.resolveAccount,
|
||||
normalize: params.normalize,
|
||||
supportsScope: ({ scope }) => scope === "dm" || scope === "group" || scope === "all",
|
||||
resolvePaths: resolveDmGroupAllowlistConfigPaths,
|
||||
readConfig: (account) => ({
|
||||
dmAllowFrom: readConfiguredAllowlistEntries(params.resolveDmAllowFrom(account)),
|
||||
groupAllowFrom: readConfiguredAllowlistEntries(params.resolveGroupAllowFrom(account)),
|
||||
dmPolicy: params.resolveDmPolicy?.(account) ?? undefined,
|
||||
groupPolicy: params.resolveGroupPolicy?.(account) ?? undefined,
|
||||
groupOverrides: params.resolveGroupOverrides?.(account),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
/** Build the common DM-only allowlist adapter for channels with legacy dm.allowFrom fallback paths. */
|
||||
export function buildLegacyDmAccountAllowlistAdapter<ResolvedAccount>(params: {
|
||||
channelId: ChannelId;
|
||||
resolveAccount: AllowlistAccountResolver<ResolvedAccount>;
|
||||
normalize: AllowlistNormalizer;
|
||||
resolveDmAllowFrom: (account: ResolvedAccount) => Array<string | number> | null | undefined;
|
||||
resolveGroupPolicy?: (account: ResolvedAccount) => string | null | undefined;
|
||||
resolveGroupOverrides?: (account: ResolvedAccount) => AllowlistGroupOverride[] | undefined;
|
||||
}): Pick<ChannelAllowlistAdapter, "supportsScope" | "readConfig" | "applyConfigEdit"> {
|
||||
return buildAccountAllowlistAdapter({
|
||||
channelId: params.channelId,
|
||||
resolveAccount: params.resolveAccount,
|
||||
normalize: params.normalize,
|
||||
supportsScope: ({ scope }) => scope === "dm",
|
||||
resolvePaths: resolveLegacyDmAllowlistConfigPaths,
|
||||
readConfig: (account) => ({
|
||||
dmAllowFrom: readConfiguredAllowlistEntries(params.resolveDmAllowFrom(account)),
|
||||
groupPolicy: params.resolveGroupPolicy?.(account) ?? undefined,
|
||||
groupOverrides: params.resolveGroupOverrides?.(account),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,15 @@ export type {
|
||||
} from "../config/types.tools.js";
|
||||
export {
|
||||
buildOpenGroupPolicyConfigureRouteAllowlistWarning,
|
||||
composeWarningCollectors,
|
||||
createAllowlistProviderGroupPolicyWarningCollector,
|
||||
createConditionalWarningCollector,
|
||||
createAllowlistProviderOpenWarningCollector,
|
||||
createAllowlistProviderRestrictSendersWarningCollector,
|
||||
createAllowlistProviderRouteAllowlistWarningCollector,
|
||||
createOpenGroupPolicyRestrictSendersWarningCollector,
|
||||
createOpenProviderGroupPolicyWarningCollector,
|
||||
createOpenProviderConfiguredRouteWarningCollector,
|
||||
buildOpenGroupPolicyRestrictSendersWarning,
|
||||
buildOpenGroupPolicyWarning,
|
||||
collectAllowlistProviderGroupPolicyWarnings,
|
||||
@@ -12,6 +21,7 @@ export {
|
||||
collectOpenGroupPolicyRestrictSendersWarnings,
|
||||
collectOpenGroupPolicyRouteAllowlistWarnings,
|
||||
collectOpenProviderGroupPolicyWarnings,
|
||||
projectWarningCollector,
|
||||
} from "../channels/plugins/group-policy-warnings.js";
|
||||
export { buildAccountScopedDmSecurityPolicy } from "../channels/plugins/helpers.js";
|
||||
export {
|
||||
|
||||
@@ -32,12 +32,16 @@ export * from "../channels/plugins/actions/reaction-message-id.js";
|
||||
export * from "../channels/plugins/actions/shared.js";
|
||||
export type * from "../channels/plugins/types.js";
|
||||
export * from "../channels/plugins/config-writes.js";
|
||||
export * from "../channels/plugins/directory-adapters.js";
|
||||
export * from "../channels/plugins/media-payload.js";
|
||||
export * from "../channels/plugins/message-tool-schema.js";
|
||||
export * from "../channels/plugins/normalize/signal.js";
|
||||
export * from "../channels/plugins/normalize/whatsapp.js";
|
||||
export * from "../channels/plugins/outbound/direct-text-media.js";
|
||||
export * from "../channels/plugins/outbound/interactive.js";
|
||||
export * from "../channels/plugins/pairing-adapters.js";
|
||||
export * from "../channels/plugins/runtime-forwarders.js";
|
||||
export * from "../channels/plugins/target-resolvers.js";
|
||||
export * from "../channels/plugins/status-issues/shared.js";
|
||||
export * from "../channels/plugins/whatsapp-heartbeat.js";
|
||||
export * from "../infra/outbound/send-deps.js";
|
||||
|
||||
@@ -4,8 +4,13 @@ export type { ReadOnlyInspectedAccount } from "../channels/read-only-account-ins
|
||||
export {
|
||||
applyDirectoryQueryAndLimit,
|
||||
collectNormalizedDirectoryIds,
|
||||
listDirectoryEntriesFromSources,
|
||||
listDirectoryGroupEntriesFromMapKeys,
|
||||
listDirectoryGroupEntriesFromMapKeysAndAllowFrom,
|
||||
listInspectedDirectoryEntriesFromSources,
|
||||
listResolvedDirectoryEntriesFromSources,
|
||||
listResolvedDirectoryGroupEntriesFromMapKeys,
|
||||
listResolvedDirectoryUserEntriesFromAllowFrom,
|
||||
listDirectoryUserEntriesFromAllowFrom,
|
||||
listDirectoryUserEntriesFromAllowFromAndMapKeys,
|
||||
toDirectoryEntries,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as channelRuntimeSdk from "openclaw/plugin-sdk/channel-runtime";
|
||||
import * as compatSdk from "openclaw/plugin-sdk/compat";
|
||||
import * as coreSdk from "openclaw/plugin-sdk/core";
|
||||
import type {
|
||||
@@ -5,6 +6,7 @@ import type {
|
||||
OpenClawPluginApi as CoreOpenClawPluginApi,
|
||||
PluginRuntime as CorePluginRuntime,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import * as directoryRuntimeSdk from "openclaw/plugin-sdk/directory-runtime";
|
||||
import * as discordSdk from "openclaw/plugin-sdk/discord";
|
||||
import * as imessageSdk from "openclaw/plugin-sdk/imessage";
|
||||
import * as lazyRuntimeSdk from "openclaw/plugin-sdk/lazy-runtime";
|
||||
@@ -58,6 +60,7 @@ const mattermostSdk = await import("openclaw/plugin-sdk/mattermost");
|
||||
const nextcloudTalkSdk = await import("openclaw/plugin-sdk/nextcloud-talk");
|
||||
const twitchSdk = await import("openclaw/plugin-sdk/twitch");
|
||||
const accountHelpersSdk = await import("openclaw/plugin-sdk/account-helpers");
|
||||
const allowlistEditSdk = await import("openclaw/plugin-sdk/allowlist-config-edit");
|
||||
const lobsterSdk = await import("openclaw/plugin-sdk/lobster");
|
||||
|
||||
describe("plugin-sdk subpath exports", () => {
|
||||
@@ -94,10 +97,42 @@ describe("plugin-sdk subpath exports", () => {
|
||||
expect(typeof accountHelpersSdk.createAccountListHelpers).toBe("function");
|
||||
});
|
||||
|
||||
it("exports allowlist edit helpers from the dedicated subpath", () => {
|
||||
expect(typeof allowlistEditSdk.buildDmGroupAccountAllowlistAdapter).toBe("function");
|
||||
expect(typeof allowlistEditSdk.buildLegacyDmAccountAllowlistAdapter).toBe("function");
|
||||
expect(typeof allowlistEditSdk.createAccountScopedAllowlistNameResolver).toBe("function");
|
||||
expect(typeof allowlistEditSdk.createFlatAllowlistOverrideResolver).toBe("function");
|
||||
expect(typeof allowlistEditSdk.createNestedAllowlistOverrideResolver).toBe("function");
|
||||
});
|
||||
|
||||
it("exports runtime helpers from the dedicated subpath", () => {
|
||||
expect(typeof runtimeSdk.createLoggerBackedRuntime).toBe("function");
|
||||
});
|
||||
|
||||
it("exports directory runtime helpers from the dedicated subpath", () => {
|
||||
expect(typeof directoryRuntimeSdk.listDirectoryEntriesFromSources).toBe("function");
|
||||
expect(typeof directoryRuntimeSdk.listInspectedDirectoryEntriesFromSources).toBe("function");
|
||||
expect(typeof directoryRuntimeSdk.listResolvedDirectoryEntriesFromSources).toBe("function");
|
||||
expect(typeof directoryRuntimeSdk.listResolvedDirectoryGroupEntriesFromMapKeys).toBe(
|
||||
"function",
|
||||
);
|
||||
expect(typeof directoryRuntimeSdk.listResolvedDirectoryUserEntriesFromAllowFrom).toBe(
|
||||
"function",
|
||||
);
|
||||
});
|
||||
|
||||
it("exports channel runtime helpers from the dedicated subpath", () => {
|
||||
expect(typeof channelRuntimeSdk.buildUnresolvedTargetResults).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.createChannelDirectoryAdapter).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.createEmptyChannelDirectoryAdapter).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.createLoggedPairingApprovalNotifier).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.createPairingPrefixStripper).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.createRuntimeDirectoryLiveAdapter).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.createRuntimeOutboundDelegates).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.resolveTargetsWithOptionalToken).toBe("function");
|
||||
expect(typeof channelRuntimeSdk.createTextPairingAdapter).toBe("function");
|
||||
});
|
||||
|
||||
it("exports provider setup helpers from the dedicated subpath", () => {
|
||||
expect(typeof providerSetupSdk.buildVllmProvider).toBe("function");
|
||||
expect(typeof providerSetupSdk.discoverOpenAICompatibleSelfHostedProvider).toBe("function");
|
||||
|
||||
Reference in New Issue
Block a user