mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 12:50:22 +00:00
feat(approvals): auto-enable native chat approvals
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
createApproverRestrictedNativeApprovalCapability,
|
||||
splitChannelApprovalCapability,
|
||||
doesApprovalRequestMatchChannelAccount,
|
||||
isChannelExecApprovalClientEnabledFromConfig,
|
||||
matchesApprovalRequestFilters,
|
||||
} from "openclaw/plugin-sdk/approval-runtime";
|
||||
import type { DiscordExecApprovalConfig, OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
@@ -75,16 +76,18 @@ export function shouldHandleDiscordApprovalRequest(params: {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!config) {
|
||||
return true;
|
||||
}
|
||||
if (!config.enabled || approvers.length === 0) {
|
||||
if (
|
||||
!isChannelExecApprovalClientEnabledFromConfig({
|
||||
enabled: config?.enabled,
|
||||
approverCount: approvers.length,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return matchesApprovalRequestFilters({
|
||||
request: params.request.request,
|
||||
agentFilter: config.agentFilter,
|
||||
sessionFilter: config.sessionFilter,
|
||||
agentFilter: config?.agentFilter,
|
||||
sessionFilter: config?.sessionFilter,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -22,34 +22,35 @@ function buildConfig(
|
||||
}
|
||||
|
||||
describe("discord exec approvals", () => {
|
||||
it("requires enablement and explicit or owner approvers", () => {
|
||||
it("auto-enables when owner approvers resolve and disables only when forced off", () => {
|
||||
expect(isDiscordExecApprovalClientEnabled({ cfg: buildConfig() })).toBe(false);
|
||||
expect(isDiscordExecApprovalClientEnabled({ cfg: buildConfig({ enabled: true }) })).toBe(false);
|
||||
expect(
|
||||
isDiscordExecApprovalClientEnabled({
|
||||
cfg: buildConfig({ enabled: true }, { allowFrom: ["123"] }),
|
||||
cfg: buildConfig({ enabled: true }),
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isDiscordExecApprovalClientEnabled({
|
||||
cfg: buildConfig({ enabled: true, approvers: ["123"] }),
|
||||
cfg: buildConfig({ approvers: ["123"] }),
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isDiscordExecApprovalClientEnabled({
|
||||
cfg: {
|
||||
...buildConfig({ enabled: true }),
|
||||
...buildConfig(),
|
||||
commands: { ownerAllowFrom: ["discord:789"] },
|
||||
} as OpenClawConfig,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isDiscordExecApprovalClientEnabled({
|
||||
cfg: buildConfig({ enabled: false, approvers: ["123"] }),
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("prefers explicit approvers when configured", () => {
|
||||
const cfg = buildConfig(
|
||||
{ enabled: true, approvers: ["456"] },
|
||||
{ allowFrom: ["123"], defaultTo: "user:789" },
|
||||
);
|
||||
const cfg = buildConfig({ approvers: ["456"] }, { allowFrom: ["123"], defaultTo: "user:789" });
|
||||
|
||||
expect(getDiscordExecApprovalApprovers({ cfg })).toEqual(["456"]);
|
||||
expect(isDiscordExecApprovalApprover({ cfg, senderId: "456" })).toBe(true);
|
||||
@@ -72,7 +73,7 @@ describe("discord exec approvals", () => {
|
||||
|
||||
it("falls back to commands.ownerAllowFrom for exec approvers", () => {
|
||||
const cfg = {
|
||||
...buildConfig({ enabled: true }),
|
||||
...buildConfig(),
|
||||
commands: { ownerAllowFrom: ["discord:123", "user:456", "789"] },
|
||||
} as OpenClawConfig;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
|
||||
import { isChannelExecApprovalClientEnabledFromConfig } from "openclaw/plugin-sdk/approval-runtime";
|
||||
import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-runtime";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import type { DiscordExecApprovalConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
@@ -53,14 +54,14 @@ export function isDiscordExecApprovalClientEnabled(params: {
|
||||
configOverride?: DiscordExecApprovalConfig | null;
|
||||
}): boolean {
|
||||
const config = params.configOverride ?? resolveDiscordAccount(params).config.execApprovals;
|
||||
return Boolean(
|
||||
config?.enabled &&
|
||||
getDiscordExecApprovalApprovers({
|
||||
return isChannelExecApprovalClientEnabledFromConfig({
|
||||
enabled: config?.enabled,
|
||||
approverCount: getDiscordExecApprovalApprovers({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
configOverride: params.configOverride,
|
||||
}).length > 0,
|
||||
);
|
||||
}).length,
|
||||
});
|
||||
}
|
||||
|
||||
export function isDiscordExecApprovalApprover(params: {
|
||||
|
||||
@@ -34,7 +34,10 @@ import {
|
||||
createDiscordApprovalCapability,
|
||||
shouldHandleDiscordApprovalRequest,
|
||||
} from "../approval-native.js";
|
||||
import { getDiscordExecApprovalApprovers } from "../exec-approvals.js";
|
||||
import {
|
||||
getDiscordExecApprovalApprovers,
|
||||
isDiscordExecApprovalClientEnabled,
|
||||
} from "../exec-approvals.js";
|
||||
import { createDiscordClient, stripUndefinedFields } from "../send.shared.js";
|
||||
import { DiscordUiContainer } from "../ui.js";
|
||||
|
||||
@@ -484,7 +487,12 @@ export class DiscordExecApprovalHandler {
|
||||
gatewayUrl: this.opts.gatewayUrl,
|
||||
eventKinds: ["exec", "plugin"],
|
||||
nativeAdapter: createDiscordApprovalCapability(this.opts.config).native,
|
||||
isConfigured: () => Boolean(this.opts.config.enabled && this.getApprovers().length > 0),
|
||||
isConfigured: () =>
|
||||
isDiscordExecApprovalClientEnabled({
|
||||
cfg: this.opts.cfg,
|
||||
accountId: this.opts.accountId,
|
||||
configOverride: this.opts.config,
|
||||
}),
|
||||
shouldHandle: (request) => this.shouldHandle(request),
|
||||
buildPendingContent: ({ request }) => {
|
||||
const actionRow = createApprovalActionRow(request);
|
||||
|
||||
@@ -20,12 +20,6 @@ import {
|
||||
} from "openclaw/plugin-sdk/config-runtime";
|
||||
import type { OpenClawConfig, ReplyToMode } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { loadConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
resolveOpenProviderRuntimeGroupPolicy,
|
||||
resolveDefaultGroupPolicy,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "openclaw/plugin-sdk/runtime-group-policy";
|
||||
import { createConnectedChannelStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
||||
import { getPluginCommandSpecs } from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
||||
@@ -38,9 +32,16 @@ import {
|
||||
} from "openclaw/plugin-sdk/runtime-env";
|
||||
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { createNonExitingRuntime, type RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
resolveOpenProviderRuntimeGroupPolicy,
|
||||
resolveDefaultGroupPolicy,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "openclaw/plugin-sdk/runtime-group-policy";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { summarizeStringEntries } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { resolveDiscordAccount } from "../accounts.js";
|
||||
import { isDiscordExecApprovalClientEnabled } from "../exec-approvals.js";
|
||||
import { fetchDiscordApplicationId } from "../probe.js";
|
||||
import { normalizeDiscordToken } from "../token.js";
|
||||
import { createDiscordVoiceCommand } from "../voice/command.js";
|
||||
@@ -824,7 +825,11 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
||||
|
||||
// Initialize exec approvals handler if enabled
|
||||
const execApprovalsConfig = discordCfg.execApprovals ?? {};
|
||||
const execApprovalsHandler = execApprovalsConfig.enabled
|
||||
const execApprovalsHandler = isDiscordExecApprovalClientEnabled({
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
configOverride: execApprovalsConfig,
|
||||
})
|
||||
? new DiscordExecApprovalHandler({
|
||||
token,
|
||||
accountId: account.accountId,
|
||||
|
||||
Reference in New Issue
Block a user