experimental-ash 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # experimental-ash
2
2
 
3
+ ## 0.8.3
4
+
5
+ ### Patch Changes
6
+
7
+ - e35a562: fix(slack): forward `webhookVerifier` credential to the underlying Slack adapter so Connex-issued credentials (`slackChannelCredentials` from `@vercel/connex/ash`) authenticate inbound webhooks via Vercel OIDC instead of HMAC.
8
+
9
+ Previously `SlackChannelCredentials` declared only `botToken` and `signingSecret`, and `slackChannel()` never passed `webhookVerifier` to `createSlackAdapter`. When Connex supplied `{ botToken, webhookVerifier }` and `SLACK_SIGNING_SECRET` was not set in the environment (the whole point of Connex), the adapter threw `ValidationError: signingSecret or webhookVerifier is required.` because the verifier was silently dropped on the ash side before reaching the adapter.
10
+
11
+ Concretely:
12
+
13
+ - `SlackChannelCredentials` now exposes `webhookVerifier?: SlackWebhookVerifier`. The verifier signature `(request: Request, body: string) => unknown | Promise<unknown>` is published as an ash-owned `SlackWebhookVerifier` type so we don't leak the third-party adapter type.
14
+ - `slackChannel()` forwards `webhookVerifier` to `createSlackAdapter` alongside `botToken` / `signingSecret` / `userName`.
15
+ - When a `webhookVerifier` is supplied, the resolver skips the `SLACK_SIGNING_SECRET` env-var fallback so Connex deployments don't accidentally pick up a stale env-var secret. An explicit `credentials.signingSecret` still wins if both are provided.
16
+
17
+ Public agent code is unchanged:
18
+
19
+ ```ts
20
+ import { slackChannelCredentials } from "@vercel/connex/ash";
21
+ import { slack } from "experimental-ash/channels/slack";
22
+
23
+ export default slack({
24
+ botName: "help-ash",
25
+ credentials: slackChannelCredentials("slack/help-ash"),
26
+ });
27
+ ```
28
+
3
29
  ## 0.8.2
4
30
 
5
31
  ### Patch Changes
@@ -6,7 +6,7 @@ import { ASH_PACKAGE_NAME } from "#package-name.js";
6
6
  let cachedPackageInfo;
7
7
  // The package build stamps the published version into `dist` so bundled
8
8
  // deployments can still report package metadata without resolving package.json.
9
- const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.8.2";
9
+ const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.8.3";
10
10
  const BUNDLED_FALLBACK_PACKAGE_VERSION_PLACEHOLDER = "__ASH_PACKAGE_VERSION_PLACEHOLDER__";
11
11
  const WORKFLOW_MODULE_ALIASES = {
12
12
  "workflow/api": "src/compiled/@workflow/core/runtime.js",
@@ -1,4 +1,4 @@
1
1
  export { slack, type SlackOptions } from "#public/channels/slack/slack.js";
2
- export { slackChannel, type SlackApiHandle, type SlackApiResponse, type SlackChannel, type SlackChannelConfig, type SlackChannelEvents, type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackInteractionAction, type SlackReceiveArgs, } from "#public/channels/slack/slackChannel.js";
2
+ export { slackChannel, type SlackApiHandle, type SlackApiResponse, type SlackChannel, type SlackChannelConfig, type SlackChannelEvents, type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackInteractionAction, type SlackReceiveArgs, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
3
3
  export { Actions, Button, Card, CardText, Divider, Fields, Image, LinkButton, Modal, RadioSelect, Section, Select, SelectOption, Table, TextInput, } from "#compiled/chat/index.js";
4
4
  export type { AdapterPostableMessage, Attachment, Author, CardElement, FileUpload, Message, PostableMessage, SentMessage, Thread, } from "#compiled/chat/index.js";
@@ -28,9 +28,29 @@ export interface SlackChannelState {
28
28
  serializedThread: SerializedThread | null;
29
29
  teamId: string | null;
30
30
  }
31
+ /**
32
+ * Verifies an inbound Slack webhook request. Returns when the request
33
+ * is authentic, throws otherwise. Used as an alternative to HMAC
34
+ * verification with `signingSecret` — for example, the Connex SDK
35
+ * supplies a verifier that authenticates Connex-forwarded webhooks
36
+ * with Vercel OIDC instead of Slack's signing secret.
37
+ */
38
+ export type SlackWebhookVerifier = (request: Request, body: string) => unknown | Promise<unknown>;
31
39
  export interface SlackChannelCredentials {
32
40
  readonly botToken?: SlackBotToken;
41
+ /**
42
+ * Slack signing secret used to HMAC-verify inbound webhook requests.
43
+ * Falls back to `process.env.SLACK_SIGNING_SECRET` when neither this
44
+ * nor `webhookVerifier` is supplied.
45
+ */
33
46
  readonly signingSecret?: string;
47
+ /**
48
+ * Custom inbound webhook verifier. When supplied, ash skips the
49
+ * `SLACK_SIGNING_SECRET` fallback and delegates verification to this
50
+ * function. Typically populated by integrations (e.g. Connex) that
51
+ * authenticate webhooks out-of-band.
52
+ */
53
+ readonly webhookVerifier?: SlackWebhookVerifier;
34
54
  }
35
55
  export interface SlackReceiveArgs {
36
56
  readonly channelId: string;
@@ -123,7 +123,12 @@ export function slackChannel(config = {}) {
123
123
  return chatPromise;
124
124
  const promise = (async () => {
125
125
  const botToken = config.credentials?.botToken ?? process.env.SLACK_BOT_TOKEN;
126
- const signingSecret = config.credentials?.signingSecret ?? process.env.SLACK_SIGNING_SECRET;
126
+ const webhookVerifier = config.credentials?.webhookVerifier;
127
+ // Skip the SLACK_SIGNING_SECRET env-var fallback when a webhook
128
+ // verifier is supplied, so integrations like Connex don't need
129
+ // the signing secret set in the environment at all.
130
+ const signingSecret = config.credentials?.signingSecret ??
131
+ (webhookVerifier ? undefined : process.env.SLACK_SIGNING_SECRET);
127
132
  if (!botToken) {
128
133
  throw new Error("slackChannel requires a bot token. Pass credentials.botToken or set SLACK_BOT_TOKEN.");
129
134
  }
@@ -135,6 +140,7 @@ export function slackChannel(config = {}) {
135
140
  const slackAdapter = slackModule.createSlackAdapter({
136
141
  botToken,
137
142
  signingSecret,
143
+ webhookVerifier,
138
144
  userName: config.botName,
139
145
  });
140
146
  const chat = new chatModule.Chat({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-ash",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "bin": {
5
5
  "ash": "./bin/ash.js",
6
6
  "experimental-ash": "./bin/ash.js"