mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 14:10:24 +00:00
feat(approvals): auto-enable native chat approvals
This commit is contained in:
@@ -51,11 +51,11 @@ export const slackChannelConfigUiHints = {
|
||||
},
|
||||
execApprovals: {
|
||||
label: "Slack Exec Approvals",
|
||||
help: "Slack-native exec approval routing and approver authorization. Enable this only when Slack should act as an explicit exec-approval client for the selected workspace account.",
|
||||
help: "Slack-native exec approval routing and approver authorization. When unset, OpenClaw auto-enables DM-first native approvals if approvers can be resolved for this workspace account.",
|
||||
},
|
||||
"execApprovals.enabled": {
|
||||
label: "Slack Exec Approvals Enabled",
|
||||
help: "Enable Slack exec approvals for this account. When false or unset, Slack messages/buttons cannot approve exec requests.",
|
||||
help: 'Controls Slack native exec approvals for this account: unset or "auto" enables DM-first native approvals when approvers can be resolved, true forces native approvals on, and false disables them.',
|
||||
},
|
||||
"execApprovals.approvers": {
|
||||
label: "Slack Exec Approval Approvers",
|
||||
|
||||
@@ -29,32 +29,36 @@ function buildConfig(
|
||||
}
|
||||
|
||||
describe("slack exec approvals", () => {
|
||||
it("requires enablement and explicit or owner approvers", () => {
|
||||
it("auto-enables when owner approvers resolve and disables only when forced off", () => {
|
||||
expect(isSlackExecApprovalClientEnabled({ cfg: buildConfig() })).toBe(false);
|
||||
expect(isSlackExecApprovalClientEnabled({ cfg: buildConfig({ enabled: true }) })).toBe(false);
|
||||
expect(
|
||||
isSlackExecApprovalClientEnabled({
|
||||
cfg: buildConfig({ enabled: true }, { allowFrom: ["U123"] }),
|
||||
cfg: buildConfig({ enabled: true }),
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isSlackExecApprovalClientEnabled({
|
||||
cfg: buildConfig({ enabled: true, approvers: ["U123"] }),
|
||||
cfg: buildConfig({ approvers: ["U123"] }),
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isSlackExecApprovalClientEnabled({
|
||||
cfg: {
|
||||
...buildConfig({ enabled: true }),
|
||||
...buildConfig(),
|
||||
commands: { ownerAllowFrom: ["slack:U123OWNER"] },
|
||||
} as OpenClawConfig,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isSlackExecApprovalClientEnabled({
|
||||
cfg: buildConfig({ enabled: false, approvers: ["U123"] }),
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("prefers explicit approvers when configured", () => {
|
||||
const cfg = buildConfig(
|
||||
{ enabled: true, approvers: ["U456"] },
|
||||
{ approvers: ["U456"] },
|
||||
{ allowFrom: ["U123"], defaultTo: "user:U789" },
|
||||
);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { logError } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { slackNativeApprovalAdapter } from "../approval-native.js";
|
||||
import {
|
||||
getSlackExecApprovalApprovers,
|
||||
isSlackExecApprovalClientEnabled,
|
||||
normalizeSlackApproverId,
|
||||
shouldHandleSlackExecApprovalRequest,
|
||||
} from "../exec-approvals.js";
|
||||
@@ -239,13 +240,10 @@ export class SlackExecApprovalHandler {
|
||||
gatewayUrl: opts.gatewayUrl,
|
||||
nativeAdapter: slackNativeApprovalAdapter.native,
|
||||
isConfigured: () =>
|
||||
Boolean(
|
||||
opts.config.enabled &&
|
||||
getSlackExecApprovalApprovers({
|
||||
cfg: opts.cfg,
|
||||
accountId: opts.accountId,
|
||||
}).length > 0,
|
||||
),
|
||||
isSlackExecApprovalClientEnabled({
|
||||
cfg: opts.cfg,
|
||||
accountId: opts.accountId,
|
||||
}),
|
||||
shouldHandle: (request) => this.shouldHandle(request),
|
||||
buildPendingContent: ({ request }) => ({
|
||||
text: buildSlackPendingApprovalText(request),
|
||||
|
||||
@@ -31,6 +31,7 @@ import { normalizeStringEntries } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { installRequestBodyLimitGuard } from "openclaw/plugin-sdk/webhook-request-guards";
|
||||
import { resolveSlackAccount } from "../accounts.js";
|
||||
import { resolveSlackWebClientOptions } from "../client.js";
|
||||
import { isSlackExecApprovalClientEnabled } from "../exec-approvals.js";
|
||||
import { normalizeSlackWebhookPath, registerSlackHttpHandler } from "../http/index.js";
|
||||
import { SLACK_TEXT_LIMIT } from "../limits.js";
|
||||
import { resolveSlackChannelAllowlist, type SlackChannelResolution } from "../resolve-channels.js";
|
||||
@@ -406,11 +407,14 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
: undefined;
|
||||
|
||||
const handleSlackMessage = createSlackMessageHandler({ ctx, account, trackEvent });
|
||||
const execApprovalsHandler = slackCfg.execApprovals?.enabled
|
||||
const execApprovalsHandler = isSlackExecApprovalClientEnabled({
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
})
|
||||
? new SlackExecApprovalHandler({
|
||||
app,
|
||||
accountId: account.accountId,
|
||||
config: slackCfg.execApprovals,
|
||||
config: slackCfg.execApprovals ?? {},
|
||||
cfg,
|
||||
})
|
||||
: null;
|
||||
|
||||
Reference in New Issue
Block a user