refactor: move group access into setup wizard

This commit is contained in:
Peter Steinberger
2026-03-15 19:52:17 -07:00
parent d8e138c743
commit 84c0326f4d
15 changed files with 305 additions and 217 deletions

View File

@@ -251,7 +251,7 @@ export function createDiscordSetupWizardProxy(
prompter: { note: (message: string, title?: string) => Promise<void> };
}) => {
const wizard = (await loadWizard()).discordSetupWizard;
if (!wizard.groupAccess) {
if (!wizard.groupAccess?.resolveAllowlist) {
return entries.map((input) => ({ input, resolved: false }));
}
try {

View File

@@ -1,5 +1,4 @@
import type { ChannelOnboardingDmPolicy } from "../../../src/channels/plugins/onboarding-types.js";
import { promptChannelAccessConfig } from "../../../src/channels/plugins/onboarding/channel-access.js";
import {
addWildcardAllowFrom,
buildSingleChannelSecretPromptState,
@@ -171,6 +170,78 @@ function setMatrixGroupRooms(cfg: CoreConfig, roomKeys: string[]) {
};
}
async function resolveMatrixGroupRooms(params: {
cfg: CoreConfig;
entries: string[];
prompter: Pick<WizardPrompter, "note">;
}): Promise<string[]> {
if (params.entries.length === 0) {
return [];
}
try {
const resolvedIds: string[] = [];
const unresolved: string[] = [];
for (const entry of params.entries) {
const trimmed = entry.trim();
if (!trimmed) {
continue;
}
const cleaned = trimmed.replace(/^(room|channel):/i, "").trim();
if (cleaned.startsWith("!") && cleaned.includes(":")) {
resolvedIds.push(cleaned);
continue;
}
const matches = await listMatrixDirectoryGroupsLive({
cfg: params.cfg,
query: trimmed,
limit: 10,
});
const exact = matches.find(
(match) => (match.name ?? "").toLowerCase() === trimmed.toLowerCase(),
);
const best = exact ?? matches[0];
if (best?.id) {
resolvedIds.push(best.id);
} else {
unresolved.push(entry);
}
}
const roomKeys = [...resolvedIds, ...unresolved.map((entry) => entry.trim()).filter(Boolean)];
const resolution = formatResolvedUnresolvedNote({
resolved: resolvedIds,
unresolved,
});
if (resolution) {
await params.prompter.note(resolution, "Matrix rooms");
}
return roomKeys;
} catch (err) {
await params.prompter.note(
`Room lookup failed; keeping entries as typed. ${String(err)}`,
"Matrix rooms",
);
return params.entries.map((entry) => entry.trim()).filter(Boolean);
}
}
const matrixGroupAccess: NonNullable<ChannelSetupWizard["groupAccess"]> = {
label: "Matrix rooms",
placeholder: "!roomId:server, #alias:server, Project Room",
currentPolicy: ({ cfg }) => cfg.channels?.matrix?.groupPolicy ?? "allowlist",
currentEntries: ({ cfg }) =>
Object.keys(cfg.channels?.matrix?.groups ?? cfg.channels?.matrix?.rooms ?? {}),
updatePrompt: ({ cfg }) => Boolean(cfg.channels?.matrix?.groups ?? cfg.channels?.matrix?.rooms),
setPolicy: ({ cfg, policy }) => setMatrixGroupPolicy(cfg as CoreConfig, policy),
resolveAllowlist: async ({ cfg, entries, prompter }) =>
await resolveMatrixGroupRooms({
cfg: cfg as CoreConfig,
entries,
prompter,
}),
applyAllowlist: ({ cfg, resolved }) =>
setMatrixGroupRooms(cfg as CoreConfig, resolved as string[]),
};
const matrixDmPolicy: ChannelOnboardingDmPolicy = {
label: "Matrix",
channel,
@@ -386,72 +457,10 @@ export const matrixSetupWizard: ChannelSetupWizard = {
next = await promptMatrixAllowFrom({ cfg: next, prompter });
}
const existingGroups = next.channels?.matrix?.groups ?? next.channels?.matrix?.rooms;
const accessConfig = await promptChannelAccessConfig({
prompter,
label: "Matrix rooms",
currentPolicy: next.channels?.matrix?.groupPolicy ?? "allowlist",
currentEntries: Object.keys(existingGroups ?? {}),
placeholder: "!roomId:server, #alias:server, Project Room",
updatePrompt: Boolean(existingGroups),
});
if (accessConfig) {
if (accessConfig.policy !== "allowlist") {
next = setMatrixGroupPolicy(next, accessConfig.policy);
} else {
let roomKeys = accessConfig.entries;
if (accessConfig.entries.length > 0) {
try {
const resolvedIds: string[] = [];
const unresolved: string[] = [];
for (const entry of accessConfig.entries) {
const trimmed = entry.trim();
if (!trimmed) {
continue;
}
const cleaned = trimmed.replace(/^(room|channel):/i, "").trim();
if (cleaned.startsWith("!") && cleaned.includes(":")) {
resolvedIds.push(cleaned);
continue;
}
const matches = await listMatrixDirectoryGroupsLive({
cfg: next,
query: trimmed,
limit: 10,
});
const exact = matches.find(
(match) => (match.name ?? "").toLowerCase() === trimmed.toLowerCase(),
);
const best = exact ?? matches[0];
if (best?.id) {
resolvedIds.push(best.id);
} else {
unresolved.push(entry);
}
}
roomKeys = [...resolvedIds, ...unresolved.map((entry) => entry.trim()).filter(Boolean)];
const resolution = formatResolvedUnresolvedNote({
resolved: resolvedIds,
unresolved,
});
if (resolution) {
await prompter.note(resolution, "Matrix rooms");
}
} catch (err) {
await prompter.note(
`Room lookup failed; keeping entries as typed. ${String(err)}`,
"Matrix rooms",
);
}
}
next = setMatrixGroupPolicy(next, "allowlist");
next = setMatrixGroupRooms(next, roomKeys);
}
}
return { cfg: next };
},
dmPolicy: matrixDmPolicy,
groupAccess: matrixGroupAccess,
disable: (cfg) => ({
...(cfg as CoreConfig),
channels: {

View File

@@ -1,5 +1,4 @@
import type { ChannelOnboardingDmPolicy } from "../../../src/channels/plugins/onboarding-types.js";
import { promptChannelAccessConfig } from "../../../src/channels/plugins/onboarding/channel-access.js";
import {
mergeAllowFromEntries,
setTopLevelChannelAllowFrom,
@@ -191,6 +190,96 @@ function setMSTeamsTeamsAllowlist(
};
}
function listMSTeamsGroupEntries(cfg: OpenClawConfig): string[] {
return Object.entries(cfg.channels?.msteams?.teams ?? {}).flatMap(([teamKey, value]) => {
const channels = value?.channels ?? {};
const channelKeys = Object.keys(channels);
if (channelKeys.length === 0) {
return [teamKey];
}
return channelKeys.map((channelKey) => `${teamKey}/${channelKey}`);
});
}
async function resolveMSTeamsGroupAllowlist(params: {
cfg: OpenClawConfig;
entries: string[];
prompter: Pick<WizardPrompter, "note">;
}): Promise<Array<{ teamKey: string; channelKey?: string }>> {
let resolvedEntries = params.entries
.map((entry) => parseMSTeamsTeamEntry(entry))
.filter(Boolean) as Array<{ teamKey: string; channelKey?: string }>;
if (params.entries.length === 0 || !resolveMSTeamsCredentials(params.cfg.channels?.msteams)) {
return resolvedEntries;
}
try {
const lookups = await resolveMSTeamsChannelAllowlist({
cfg: params.cfg,
entries: params.entries,
});
const resolvedChannels = lookups.filter(
(entry) => entry.resolved && entry.teamId && entry.channelId,
);
const resolvedTeams = lookups.filter(
(entry) => entry.resolved && entry.teamId && !entry.channelId,
);
const unresolved = lookups.filter((entry) => !entry.resolved).map((entry) => entry.input);
resolvedEntries = [
...resolvedChannels.map((entry) => ({
teamKey: entry.teamId as string,
channelKey: entry.channelId as string,
})),
...resolvedTeams.map((entry) => ({
teamKey: entry.teamId as string,
})),
...unresolved.map((entry) => parseMSTeamsTeamEntry(entry)).filter(Boolean),
] as Array<{ teamKey: string; channelKey?: string }>;
const summary: string[] = [];
if (resolvedChannels.length > 0) {
summary.push(
`Resolved channels: ${resolvedChannels
.map((entry) => entry.channelId)
.filter(Boolean)
.join(", ")}`,
);
}
if (resolvedTeams.length > 0) {
summary.push(
`Resolved teams: ${resolvedTeams
.map((entry) => entry.teamId)
.filter(Boolean)
.join(", ")}`,
);
}
if (unresolved.length > 0) {
summary.push(`Unresolved (kept as typed): ${unresolved.join(", ")}`);
}
if (summary.length > 0) {
await params.prompter.note(summary.join("\n"), "MS Teams channels");
}
return resolvedEntries;
} catch (err) {
await params.prompter.note(
`Channel lookup failed; keeping entries as typed. ${String(err)}`,
"MS Teams channels",
);
return resolvedEntries;
}
}
const msteamsGroupAccess: NonNullable<ChannelSetupWizard["groupAccess"]> = {
label: "MS Teams channels",
placeholder: "Team Name/Channel Name, teamId/conversationId",
currentPolicy: ({ cfg }) => cfg.channels?.msteams?.groupPolicy ?? "allowlist",
currentEntries: ({ cfg }) => listMSTeamsGroupEntries(cfg),
updatePrompt: ({ cfg }) => Boolean(cfg.channels?.msteams?.teams),
setPolicy: ({ cfg, policy }) => setMSTeamsGroupPolicy(cfg, policy),
resolveAllowlist: async ({ cfg, entries, prompter }) =>
await resolveMSTeamsGroupAllowlist({ cfg, entries, prompter }),
applyAllowlist: ({ cfg, resolved }) =>
setMSTeamsTeamsAllowlist(cfg, resolved as Array<{ teamKey: string; channelKey?: string }>),
};
const msteamsDmPolicy: ChannelOnboardingDmPolicy = {
label: "MS Teams",
channel,
@@ -290,96 +379,10 @@ export const msteamsSetupWizard: ChannelSetupWizard = {
};
}
const currentEntries = Object.entries(next.channels?.msteams?.teams ?? {}).flatMap(
([teamKey, value]) => {
const channels = value?.channels ?? {};
const channelKeys = Object.keys(channels);
if (channelKeys.length === 0) {
return [teamKey];
}
return channelKeys.map((channelKey) => `${teamKey}/${channelKey}`);
},
);
const accessConfig = await promptChannelAccessConfig({
prompter,
label: "MS Teams channels",
currentPolicy: next.channels?.msteams?.groupPolicy ?? "allowlist",
currentEntries,
placeholder: "Team Name/Channel Name, teamId/conversationId",
updatePrompt: Boolean(next.channels?.msteams?.teams),
});
if (accessConfig) {
if (accessConfig.policy !== "allowlist") {
next = setMSTeamsGroupPolicy(next, accessConfig.policy);
} else {
let entries = accessConfig.entries
.map((entry) => parseMSTeamsTeamEntry(entry))
.filter(Boolean) as Array<{ teamKey: string; channelKey?: string }>;
if (accessConfig.entries.length > 0 && resolveMSTeamsCredentials(next.channels?.msteams)) {
try {
const resolvedEntries = await resolveMSTeamsChannelAllowlist({
cfg: next,
entries: accessConfig.entries,
});
const resolvedChannels = resolvedEntries.filter(
(entry) => entry.resolved && entry.teamId && entry.channelId,
);
const resolvedTeams = resolvedEntries.filter(
(entry) => entry.resolved && entry.teamId && !entry.channelId,
);
const unresolved = resolvedEntries
.filter((entry) => !entry.resolved)
.map((entry) => entry.input);
entries = [
...resolvedChannels.map((entry) => ({
teamKey: entry.teamId as string,
channelKey: entry.channelId as string,
})),
...resolvedTeams.map((entry) => ({
teamKey: entry.teamId as string,
})),
...unresolved.map((entry) => parseMSTeamsTeamEntry(entry)).filter(Boolean),
] as Array<{ teamKey: string; channelKey?: string }>;
if (resolvedChannels.length > 0 || resolvedTeams.length > 0 || unresolved.length > 0) {
const summary: string[] = [];
if (resolvedChannels.length > 0) {
summary.push(
`Resolved channels: ${resolvedChannels
.map((entry) => entry.channelId)
.filter(Boolean)
.join(", ")}`,
);
}
if (resolvedTeams.length > 0) {
summary.push(
`Resolved teams: ${resolvedTeams
.map((entry) => entry.teamId)
.filter(Boolean)
.join(", ")}`,
);
}
if (unresolved.length > 0) {
summary.push(`Unresolved (kept as typed): ${unresolved.join(", ")}`);
}
await prompter.note(summary.join("\n"), "MS Teams channels");
}
} catch (err) {
await prompter.note(
`Channel lookup failed; keeping entries as typed. ${String(err)}`,
"MS Teams channels",
);
}
}
next = setMSTeamsGroupPolicy(next, "allowlist");
next = setMSTeamsTeamsAllowlist(next, entries);
}
}
return { cfg: next, accountId: DEFAULT_ACCOUNT_ID };
},
dmPolicy: msteamsDmPolicy,
groupAccess: msteamsGroupAccess,
disable: (cfg) => ({
...cfg,
channels: {

View File

@@ -455,7 +455,7 @@ export function createSlackSetupWizardProxy(
}) => {
try {
const wizard = (await loadWizard()).slackSetupWizard;
if (!wizard.groupAccess) {
if (!wizard.groupAccess?.resolveAllowlist) {
return entries;
}
return await wizard.groupAccess.resolveAllowlist({

View File

@@ -3,7 +3,6 @@
*/
import type { ChannelOnboardingDmPolicy } from "../../../src/channels/plugins/onboarding-types.js";
import { promptChannelAccessConfig } from "../../../src/channels/plugins/onboarding/channel-access.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
@@ -228,6 +227,26 @@ function setTwitchAccessControl(
});
}
function resolveTwitchGroupPolicy(cfg: OpenClawConfig): "open" | "allowlist" | "disabled" {
const account = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
if (account?.allowedRoles?.includes("all")) {
return "open";
}
if (account?.allowedRoles?.includes("moderator")) {
return "allowlist";
}
return "disabled";
}
function setTwitchGroupPolicy(
cfg: OpenClawConfig,
policy: "open" | "allowlist" | "disabled",
): OpenClawConfig {
const allowedRoles: TwitchRole[] =
policy === "open" ? ["all"] : policy === "allowlist" ? ["moderator", "vip"] : [];
return setTwitchAccessControl(cfg, allowedRoles, true);
}
const twitchDmPolicy: ChannelOnboardingDmPolicy = {
label: "Twitch",
channel,
@@ -270,6 +289,24 @@ const twitchDmPolicy: ChannelOnboardingDmPolicy = {
},
};
const twitchGroupAccess: NonNullable<ChannelSetupWizard["groupAccess"]> = {
label: "Twitch chat",
placeholder: "",
skipAllowlistEntries: true,
currentPolicy: ({ cfg }) => resolveTwitchGroupPolicy(cfg as OpenClawConfig),
currentEntries: ({ cfg }) => {
const account = getAccountConfig(cfg as OpenClawConfig, DEFAULT_ACCOUNT_ID);
return account?.allowFrom ?? [];
},
updatePrompt: ({ cfg }) => {
const account = getAccountConfig(cfg as OpenClawConfig, DEFAULT_ACCOUNT_ID);
return Boolean(account?.allowedRoles?.length || account?.allowFrom?.length);
},
setPolicy: ({ cfg, policy }) => setTwitchGroupPolicy(cfg as OpenClawConfig, policy),
resolveAllowlist: async () => [],
applyAllowlist: ({ cfg }) => cfg as OpenClawConfig,
};
export const twitchSetupAdapter: ChannelSetupAdapter = {
resolveAccountId: () => DEFAULT_ACCOUNT_ID,
applyAccountConfig: ({ cfg }) =>
@@ -342,37 +379,10 @@ export const twitchSetupWizard: ChannelSetupWizard = {
? await twitchDmPolicy.promptAllowFrom({ cfg: cfgWithAccount, prompter })
: cfgWithAccount;
if (!account?.allowFrom || account.allowFrom.length === 0) {
const accessConfig = await promptChannelAccessConfig({
prompter,
label: "Twitch chat",
currentPolicy: account?.allowedRoles?.includes("all")
? "open"
: account?.allowedRoles?.includes("moderator")
? "allowlist"
: "disabled",
currentEntries: [],
placeholder: "",
updatePrompt: false,
});
if (accessConfig) {
const allowedRoles: TwitchRole[] =
accessConfig.policy === "open"
? ["all"]
: accessConfig.policy === "allowlist"
? ["moderator", "vip"]
: [];
return {
cfg: setTwitchAccessControl(cfgWithAllowFrom, allowedRoles, true),
};
}
}
return { cfg: cfgWithAllowFrom };
},
dmPolicy: twitchDmPolicy,
groupAccess: twitchGroupAccess,
disable: (cfg) => {
const twitch = (cfg.channels as Record<string, unknown>)?.twitch as
| Record<string, unknown>

View File

@@ -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({

View File

@@ -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,

View File

@@ -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({

View File

@@ -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,

View File

@@ -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,
});
}

View File

@@ -27,7 +27,6 @@ export { resolveChannelMediaMaxBytes } from "../channels/plugins/media-limits.js
export {
addWildcardAllowFrom,
mergeAllowFromEntries,
promptAccountId,
splitOnboardingEntries,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/onboarding/helpers.js";

View File

@@ -15,7 +15,6 @@ export {
} from "../channels/plugins/helpers.js";
export {
addWildcardAllowFrom,
promptAccountId,
setTopLevelChannelAllowFrom,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/onboarding/helpers.js";

View File

@@ -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,

View File

@@ -15,7 +15,6 @@ export {
buildSingleChannelSecretPromptState,
addWildcardAllowFrom,
mergeAllowFromEntries,
promptAccountId,
promptSingleChannelSecretInput,
runSingleChannelSecretStep,
setTopLevelChannelDmPolicyWithAllowFrom,

View File

@@ -14,7 +14,6 @@ export { formatPairingApproveHint } from "../channels/plugins/helpers.js";
export {
addWildcardAllowFrom,
mergeAllowFromEntries,
promptAccountId,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/onboarding/helpers.js";
export {