feat(slack): add HTTP receiver webhook mode (#1143) - thanks @jdrhyne
Co-authored-by: Jonathan Rhyne <jdrhyne@users.noreply.github.com>
This commit is contained in:
65
src/config/slack-http-config.test.ts
Normal file
65
src/config/slack-http-config.test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { validateConfigObject } from "./config.js";
|
||||
|
||||
describe("Slack HTTP mode config", () => {
|
||||
it("accepts HTTP mode when signing secret is configured", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
slack: {
|
||||
mode: "http",
|
||||
signingSecret: "secret",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects HTTP mode without signing secret", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
slack: {
|
||||
mode: "http",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) {
|
||||
expect(res.issues[0]?.path).toBe("channels.slack.signingSecret");
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts account HTTP mode when base signing secret is set", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
slack: {
|
||||
signingSecret: "secret",
|
||||
accounts: {
|
||||
ops: {
|
||||
mode: "http",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects account HTTP mode without signing secret", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
slack: {
|
||||
accounts: {
|
||||
ops: {
|
||||
mode: "http",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) {
|
||||
expect(res.issues[0]?.path).toBe("channels.slack.accounts.ops.signingSecret");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -70,6 +70,12 @@ export type SlackThreadConfig = {
|
||||
export type SlackAccountConfig = {
|
||||
/** Optional display name for this account (used in CLI/UI lists). */
|
||||
name?: string;
|
||||
/** Slack connection mode (socket|http). Default: socket. */
|
||||
mode?: "socket" | "http";
|
||||
/** Slack signing secret (required for HTTP mode). */
|
||||
signingSecret?: string;
|
||||
/** Slack Events API webhook path (default: /slack/events). */
|
||||
webhookPath?: string;
|
||||
/** Optional provider capability tags used for agent/runtime guidance. */
|
||||
capabilities?: string[];
|
||||
/** Override native command registration for Slack (bool or "auto"). */
|
||||
|
||||
@@ -258,6 +258,9 @@ export const SlackThreadSchema = z.object({
|
||||
|
||||
export const SlackAccountSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
mode: z.enum(["socket", "http"]).optional(),
|
||||
signingSecret: z.string().optional(),
|
||||
webhookPath: z.string().optional(),
|
||||
capabilities: z.array(z.string()).optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
commands: ProviderCommandsSchema,
|
||||
@@ -305,7 +308,35 @@ export const SlackAccountSchema = z.object({
|
||||
});
|
||||
|
||||
export const SlackConfigSchema = SlackAccountSchema.extend({
|
||||
mode: z.enum(["socket", "http"]).optional().default("socket"),
|
||||
signingSecret: z.string().optional(),
|
||||
webhookPath: z.string().optional().default("/slack/events"),
|
||||
accounts: z.record(z.string(), SlackAccountSchema.optional()).optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
const baseMode = value.mode ?? "socket";
|
||||
if (baseMode === "http" && !value.signingSecret) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'channels.slack.mode="http" requires channels.slack.signingSecret',
|
||||
path: ["signingSecret"],
|
||||
});
|
||||
}
|
||||
if (!value.accounts) return;
|
||||
for (const [accountId, account] of Object.entries(value.accounts)) {
|
||||
if (!account) continue;
|
||||
if (account.enabled === false) continue;
|
||||
const accountMode = account.mode ?? baseMode;
|
||||
if (accountMode !== "http") continue;
|
||||
const accountSecret = account.signingSecret ?? value.signingSecret;
|
||||
if (!accountSecret) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
'channels.slack.accounts.*.mode="http" requires channels.slack.signingSecret or channels.slack.accounts.*.signingSecret',
|
||||
path: ["accounts", accountId, "signingSecret"],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const SignalAccountSchemaBase = z.object({
|
||||
|
||||
Reference in New Issue
Block a user