From 3711f6ebb3e525f5ee701e02ee16ddfae527d2fb Mon Sep 17 00:00:00 2001 From: Pascal B Date: Fri, 25 Jul 2025 11:01:03 -0400 Subject: [PATCH 1/2] chore(sdk): make .addIntegration less strict (#14056) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: François Levasseur --- bots/bugbuster/bot.definition.ts | 12 +- packages/cli/package.json | 2 +- .../project-command.ts | 9 ++ packages/cli/templates/empty-bot/package.json | 2 +- .../templates/empty-integration/package.json | 2 +- .../cli/templates/empty-plugin/package.json | 2 +- .../cli/templates/hello-world/package.json | 2 +- .../templates/webhook-message/package.json | 2 +- packages/sdk/package.json | 2 +- packages/sdk/src/bot/definition.test.ts | 150 ++++++++++++++++++ packages/sdk/src/bot/definition.ts | 12 +- pnpm-lock.yaml | 12 +- 12 files changed, 179 insertions(+), 30 deletions(-) create mode 100644 packages/sdk/src/bot/definition.test.ts diff --git a/bots/bugbuster/bot.definition.ts b/bots/bugbuster/bot.definition.ts index 3f796acfaab..cd0508f86b0 100644 --- a/bots/bugbuster/bot.definition.ts +++ b/bots/bugbuster/bot.definition.ts @@ -33,14 +33,4 @@ export default new sdk.BotDefinition({ githubWebhookSecret: genenv.BUGBUSTER_GITHUB_WEBHOOK_SECRET, }, }) - .addIntegration(slack, { - enabled: true, - // @ts-ignore TODO: fix config - configurationType: 'botToken', - // @ts-ignore TODO: fix config - configuration: { - botToken: genenv.BUGBUSTER_SLACK_BOT_TOKEN, - signingSecret: genenv.BUGBUSTER_SLACK_SIGNING_SECRET, - botName: 'BugBuster', - }, - }) + .addIntegration(slack) diff --git a/packages/cli/package.json b/packages/cli/package.json index 8c41d9b0754..c61630eb853 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -22,7 +22,7 @@ "@apidevtools/json-schema-ref-parser": "^11.7.0", "@botpress/chat": "0.5.1", "@botpress/client": "1.20.0", - "@botpress/sdk": "4.15.1", + "@botpress/sdk": "4.15.2", "@bpinternal/const": "^0.1.0", "@bpinternal/tunnel": "^0.1.1", "@bpinternal/yargs-extra": "^0.0.3", diff --git a/packages/cli/src/command-implementations/project-command.ts b/packages/cli/src/command-implementations/project-command.ts index be0c5ca6ae1..f34bdcdfb8a 100644 --- a/packages/cli/src/command-implementations/project-command.ts +++ b/packages/cli/src/command-implementations/project-command.ts @@ -366,6 +366,15 @@ export abstract class ProjectCommand extends return { integrations: _(integrations) .keyBy((i) => i.id) + .mapValues( + ({ enabled, configurationType, configuration, disabledChannels }) => + ({ + enabled, + configurationType, + configuration, + disabledChannels, + }) satisfies NonNullable[string] + ) .value(), plugins: utils.records.mapValues(pluginsWithBackingIntegrations, (plugin) => ({ ...plugin, diff --git a/packages/cli/templates/empty-bot/package.json b/packages/cli/templates/empty-bot/package.json index 3ef08eb27c0..b14bdb5c517 100644 --- a/packages/cli/templates/empty-bot/package.json +++ b/packages/cli/templates/empty-bot/package.json @@ -6,7 +6,7 @@ "private": true, "dependencies": { "@botpress/client": "1.20.0", - "@botpress/sdk": "4.15.1" + "@botpress/sdk": "4.15.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/empty-integration/package.json b/packages/cli/templates/empty-integration/package.json index dacd261a46a..5b9438f85ef 100644 --- a/packages/cli/templates/empty-integration/package.json +++ b/packages/cli/templates/empty-integration/package.json @@ -7,7 +7,7 @@ "private": true, "dependencies": { "@botpress/client": "1.20.0", - "@botpress/sdk": "4.15.1" + "@botpress/sdk": "4.15.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/empty-plugin/package.json b/packages/cli/templates/empty-plugin/package.json index 301f9f773fc..ef15e1e5868 100644 --- a/packages/cli/templates/empty-plugin/package.json +++ b/packages/cli/templates/empty-plugin/package.json @@ -6,7 +6,7 @@ }, "private": true, "dependencies": { - "@botpress/sdk": "4.15.1" + "@botpress/sdk": "4.15.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/hello-world/package.json b/packages/cli/templates/hello-world/package.json index a4fc6692298..caf11b44b56 100644 --- a/packages/cli/templates/hello-world/package.json +++ b/packages/cli/templates/hello-world/package.json @@ -7,7 +7,7 @@ "private": true, "dependencies": { "@botpress/client": "1.20.0", - "@botpress/sdk": "4.15.1" + "@botpress/sdk": "4.15.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/webhook-message/package.json b/packages/cli/templates/webhook-message/package.json index d598a295091..a7d56162761 100644 --- a/packages/cli/templates/webhook-message/package.json +++ b/packages/cli/templates/webhook-message/package.json @@ -7,7 +7,7 @@ "private": true, "dependencies": { "@botpress/client": "1.20.0", - "@botpress/sdk": "4.15.1", + "@botpress/sdk": "4.15.2", "axios": "^1.6.8" }, "devDependencies": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 22f0fb32219..42d5ad6be99 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@botpress/sdk", - "version": "4.15.1", + "version": "4.15.2", "description": "Botpress SDK", "main": "./dist/index.cjs", "module": "./dist/index.mjs", diff --git a/packages/sdk/src/bot/definition.test.ts b/packages/sdk/src/bot/definition.test.ts new file mode 100644 index 00000000000..5bd777dd41f --- /dev/null +++ b/packages/sdk/src/bot/definition.test.ts @@ -0,0 +1,150 @@ +import { test } from 'vitest' +import { IntegrationConfigInstance, IntegrationInstance } from './definition' +import * as utils from '../utils/type-utils' +import { IntegrationDefinition } from '../integration' +import { z } from '@bpinternal/zui' + +test('IntegrationInstance should contain important API fields', async () => { + type Actual = IntegrationInstance + type Expected = { + enabled?: boolean + configurationType?: string | null + configuration?: Record + } + + type _assertion = utils.AssertExtends +}) + +test('IntegrationConfigInstance of integration with no config should be empty', async () => { + const def = new IntegrationDefinition({ + name: 'frodo', + version: '1.0.0', + }) + type Def = typeof def + + type Actual = IntegrationConfigInstance<{ + type: 'integration' + name: Def['name'] + version: Def['version'] + definition: Def + implementation: null + }> + + type Expected = { + enabled: boolean + disabledChannels?: string[] | undefined + } & ( + | { + configurationType?: null + configuration: Record + } + | { + configurationType: string + configuration: Record + } + ) + + type _assertion = utils.AssertAll< + [ + // + utils.AssertExtends, + utils.AssertExtends, + utils.IsEquivalent, + ] + > +}) + +test('IntegrationConfigInstance of integration with single config schema should only allow the single config schema', async () => { + const def = new IntegrationDefinition({ + name: 'frodo', + version: '1.0.0', + configuration: { + schema: z.object({ + theOneRing: z.string().describe('The One Ring'), + }), + }, + }) + type Def = typeof def + + type Actual = IntegrationConfigInstance<{ + type: 'integration' + name: Def['name'] + version: Def['version'] + definition: Def + implementation: null + }> + + type Expected = { + enabled: boolean + disabledChannels?: string[] | undefined + } & ( + | { + configurationType?: null + configuration: { theOneRing: string } + } + | { + configurationType: string + configuration: Record + } + ) + + type _assertion = utils.AssertAll< + [ + // + utils.AssertExtends, + utils.AssertExtends, + utils.IsEquivalent, + ] + > +}) + +test('IntegrationConfigInstance of integration with multiple config schemas should allow any of the schemas', async () => { + const def = new IntegrationDefinition({ + name: 'frodo', + version: '1.0.0', + configuration: { + schema: z.object({ + theOneRing: z.string().describe('The One Ring'), + }), + }, + configurations: { + withSword: { + schema: z.object({ + sting: z.string().describe('Sting; The sword of Frodo'), + }), + }, + }, + }) + type Def = typeof def + + type Actual = IntegrationConfigInstance<{ + type: 'integration' + name: Def['name'] + version: Def['version'] + definition: Def + implementation: null + }> + + type Expected = { + enabled: boolean + disabledChannels?: string[] | undefined + } & ( + | { + configurationType?: null + configuration: { theOneRing: string } + } + | { + configurationType: 'withSword' + configuration: { sting: string } + } + ) + + type _assertion = utils.AssertAll< + [ + // + utils.AssertExtends, + utils.AssertExtends, + utils.IsEquivalent, + ] + > +}) diff --git a/packages/sdk/src/bot/definition.ts b/packages/sdk/src/bot/definition.ts index 187e4dc588f..1a1672f109b 100644 --- a/packages/sdk/src/bot/definition.ts +++ b/packages/sdk/src/bot/definition.ts @@ -99,7 +99,7 @@ export type PluginConfigInstance

= { } } -export type IntegrationInstance = IntegrationPackage & IntegrationConfigInstance +export type IntegrationInstance = IntegrationPackage & Partial export type PluginInstance = PluginPackage & PluginConfigInstance export type BotDefinitionProps< @@ -199,18 +199,18 @@ export class BotDefinition< } } - public addIntegration(integrationPkg: I, config: IntegrationConfigInstance): this { + public addIntegration(integrationPkg: I, config?: IntegrationConfigInstance): this { const self = this as Writable if (!self.integrations) { self.integrations = {} } self.integrations[integrationPkg.name] = { - enabled: config.enabled, ...integrationPkg, - configurationType: config.configurationType, - configuration: config.configuration, - disabledChannels: config.disabledChannels, + enabled: config?.enabled, + configurationType: config?.configurationType, + configuration: config?.configuration, + disabledChannels: config?.disabledChannels, } return this } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5fd487b4cab..c2aa0997e2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2009,7 +2009,7 @@ importers: specifier: 1.20.0 version: link:../client '@botpress/sdk': - specifier: 4.15.1 + specifier: 4.15.2 version: link:../sdk '@bpinternal/const': specifier: ^0.1.0 @@ -2121,7 +2121,7 @@ importers: specifier: 1.20.0 version: link:../../../client '@botpress/sdk': - specifier: 4.15.1 + specifier: 4.15.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2137,7 +2137,7 @@ importers: specifier: 1.20.0 version: link:../../../client '@botpress/sdk': - specifier: 4.15.1 + specifier: 4.15.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2150,7 +2150,7 @@ importers: packages/cli/templates/empty-plugin: dependencies: '@botpress/sdk': - specifier: 4.15.1 + specifier: 4.15.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2166,7 +2166,7 @@ importers: specifier: 1.20.0 version: link:../../../client '@botpress/sdk': - specifier: 4.15.1 + specifier: 4.15.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2182,7 +2182,7 @@ importers: specifier: 1.20.0 version: link:../../../client '@botpress/sdk': - specifier: 4.15.1 + specifier: 4.15.2 version: link:../../../sdk axios: specifier: ^1.6.8 From 50b5a4b43b1f8e87dff600ff570a0cb9697651b2 Mon Sep 17 00:00:00 2001 From: Pascal B Date: Fri, 25 Jul 2025 13:18:35 -0400 Subject: [PATCH 2/2] docs(slack): add missing scope to readme (#14083) --- integrations/slack/hub.md | 1 + integrations/slack/integration.definition.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integrations/slack/hub.md b/integrations/slack/hub.md index 0719ef14064..22dba210b4d 100644 --- a/integrations/slack/hub.md +++ b/integrations/slack/hub.md @@ -51,6 +51,7 @@ If you prefer to manually configure the integration, you can provide a bot token - `team:read`: needed to obtain metadata on your team in order to operate on the right instance of your bot. - `users.profile:read`: needed to retrieve profile information for channel and DM members. - `users:read`: needed to obtain a list of all members of the workspace and to receive notifications when new members join the workspace. + - `users:read.email`: needed for the `Get User Profile` action. 6. **IMPORTANT:** install your Slack app to your workspace. This is a crucial step to ensure that the bot can send and receive messages. To do this, scroll up to the "OAuth Tokens for Your Workspace" section and click "Install App to Workspace". Follow the on-screen instructions to authorize the app. 7. Scroll up to the "Advanced token security via token rotation " section and click "Opt In" to enable token rotation. Confirm you wish to opt in. 8. Copy the Refresh Token (starts with `xoxe-1-`) or legacy Bot Token (starts with `xoxb-`). You will need it to set up the integration on Botpress. You may need to refresh the page in the Slack API portal to see the token. diff --git a/integrations/slack/integration.definition.ts b/integrations/slack/integration.definition.ts index 1f3af80f29b..d00b3b2c648 100644 --- a/integrations/slack/integration.definition.ts +++ b/integrations/slack/integration.definition.ts @@ -17,7 +17,7 @@ export default new IntegrationDefinition({ name: 'slack', title: 'Slack', description: 'Automate interactions with your team.', - version: '2.5.4', + version: '2.5.5', icon: 'icon.svg', readme: 'hub.md', configuration,