mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 17:10:25 +00:00
refactor: move group access into setup wizard
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import { configureChannelAccessWithAllowlist } from "./channel-access-configure.js";
|
||||
import type { ChannelAccessPolicy } from "./channel-access.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { configureChannelAccessWithAllowlist } from "./setup-group-access-configure.js";
|
||||
import type { ChannelAccessPolicy } from "./setup-group-access.js";
|
||||
|
||||
function createPrompter(params: { confirm: boolean; policy?: ChannelAccessPolicy; text?: string }) {
|
||||
return {
|
||||
@@ -89,6 +89,41 @@ describe("configureChannelAccessWithAllowlist", () => {
|
||||
expect(applyAllowlist).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("supports allowlist policies without prompting for entries", async () => {
|
||||
const cfg: OpenClawConfig = {};
|
||||
const prompter = createPrompter({
|
||||
confirm: true,
|
||||
policy: "allowlist",
|
||||
});
|
||||
const setPolicy = vi.fn(
|
||||
(next: OpenClawConfig, policy: ChannelAccessPolicy): OpenClawConfig => ({
|
||||
...next,
|
||||
channels: { twitch: { groupPolicy: policy } },
|
||||
}),
|
||||
);
|
||||
const resolveAllowlist = vi.fn(async () => ["ignored"]);
|
||||
const applyAllowlist = vi.fn((params: { cfg: OpenClawConfig }) => params.cfg);
|
||||
|
||||
const next = await configureChannelAccessWithAllowlist({
|
||||
cfg,
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
prompter: prompter as any,
|
||||
label: "Twitch chat",
|
||||
currentPolicy: "disabled",
|
||||
currentEntries: [],
|
||||
placeholder: "",
|
||||
updatePrompt: false,
|
||||
skipAllowlistEntries: true,
|
||||
setPolicy,
|
||||
resolveAllowlist,
|
||||
applyAllowlist,
|
||||
});
|
||||
|
||||
expect(next.channels).toEqual({ twitch: { groupPolicy: "allowlist" } });
|
||||
expect(resolveAllowlist).not.toHaveBeenCalled();
|
||||
expect(applyAllowlist).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("resolves allowlist entries and applies them after forcing allowlist policy", async () => {
|
||||
const cfg: OpenClawConfig = {};
|
||||
const prompter = createPrompter({
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
||||
import { promptChannelAccessConfig, type ChannelAccessPolicy } from "./channel-access.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { WizardPrompter } from "../../wizard/prompts.js";
|
||||
import { promptChannelAccessConfig, type ChannelAccessPolicy } from "./setup-group-access.js";
|
||||
|
||||
export async function configureChannelAccessWithAllowlist<TResolved>(params: {
|
||||
cfg: OpenClawConfig;
|
||||
@@ -10,9 +10,10 @@ export async function configureChannelAccessWithAllowlist<TResolved>(params: {
|
||||
currentEntries: string[];
|
||||
placeholder: string;
|
||||
updatePrompt: boolean;
|
||||
skipAllowlistEntries?: boolean;
|
||||
setPolicy: (cfg: OpenClawConfig, policy: ChannelAccessPolicy) => OpenClawConfig;
|
||||
resolveAllowlist: (params: { cfg: OpenClawConfig; entries: string[] }) => Promise<TResolved>;
|
||||
applyAllowlist: (params: { cfg: OpenClawConfig; resolved: TResolved }) => OpenClawConfig;
|
||||
resolveAllowlist?: (params: { cfg: OpenClawConfig; entries: string[] }) => Promise<TResolved>;
|
||||
applyAllowlist?: (params: { cfg: OpenClawConfig; resolved: TResolved }) => OpenClawConfig;
|
||||
}): Promise<OpenClawConfig> {
|
||||
let next = params.cfg;
|
||||
const accessConfig = await promptChannelAccessConfig({
|
||||
@@ -22,6 +23,7 @@ export async function configureChannelAccessWithAllowlist<TResolved>(params: {
|
||||
currentEntries: params.currentEntries,
|
||||
placeholder: params.placeholder,
|
||||
updatePrompt: params.updatePrompt,
|
||||
skipAllowlistEntries: params.skipAllowlistEntries,
|
||||
});
|
||||
if (!accessConfig) {
|
||||
return next;
|
||||
@@ -29,6 +31,9 @@ export async function configureChannelAccessWithAllowlist<TResolved>(params: {
|
||||
if (accessConfig.policy !== "allowlist") {
|
||||
return params.setPolicy(next, accessConfig.policy);
|
||||
}
|
||||
if (params.skipAllowlistEntries || !params.resolveAllowlist || !params.applyAllowlist) {
|
||||
return params.setPolicy(next, "allowlist");
|
||||
}
|
||||
const resolved = await params.resolveAllowlist({
|
||||
cfg: next,
|
||||
entries: accessConfig.entries,
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
promptChannelAccessConfig,
|
||||
promptChannelAllowlist,
|
||||
promptChannelAccessPolicy,
|
||||
} from "./channel-access.js";
|
||||
} from "./setup-group-access.js";
|
||||
|
||||
function createPrompter(params?: {
|
||||
confirm?: (options: { message: string; initialValue: boolean }) => Promise<boolean>;
|
||||
@@ -83,6 +83,27 @@ describe("promptChannelAccessPolicy", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("promptChannelAccessConfig", () => {
|
||||
it("skips the allowlist text prompt when entries are policy-only", async () => {
|
||||
const prompter = createPrompter({
|
||||
confirm: async () => true,
|
||||
select: async () => "allowlist",
|
||||
text: async () => {
|
||||
throw new Error("text prompt should not run");
|
||||
},
|
||||
});
|
||||
|
||||
const result = await promptChannelAccessConfig({
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
prompter: prompter as any,
|
||||
label: "Twitch chat",
|
||||
skipAllowlistEntries: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ policy: "allowlist", entries: [] });
|
||||
});
|
||||
});
|
||||
|
||||
describe("promptChannelAccessConfig", () => {
|
||||
it("returns null when user skips configuration", async () => {
|
||||
const prompter = createPrompter({
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
||||
import { splitOnboardingEntries } from "./helpers.js";
|
||||
import type { WizardPrompter } from "../../wizard/prompts.js";
|
||||
import { splitOnboardingEntries } from "./onboarding/helpers.js";
|
||||
|
||||
export type ChannelAccessPolicy = "allowlist" | "open" | "disabled";
|
||||
|
||||
@@ -64,6 +64,7 @@ export async function promptChannelAccessConfig(params: {
|
||||
placeholder?: string;
|
||||
allowOpen?: boolean;
|
||||
allowDisabled?: boolean;
|
||||
skipAllowlistEntries?: boolean;
|
||||
defaultPrompt?: boolean;
|
||||
updatePrompt?: boolean;
|
||||
}): Promise<{ policy: ChannelAccessPolicy; entries: string[] } | null> {
|
||||
@@ -88,6 +89,9 @@ export async function promptChannelAccessConfig(params: {
|
||||
if (policy !== "allowlist") {
|
||||
return { policy, entries: [] };
|
||||
}
|
||||
if (params.skipAllowlistEntries) {
|
||||
return { policy, entries: [] };
|
||||
}
|
||||
const entries = await promptChannelAllowlist({
|
||||
prompter: params.prompter,
|
||||
label: params.label,
|
||||
@@ -8,14 +8,14 @@ import type {
|
||||
ChannelOnboardingStatus,
|
||||
ChannelOnboardingStatusContext,
|
||||
} from "./onboarding-types.js";
|
||||
import { configureChannelAccessWithAllowlist } from "./onboarding/channel-access-configure.js";
|
||||
import type { ChannelAccessPolicy } from "./onboarding/channel-access.js";
|
||||
import {
|
||||
promptResolvedAllowFrom,
|
||||
resolveAccountIdForConfigure,
|
||||
runSingleChannelSecretStep,
|
||||
splitOnboardingEntries,
|
||||
} from "./onboarding/helpers.js";
|
||||
import { configureChannelAccessWithAllowlist } from "./setup-group-access-configure.js";
|
||||
import type { ChannelAccessPolicy } from "./setup-group-access.js";
|
||||
import type { ChannelSetupInput } from "./types.core.js";
|
||||
import type { ChannelPlugin } from "./types.js";
|
||||
|
||||
@@ -184,6 +184,7 @@ export type ChannelSetupWizardGroupAccess = {
|
||||
placeholder: string;
|
||||
helpTitle?: string;
|
||||
helpLines?: string[];
|
||||
skipAllowlistEntries?: boolean;
|
||||
currentPolicy: (params: { cfg: OpenClawConfig; accountId: string }) => ChannelAccessPolicy;
|
||||
currentEntries: (params: { cfg: OpenClawConfig; accountId: string }) => string[];
|
||||
updatePrompt: (params: { cfg: OpenClawConfig; accountId: string }) => boolean;
|
||||
@@ -192,14 +193,14 @@ export type ChannelSetupWizardGroupAccess = {
|
||||
accountId: string;
|
||||
policy: ChannelAccessPolicy;
|
||||
}) => OpenClawConfig;
|
||||
resolveAllowlist: (params: {
|
||||
resolveAllowlist?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId: string;
|
||||
credentialValues: ChannelSetupWizardCredentialValues;
|
||||
entries: string[];
|
||||
prompter: Pick<WizardPrompter, "note">;
|
||||
}) => Promise<unknown>;
|
||||
applyAllowlist: (params: {
|
||||
applyAllowlist?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId: string;
|
||||
resolved: unknown;
|
||||
@@ -757,26 +758,31 @@ export function buildChannelOnboardingAdapterFromSetupWizard(params: {
|
||||
currentEntries: access.currentEntries({ cfg: next, accountId }),
|
||||
placeholder: access.placeholder,
|
||||
updatePrompt: access.updatePrompt({ cfg: next, accountId }),
|
||||
skipAllowlistEntries: access.skipAllowlistEntries,
|
||||
setPolicy: (currentCfg, policy) =>
|
||||
access.setPolicy({
|
||||
cfg: currentCfg,
|
||||
accountId,
|
||||
policy,
|
||||
}),
|
||||
resolveAllowlist: async ({ cfg: currentCfg, entries }) =>
|
||||
await access.resolveAllowlist({
|
||||
cfg: currentCfg,
|
||||
accountId,
|
||||
credentialValues,
|
||||
entries,
|
||||
prompter,
|
||||
}),
|
||||
applyAllowlist: ({ cfg: currentCfg, resolved }) =>
|
||||
access.applyAllowlist({
|
||||
cfg: currentCfg,
|
||||
accountId,
|
||||
resolved,
|
||||
}),
|
||||
resolveAllowlist: access.resolveAllowlist
|
||||
? async ({ cfg: currentCfg, entries }) =>
|
||||
await access.resolveAllowlist!({
|
||||
cfg: currentCfg,
|
||||
accountId,
|
||||
credentialValues,
|
||||
entries,
|
||||
prompter,
|
||||
})
|
||||
: undefined,
|
||||
applyAllowlist: access.applyAllowlist
|
||||
? ({ cfg: currentCfg, resolved }) =>
|
||||
access.applyAllowlist!({
|
||||
cfg: currentCfg,
|
||||
accountId,
|
||||
resolved,
|
||||
})
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ export { resolveChannelMediaMaxBytes } from "../channels/plugins/media-limits.js
|
||||
export {
|
||||
addWildcardAllowFrom,
|
||||
mergeAllowFromEntries,
|
||||
promptAccountId,
|
||||
splitOnboardingEntries,
|
||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||
} from "../channels/plugins/onboarding/helpers.js";
|
||||
|
||||
@@ -15,7 +15,6 @@ export {
|
||||
} from "../channels/plugins/helpers.js";
|
||||
export {
|
||||
addWildcardAllowFrom,
|
||||
promptAccountId,
|
||||
setTopLevelChannelAllowFrom,
|
||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||
} from "../channels/plugins/onboarding/helpers.js";
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
export type { ReplyPayload } from "../auto-reply/types.js";
|
||||
export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
export { promptAccountId } from "../channels/plugins/onboarding/helpers.js";
|
||||
export {
|
||||
applyAccountNameToChannelSection,
|
||||
patchScopedAccountConfig,
|
||||
|
||||
@@ -15,7 +15,6 @@ export {
|
||||
buildSingleChannelSecretPromptState,
|
||||
addWildcardAllowFrom,
|
||||
mergeAllowFromEntries,
|
||||
promptAccountId,
|
||||
promptSingleChannelSecretInput,
|
||||
runSingleChannelSecretStep,
|
||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||
|
||||
@@ -14,7 +14,6 @@ export { formatPairingApproveHint } from "../channels/plugins/helpers.js";
|
||||
export {
|
||||
addWildcardAllowFrom,
|
||||
mergeAllowFromEntries,
|
||||
promptAccountId,
|
||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||
} from "../channels/plugins/onboarding/helpers.js";
|
||||
export {
|
||||
|
||||
Reference in New Issue
Block a user