@botpress/cli 0.2.3 → 0.2.5

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/command-implementations/global-command.ts"],
4
- "sourcesContent": ["import type { YargsConfig } from '@bpinternal/yargs-extra'\nimport chalk from 'chalk'\nimport fs from 'fs'\nimport latestVersion from 'latest-version'\nimport _ from 'lodash'\nimport * as pathlib from 'path'\nimport semver from 'semver'\nimport type { ApiClientFactory } from '../api-client'\nimport type * as config from '../config'\nimport * as consts from '../consts'\nimport * as errors from '../errors'\nimport type { CommandArgv, CommandDefinition } from '../typings'\nimport * as utils from '../utils'\nimport { BaseCommand } from './base-command'\n\nexport type GlobalCommandDefinition = CommandDefinition<typeof config.schemas.global>\nexport type GlobalCache = { apiUrl: string; token: string; workspaceId: string }\n\nexport type ConfigurableGlobalPaths = { botpressHomeDir: string; cliRootDir: utils.path.AbsolutePath }\nexport type ConstantGlobalPaths = typeof consts.fromHomeDir & typeof consts.fromCliRootDir\nexport type AllGlobalPaths = ConfigurableGlobalPaths & ConstantGlobalPaths\n\nclass GlobalPaths extends utils.path.PathStore<keyof AllGlobalPaths> {\n public constructor(argv: CommandArgv<GlobalCommandDefinition>) {\n const absBotpressHome = utils.path.absoluteFrom(utils.path.cwd(), argv.botpressHome)\n super({\n cliRootDir: consts.cliRootDir,\n botpressHomeDir: absBotpressHome,\n ..._.mapValues(consts.fromHomeDir, (p) => utils.path.absoluteFrom(absBotpressHome, p)),\n ..._.mapValues(consts.fromCliRootDir, (p) => utils.path.absoluteFrom(consts.cliRootDir, p)),\n })\n }\n}\n\ntype PackageJson = { name: string; version: string }\n\nconst UPDATE_MSG = (props: PackageJson & { latest: string }) =>\n `${chalk.bold('Update available')} ${chalk.dim(props.version)} \u2192 ${chalk.green(props.latest)}\n\nTo update, run:\n for npm ${chalk.cyan(`npm i -g ${props.name}`)}\n for yarn ${chalk.cyan(`yarn global add ${props.name}`)}\n for pnpm ${chalk.cyan(`pnpm i -g ${props.name}`)}`\n\nexport abstract class GlobalCommand<C extends GlobalCommandDefinition> extends BaseCommand<C> {\n protected api: ApiClientFactory\n protected prompt: utils.prompt.CLIPrompt\n\n public constructor(\n api: ApiClientFactory,\n prompt: utils.prompt.CLIPrompt,\n ...args: ConstructorParameters<typeof BaseCommand<C>>\n ) {\n super(...args)\n this.api = api\n this.prompt = prompt\n }\n\n protected get globalPaths() {\n return new GlobalPaths(this.argv)\n }\n\n protected get globalCache() {\n return new utils.cache.FSKeyValueCache<GlobalCache>(this.globalPaths.abs.globalCacheFile)\n }\n\n protected override bootstrap = async () => {\n const pkgJson = await this._readPackageJson()\n\n const versionText = chalk.bold(`v${pkgJson.version}`)\n this.logger.log(`Botpress CLI ${versionText}`, { prefix: '\uD83E\uDD16' })\n\n await this._notifyUpdate(pkgJson)\n\n const paths = this.globalPaths\n if (paths.abs.botpressHomeDir !== consts.defaultBotpressHome) {\n this.logger.log(`Using custom botpress home: ${paths.abs.botpressHomeDir}`, { prefix: '\uD83C\uDFE0' })\n }\n }\n\n protected override teardown = async () => {\n this.logger.cleanup()\n }\n\n protected async ensureLoginAndCreateClient(credentials: YargsConfig<typeof config.schemas.credentials>) {\n const cache = this.globalCache\n const token = await cache.get('token')\n const workspaceId = credentials.workspaceId ?? (await cache.get('workspaceId'))\n const apiUrl = credentials.apiUrl ?? (await cache.get('apiUrl'))\n\n if (!(token && workspaceId && apiUrl)) {\n throw new errors.NotLoggedInError()\n }\n\n return this.api.newClient({ apiUrl, token, workspaceId }, this.logger)\n }\n\n private _notifyUpdate = async (pkgJson: PackageJson): Promise<void> => {\n try {\n const latest = await latestVersion(pkgJson.name)\n const isOutdated = semver.lt(pkgJson.version, latest)\n if (isOutdated) {\n this.logger.box(UPDATE_MSG({ ...pkgJson, latest }))\n }\n } catch (thrown) {\n const err = errors.BotpressCLIError.map(thrown)\n this.logger.debug(`Failed to check for updates: ${err.message}`)\n }\n }\n\n private _readPackageJson = async (): Promise<PackageJson> => {\n const path = pathlib.join(this.globalPaths.abs.cliRootDir, 'package.json')\n const strContent = await fs.promises.readFile(path, 'utf8')\n const jsonContent = JSON.parse(strContent)\n return jsonContent\n }\n}\n"],
4
+ "sourcesContent": ["import type { YargsConfig } from '@bpinternal/yargs-extra'\nimport chalk from 'chalk'\nimport fs from 'fs'\nimport latestVersion from 'latest-version'\nimport _ from 'lodash'\nimport * as pathlib from 'path'\nimport semver from 'semver'\nimport type { ApiClientFactory } from '../api/client'\nimport type * as config from '../config'\nimport * as consts from '../consts'\nimport * as errors from '../errors'\nimport type { CommandArgv, CommandDefinition } from '../typings'\nimport * as utils from '../utils'\nimport { BaseCommand } from './base-command'\n\nexport type GlobalCommandDefinition = CommandDefinition<typeof config.schemas.global>\nexport type GlobalCache = { apiUrl: string; token: string; workspaceId: string }\n\nexport type ConfigurableGlobalPaths = { botpressHomeDir: string; cliRootDir: utils.path.AbsolutePath }\nexport type ConstantGlobalPaths = typeof consts.fromHomeDir & typeof consts.fromCliRootDir\nexport type AllGlobalPaths = ConfigurableGlobalPaths & ConstantGlobalPaths\n\nclass GlobalPaths extends utils.path.PathStore<keyof AllGlobalPaths> {\n public constructor(argv: CommandArgv<GlobalCommandDefinition>) {\n const absBotpressHome = utils.path.absoluteFrom(utils.path.cwd(), argv.botpressHome)\n super({\n cliRootDir: consts.cliRootDir,\n botpressHomeDir: absBotpressHome,\n ..._.mapValues(consts.fromHomeDir, (p) => utils.path.absoluteFrom(absBotpressHome, p)),\n ..._.mapValues(consts.fromCliRootDir, (p) => utils.path.absoluteFrom(consts.cliRootDir, p)),\n })\n }\n}\n\ntype PackageJson = { name: string; version: string }\n\nconst UPDATE_MSG = (props: PackageJson & { latest: string }) =>\n `${chalk.bold('Update available')} ${chalk.dim(props.version)} \u2192 ${chalk.green(props.latest)}\n\nTo update, run:\n for npm ${chalk.cyan(`npm i -g ${props.name}`)}\n for yarn ${chalk.cyan(`yarn global add ${props.name}`)}\n for pnpm ${chalk.cyan(`pnpm i -g ${props.name}`)}`\n\nexport abstract class GlobalCommand<C extends GlobalCommandDefinition> extends BaseCommand<C> {\n protected api: ApiClientFactory\n protected prompt: utils.prompt.CLIPrompt\n\n public constructor(\n api: ApiClientFactory,\n prompt: utils.prompt.CLIPrompt,\n ...args: ConstructorParameters<typeof BaseCommand<C>>\n ) {\n super(...args)\n this.api = api\n this.prompt = prompt\n }\n\n protected get globalPaths() {\n return new GlobalPaths(this.argv)\n }\n\n protected get globalCache() {\n return new utils.cache.FSKeyValueCache<GlobalCache>(this.globalPaths.abs.globalCacheFile)\n }\n\n protected override bootstrap = async () => {\n const pkgJson = await this._readPackageJson()\n\n const versionText = chalk.bold(`v${pkgJson.version}`)\n this.logger.log(`Botpress CLI ${versionText}`, { prefix: '\uD83E\uDD16' })\n\n await this._notifyUpdate(pkgJson)\n\n const paths = this.globalPaths\n if (paths.abs.botpressHomeDir !== consts.defaultBotpressHome) {\n this.logger.log(`Using custom botpress home: ${paths.abs.botpressHomeDir}`, { prefix: '\uD83C\uDFE0' })\n }\n }\n\n protected override teardown = async () => {\n this.logger.cleanup()\n }\n\n protected async ensureLoginAndCreateClient(credentials: YargsConfig<typeof config.schemas.credentials>) {\n const cache = this.globalCache\n const token = await cache.get('token')\n const workspaceId = credentials.workspaceId ?? (await cache.get('workspaceId'))\n const apiUrl = credentials.apiUrl ?? (await cache.get('apiUrl'))\n\n if (!(token && workspaceId && apiUrl)) {\n throw new errors.NotLoggedInError()\n }\n\n return this.api.newClient({ apiUrl, token, workspaceId }, this.logger)\n }\n\n private _notifyUpdate = async (pkgJson: PackageJson): Promise<void> => {\n try {\n const latest = await latestVersion(pkgJson.name)\n const isOutdated = semver.lt(pkgJson.version, latest)\n if (isOutdated) {\n this.logger.box(UPDATE_MSG({ ...pkgJson, latest }))\n }\n } catch (thrown) {\n const err = errors.BotpressCLIError.map(thrown)\n this.logger.debug(`Failed to check for updates: ${err.message}`)\n }\n }\n\n private _readPackageJson = async (): Promise<PackageJson> => {\n const path = pathlib.join(this.globalPaths.abs.cliRootDir, 'package.json')\n const strContent = await fs.promises.readFile(path, 'utf8')\n const jsonContent = JSON.parse(strContent)\n return jsonContent\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["_", "chalk", "latestVersion", "semver", "fs"]
7
7
  }
@@ -27,7 +27,7 @@ __export(command_implementations_exports, {
27
27
  default: () => command_implementations_default
28
28
  });
29
29
  module.exports = __toCommonJS(command_implementations_exports);
30
- var import_api_client = require("../api-client");
30
+ var import_client = require("../api/client");
31
31
  var import_logger = require("../logger");
32
32
  var utils = __toESM(require("../utils"));
33
33
  var import_add_command = require("./add-command");
@@ -45,7 +45,7 @@ var import_serve_command = require("./serve-command");
45
45
  const getHandler = (cls) => async (argv) => {
46
46
  const logger = new import_logger.Logger(argv);
47
47
  const prompt = new utils.prompt.CLIPrompt(argv, logger);
48
- return new cls(import_api_client.ApiClient, prompt, logger, argv).handler();
48
+ return new cls(import_client.ApiClient, prompt, logger, argv).handler();
49
49
  };
50
50
  var command_implementations_default = {
51
51
  login: getHandler(import_login_command.LoginCommand),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/command-implementations/index.ts"],
4
- "sourcesContent": ["import { ApiClient } from '../api-client'\nimport type commandDefinitions from '../command-definitions'\nimport type { ImplementationTree } from '../command-tree'\nimport { Logger } from '../logger'\nimport type { CommandArgv } from '../typings'\nimport * as utils from '../utils'\nimport { AddCommand } from './add-command'\nimport type { BaseCommand } from './base-command'\nimport * as bots from './bot-commands'\nimport { BuildCommand } from './build-command'\nimport { BundleCommand } from './bundle-command'\nimport { DeployCommand } from './deploy-command'\nimport { DevCommand } from './dev-command'\nimport { GenerateCommand } from './gen-command'\nimport type { GlobalCommand, GlobalCommandDefinition } from './global-command'\nimport { InitCommand } from './init-command'\nimport * as integrations from './integration-commands'\nimport { LoginCommand } from './login-command'\nimport { LogoutCommand } from './logout-command'\nimport { ServeCommand } from './serve-command'\n\ntype GlobalCtor<C extends GlobalCommandDefinition> = new (\n ...args: ConstructorParameters<typeof GlobalCommand<C>>\n) => BaseCommand<C>\n\nconst getHandler =\n <C extends GlobalCommandDefinition>(cls: GlobalCtor<C>) =>\n async (argv: CommandArgv<C>) => {\n const logger = new Logger(argv)\n const prompt = new utils.prompt.CLIPrompt(argv, logger)\n return new cls(ApiClient, prompt, logger, argv).handler()\n }\n\nexport default {\n login: getHandler(LoginCommand),\n logout: getHandler(LogoutCommand),\n bots: {\n subcommands: {\n create: getHandler(bots.CreateBotCommand),\n get: getHandler(bots.GetBotCommand),\n delete: getHandler(bots.DeleteBotCommand),\n list: getHandler(bots.ListBotsCommand),\n },\n },\n integrations: {\n subcommands: {\n get: getHandler(integrations.GetIntegrationCommand),\n list: getHandler(integrations.ListIntegrationsCommand),\n delete: getHandler(integrations.DeleteIntegrationCommand),\n },\n },\n init: getHandler(InitCommand),\n generate: getHandler(GenerateCommand),\n bundle: getHandler(BundleCommand),\n build: getHandler(BuildCommand),\n serve: getHandler(ServeCommand),\n deploy: getHandler(DeployCommand),\n add: getHandler(AddCommand),\n dev: getHandler(DevCommand),\n} satisfies ImplementationTree<typeof commandDefinitions>\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAA0B;AAG1B,oBAAuB;AAEvB,YAAuB;AACvB,yBAA2B;AAE3B,WAAsB;AACtB,2BAA6B;AAC7B,4BAA8B;AAC9B,4BAA8B;AAC9B,yBAA2B;AAC3B,yBAAgC;AAEhC,0BAA4B;AAC5B,mBAA8B;AAC9B,2BAA6B;AAC7B,4BAA8B;AAC9B,2BAA6B;AAM7B,MAAM,aACJ,CAAoC,QACpC,OAAO,SAAyB;AAC9B,QAAM,SAAS,IAAI,qBAAO,IAAI;AAC9B,QAAM,SAAS,IAAI,MAAM,OAAO,UAAU,MAAM,MAAM;AACtD,SAAO,IAAI,IAAI,6BAAW,QAAQ,QAAQ,IAAI,EAAE,QAAQ;AAC1D;AAEF,IAAO,kCAAQ;AAAA,EACb,OAAO,WAAW,iCAAY;AAAA,EAC9B,QAAQ,WAAW,mCAAa;AAAA,EAChC,MAAM;AAAA,IACJ,aAAa;AAAA,MACX,QAAQ,WAAW,KAAK,gBAAgB;AAAA,MACxC,KAAK,WAAW,KAAK,aAAa;AAAA,MAClC,QAAQ,WAAW,KAAK,gBAAgB;AAAA,MACxC,MAAM,WAAW,KAAK,eAAe;AAAA,IACvC;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ,aAAa;AAAA,MACX,KAAK,WAAW,aAAa,qBAAqB;AAAA,MAClD,MAAM,WAAW,aAAa,uBAAuB;AAAA,MACrD,QAAQ,WAAW,aAAa,wBAAwB;AAAA,IAC1D;AAAA,EACF;AAAA,EACA,MAAM,WAAW,+BAAW;AAAA,EAC5B,UAAU,WAAW,kCAAe;AAAA,EACpC,QAAQ,WAAW,mCAAa;AAAA,EAChC,OAAO,WAAW,iCAAY;AAAA,EAC9B,OAAO,WAAW,iCAAY;AAAA,EAC9B,QAAQ,WAAW,mCAAa;AAAA,EAChC,KAAK,WAAW,6BAAU;AAAA,EAC1B,KAAK,WAAW,6BAAU;AAC5B;",
4
+ "sourcesContent": ["import { ApiClient } from '../api/client'\nimport type commandDefinitions from '../command-definitions'\nimport type { ImplementationTree } from '../command-tree'\nimport { Logger } from '../logger'\nimport type { CommandArgv } from '../typings'\nimport * as utils from '../utils'\nimport { AddCommand } from './add-command'\nimport type { BaseCommand } from './base-command'\nimport * as bots from './bot-commands'\nimport { BuildCommand } from './build-command'\nimport { BundleCommand } from './bundle-command'\nimport { DeployCommand } from './deploy-command'\nimport { DevCommand } from './dev-command'\nimport { GenerateCommand } from './gen-command'\nimport type { GlobalCommand, GlobalCommandDefinition } from './global-command'\nimport { InitCommand } from './init-command'\nimport * as integrations from './integration-commands'\nimport { LoginCommand } from './login-command'\nimport { LogoutCommand } from './logout-command'\nimport { ServeCommand } from './serve-command'\n\ntype GlobalCtor<C extends GlobalCommandDefinition> = new (\n ...args: ConstructorParameters<typeof GlobalCommand<C>>\n) => BaseCommand<C>\n\nconst getHandler =\n <C extends GlobalCommandDefinition>(cls: GlobalCtor<C>) =>\n async (argv: CommandArgv<C>) => {\n const logger = new Logger(argv)\n const prompt = new utils.prompt.CLIPrompt(argv, logger)\n return new cls(ApiClient, prompt, logger, argv).handler()\n }\n\nexport default {\n login: getHandler(LoginCommand),\n logout: getHandler(LogoutCommand),\n bots: {\n subcommands: {\n create: getHandler(bots.CreateBotCommand),\n get: getHandler(bots.GetBotCommand),\n delete: getHandler(bots.DeleteBotCommand),\n list: getHandler(bots.ListBotsCommand),\n },\n },\n integrations: {\n subcommands: {\n get: getHandler(integrations.GetIntegrationCommand),\n list: getHandler(integrations.ListIntegrationsCommand),\n delete: getHandler(integrations.DeleteIntegrationCommand),\n },\n },\n init: getHandler(InitCommand),\n generate: getHandler(GenerateCommand),\n bundle: getHandler(BundleCommand),\n build: getHandler(BuildCommand),\n serve: getHandler(ServeCommand),\n deploy: getHandler(DeployCommand),\n add: getHandler(AddCommand),\n dev: getHandler(DevCommand),\n} satisfies ImplementationTree<typeof commandDefinitions>\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA0B;AAG1B,oBAAuB;AAEvB,YAAuB;AACvB,yBAA2B;AAE3B,WAAsB;AACtB,2BAA6B;AAC7B,4BAA8B;AAC9B,4BAA8B;AAC9B,yBAA2B;AAC3B,yBAAgC;AAEhC,0BAA4B;AAC5B,mBAA8B;AAC9B,2BAA6B;AAC7B,4BAA8B;AAC9B,2BAA6B;AAM7B,MAAM,aACJ,CAAoC,QACpC,OAAO,SAAyB;AAC9B,QAAM,SAAS,IAAI,qBAAO,IAAI;AAC9B,QAAM,SAAS,IAAI,MAAM,OAAO,UAAU,MAAM,MAAM;AACtD,SAAO,IAAI,IAAI,yBAAW,QAAQ,QAAQ,IAAI,EAAE,QAAQ;AAC1D;AAEF,IAAO,kCAAQ;AAAA,EACb,OAAO,WAAW,iCAAY;AAAA,EAC9B,QAAQ,WAAW,mCAAa;AAAA,EAChC,MAAM;AAAA,IACJ,aAAa;AAAA,MACX,QAAQ,WAAW,KAAK,gBAAgB;AAAA,MACxC,KAAK,WAAW,KAAK,aAAa;AAAA,MAClC,QAAQ,WAAW,KAAK,gBAAgB;AAAA,MACxC,MAAM,WAAW,KAAK,eAAe;AAAA,IACvC;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ,aAAa;AAAA,MACX,KAAK,WAAW,aAAa,qBAAqB;AAAA,MAClD,MAAM,WAAW,aAAa,uBAAuB;AAAA,MACrD,QAAQ,WAAW,aAAa,wBAAwB;AAAA,IAC1D;AAAA,EACF;AAAA,EACA,MAAM,WAAW,+BAAW;AAAA,EAC5B,UAAU,WAAW,kCAAe;AAAA,EACpC,QAAQ,WAAW,mCAAa;AAAA,EAChC,OAAO,WAAW,iCAAY;AAAA,EAC9B,OAAO,WAAW,iCAAY;AAAA,EAC9B,QAAQ,WAAW,mCAAa;AAAA,EAChC,KAAK,WAAW,6BAAU;AAAA,EAC1B,KAAK,WAAW,6BAAU;AAC5B;",
6
6
  "names": []
7
7
  }
@@ -85,12 +85,6 @@ class ProjectCommand extends import_global_command.GlobalCommand {
85
85
  await import_fs.default.promises.writeFile(filePath, file.content);
86
86
  }
87
87
  }
88
- prepareIntegrations(botImpl, botInfo) {
89
- const { integrations: integrationList } = botImpl.definition;
90
- const integrationsToUninstall = (0, import_lodash.default)(botInfo.integrations).keys().filter((key) => !integrationList?.map((i) => i.id).includes(key)).zipObject().mapValues(() => null).value();
91
- const integrationsToInstall = (0, import_lodash.default)(integrationList ?? []).keyBy((i) => i.id).mapValues(({ enabled, configuration }) => ({ enabled, configuration })).value();
92
- return { ...integrationsToUninstall, ...integrationsToInstall };
93
- }
94
88
  displayWebhookUrls(bot) {
95
89
  if (!import_lodash.default.keys(bot.integrations).length) {
96
90
  this.logger.debug("No integrations in bot");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/command-implementations/project-command.ts"],
4
- "sourcesContent": ["import type * as bpclient from '@botpress/client'\nimport type { IntegrationDefinition, Bot as BotImpl } from '@botpress/sdk'\nimport type { YargsConfig } from '@bpinternal/yargs-extra'\nimport chalk from 'chalk'\nimport fs from 'fs'\nimport _ from 'lodash'\nimport pathlib from 'path'\nimport * as codegen from '../code-generation'\nimport type * as config from '../config'\nimport * as consts from '../consts'\nimport * as errors from '../errors'\nimport type { CommandArgv, CommandDefinition } from '../typings'\nimport * as utils from '../utils'\nimport { GlobalCommand } from './global-command'\n\ntype IntegrationInstances = Parameters<bpclient.Client['updateBot']>[0]['integrations']\n\nexport type ProjectCommandDefinition = CommandDefinition<typeof config.schemas.project>\nexport type ProjectCache = { botId: string; devId: string }\n\ntype ConfigurableProjectPaths = { entryPoint: string; outDir: string; workDir: string }\ntype ConstantProjectPaths = typeof consts.fromOutDir & typeof consts.fromWorkDir\ntype AllProjectPaths = ConfigurableProjectPaths & ConstantProjectPaths\n\nclass ProjectPaths extends utils.path.PathStore<keyof AllProjectPaths> {\n public constructor(argv: CommandArgv<ProjectCommandDefinition>) {\n const absWorkDir = utils.path.absoluteFrom(utils.path.cwd(), argv.workDir)\n const absEntrypoint = utils.path.absoluteFrom(absWorkDir, argv.entryPoint)\n const absOutDir = utils.path.absoluteFrom(absWorkDir, argv.outDir)\n super({\n workDir: absWorkDir,\n entryPoint: absEntrypoint,\n outDir: absOutDir,\n ..._.mapValues(consts.fromOutDir, (p) => utils.path.absoluteFrom(absOutDir, p)),\n ..._.mapValues(consts.fromWorkDir, (p) => utils.path.absoluteFrom(absWorkDir, p)),\n })\n }\n}\n\nexport abstract class ProjectCommand<C extends ProjectCommandDefinition> extends GlobalCommand<C> {\n protected get projectPaths() {\n return new ProjectPaths(this.argv)\n }\n\n protected get projectCache() {\n return new utils.cache.FSKeyValueCache<ProjectCache>(this.projectPaths.abs.projectCacheFile)\n }\n\n protected async readIntegrationDefinitionFromFS(): Promise<IntegrationDefinition | undefined> {\n const abs = this.projectPaths.abs\n const rel = this.projectPaths.rel('workDir')\n\n if (!fs.existsSync(abs.definition)) {\n this.logger.debug(`Integration definition not found at ${rel.definition}`)\n return\n }\n\n const { outputFiles } = await utils.esbuild.buildEntrypoint({\n cwd: abs.workDir,\n outfile: '',\n entrypoint: rel.definition,\n write: false,\n })\n\n const artifact = outputFiles[0]\n if (!artifact) {\n throw new errors.BotpressCLIError('Could not read integration definition')\n }\n\n const { default: definition } = utils.require.requireJsCode<{ default: IntegrationDefinition }>(artifact.text)\n return definition\n }\n\n protected async writeGeneratedFilesToOutFolder(files: codegen.File[]) {\n for (const file of files) {\n const filePath = utils.path.absoluteFrom(this.projectPaths.abs.outDir, file.path)\n const dirPath = pathlib.dirname(filePath)\n await fs.promises.mkdir(dirPath, { recursive: true })\n await fs.promises.writeFile(filePath, file.content)\n }\n }\n\n protected prepareIntegrations(botImpl: BotImpl, botInfo: bpclient.Bot): IntegrationInstances {\n const { integrations: integrationList } = botImpl.definition\n\n const integrationsToUninstall: IntegrationInstances = _(botInfo.integrations)\n .keys()\n .filter((key) => !integrationList?.map((i) => i.id).includes(key))\n .zipObject()\n .mapValues(() => null)\n .value()\n\n const integrationsToInstall: IntegrationInstances = _(integrationList ?? [])\n .keyBy((i) => i.id)\n .mapValues(({ enabled, configuration }) => ({ enabled, configuration }))\n .value()\n\n return { ...integrationsToUninstall, ...integrationsToInstall }\n }\n\n protected displayWebhookUrls(bot: bpclient.Bot) {\n if (!_.keys(bot.integrations).length) {\n this.logger.debug('No integrations in bot')\n return\n }\n\n this.logger.log('Integrations:')\n for (const integration of Object.values(bot.integrations)) {\n if (!integration.enabled) {\n this.logger.log(`${chalk.grey(integration.name)} ${chalk.italic('(disabled)')}: ${integration.webhookUrl}`, {\n prefix: { symbol: '\u25CB', indent: 2 },\n })\n } else {\n this.logger.log(`${chalk.bold(integration.name)} : ${integration.webhookUrl}`, {\n prefix: { symbol: '\u25CF', indent: 2 },\n })\n }\n }\n }\n\n protected async promptSecrets(\n integrationDef: IntegrationDefinition,\n argv: YargsConfig<typeof config.schemas.secrets>\n ): Promise<Record<string, string>> {\n const { secrets: secretDefinitions } = integrationDef\n if (!secretDefinitions) {\n return {}\n }\n\n const secretArgv = this._parseArgvSecrets(argv.secrets)\n const invalidSecret = Object.keys(secretArgv).find((s) => !secretDefinitions.includes(s))\n if (invalidSecret) {\n throw new errors.BotpressCLIError(`Secret ${invalidSecret} is not defined in integration definition`)\n }\n\n const values: Record<string, string> = {}\n for (const secretDef of secretDefinitions) {\n const argvSecret = secretArgv[secretDef]\n if (argvSecret) {\n this.logger.debug(`Using secret \"${secretDef}\" from argv`)\n values[secretDef] = argvSecret\n continue\n }\n\n const prompted = await this.prompt.text(`Enter value for secret \"${secretDef}\"`)\n if (!prompted) {\n throw new errors.BotpressCLIError('Secret is required')\n }\n values[secretDef] = prompted\n }\n\n const envVariables = _.mapKeys(values, (_v, k) => codegen.secretEnvVariableName(k))\n return envVariables\n }\n\n private _parseArgvSecrets(argvSecrets: string[]): Record<string, string> {\n const parsed: Record<string, string> = {}\n for (const secret of argvSecrets) {\n const [key, value] = this._splitOnce(secret, '=')\n if (!value) {\n throw new errors.BotpressCLIError(\n `Secret \"${key}\" is missing a value. Expected format: \"SECRET_NAME=secretValue\"`\n )\n }\n parsed[key!] = value\n }\n\n return parsed\n }\n\n private _splitOnce = (text: string, separator: string): [string, string | undefined] => {\n const index = text.indexOf(separator)\n if (index === -1) {\n return [text, undefined]\n }\n return [text.slice(0, index), text.slice(index + 1)]\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,mBAAkB;AAClB,gBAAe;AACf,oBAAc;AACd,kBAAoB;AACpB,cAAyB;AAEzB,aAAwB;AACxB,aAAwB;AAExB,YAAuB;AACvB,4BAA8B;AAW9B,MAAM,qBAAqB,MAAM,KAAK,UAAiC;AAAA,EAC9D,YAAY,MAA6C;AAC9D,UAAM,aAAa,MAAM,KAAK,aAAa,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO;AACzE,UAAM,gBAAgB,MAAM,KAAK,aAAa,YAAY,KAAK,UAAU;AACzE,UAAM,YAAY,MAAM,KAAK,aAAa,YAAY,KAAK,MAAM;AACjE,UAAM;AAAA,MACJ,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,GAAG,cAAAA,QAAE,UAAU,OAAO,YAAY,CAAC,MAAM,MAAM,KAAK,aAAa,WAAW,CAAC,CAAC;AAAA,MAC9E,GAAG,cAAAA,QAAE,UAAU,OAAO,aAAa,CAAC,MAAM,MAAM,KAAK,aAAa,YAAY,CAAC,CAAC;AAAA,IAClF,CAAC;AAAA,EACH;AACF;AAEO,MAAe,uBAA2D,oCAAiB;AAAA,EAChG,IAAc,eAAe;AAC3B,WAAO,IAAI,aAAa,KAAK,IAAI;AAAA,EACnC;AAAA,EAEA,IAAc,eAAe;AAC3B,WAAO,IAAI,MAAM,MAAM,gBAA8B,KAAK,aAAa,IAAI,gBAAgB;AAAA,EAC7F;AAAA,EAEA,MAAgB,kCAA8E;AAC5F,UAAM,MAAM,KAAK,aAAa;AAC9B,UAAM,MAAM,KAAK,aAAa,IAAI,SAAS;AAE3C,QAAI,CAAC,UAAAC,QAAG,WAAW,IAAI,UAAU,GAAG;AAClC,WAAK,OAAO,MAAM,uCAAuC,IAAI,YAAY;AACzE;AAAA,IACF;AAEA,UAAM,EAAE,YAAY,IAAI,MAAM,MAAM,QAAQ,gBAAgB;AAAA,MAC1D,KAAK,IAAI;AAAA,MACT,SAAS;AAAA,MACT,YAAY,IAAI;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AAED,UAAM,WAAW,YAAY;AAC7B,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,OAAO,iBAAiB,uCAAuC;AAAA,IAC3E;AAEA,UAAM,EAAE,SAAS,WAAW,IAAI,MAAM,QAAQ,cAAkD,SAAS,IAAI;AAC7G,WAAO;AAAA,EACT;AAAA,EAEA,MAAgB,+BAA+B,OAAuB;AACpE,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAW,MAAM,KAAK,aAAa,KAAK,aAAa,IAAI,QAAQ,KAAK,IAAI;AAChF,YAAM,UAAU,YAAAC,QAAQ,QAAQ,QAAQ;AACxC,YAAM,UAAAD,QAAG,SAAS,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACpD,YAAM,UAAAA,QAAG,SAAS,UAAU,UAAU,KAAK,OAAO;AAAA,IACpD;AAAA,EACF;AAAA,EAEU,oBAAoB,SAAkB,SAA6C;AAC3F,UAAM,EAAE,cAAc,gBAAgB,IAAI,QAAQ;AAElD,UAAM,8BAAgD,cAAAD,SAAE,QAAQ,YAAY,EACzE,KAAK,EACL,OAAO,CAAC,QAAQ,CAAC,iBAAiB,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,SAAS,GAAG,CAAC,EAChE,UAAU,EACV,UAAU,MAAM,IAAI,EACpB,MAAM;AAET,UAAM,4BAA8C,cAAAA,SAAE,mBAAmB,CAAC,CAAC,EACxE,MAAM,CAAC,MAAM,EAAE,EAAE,EACjB,UAAU,CAAC,EAAE,SAAS,cAAc,OAAO,EAAE,SAAS,cAAc,EAAE,EACtE,MAAM;AAET,WAAO,EAAE,GAAG,yBAAyB,GAAG,sBAAsB;AAAA,EAChE;AAAA,EAEU,mBAAmB,KAAmB;AAC9C,QAAI,CAAC,cAAAA,QAAE,KAAK,IAAI,YAAY,EAAE,QAAQ;AACpC,WAAK,OAAO,MAAM,wBAAwB;AAC1C;AAAA,IACF;AAEA,SAAK,OAAO,IAAI,eAAe;AAC/B,eAAW,eAAe,OAAO,OAAO,IAAI,YAAY,GAAG;AACzD,UAAI,CAAC,YAAY,SAAS;AACxB,aAAK,OAAO,IAAI,GAAG,aAAAG,QAAM,KAAK,YAAY,IAAI,KAAK,aAAAA,QAAM,OAAO,YAAY,MAAM,YAAY,cAAc;AAAA,UAC1G,QAAQ,EAAE,QAAQ,UAAK,QAAQ,EAAE;AAAA,QACnC,CAAC;AAAA,MACH,OAAO;AACL,aAAK,OAAO,IAAI,GAAG,aAAAA,QAAM,KAAK,YAAY,IAAI,OAAO,YAAY,cAAc;AAAA,UAC7E,QAAQ,EAAE,QAAQ,UAAK,QAAQ,EAAE;AAAA,QACnC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAgB,cACd,gBACA,MACiC;AACjC,UAAM,EAAE,SAAS,kBAAkB,IAAI;AACvC,QAAI,CAAC,mBAAmB;AACtB,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,aAAa,KAAK,kBAAkB,KAAK,OAAO;AACtD,UAAM,gBAAgB,OAAO,KAAK,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,kBAAkB,SAAS,CAAC,CAAC;AACxF,QAAI,eAAe;AACjB,YAAM,IAAI,OAAO,iBAAiB,UAAU,wDAAwD;AAAA,IACtG;AAEA,UAAM,SAAiC,CAAC;AACxC,eAAW,aAAa,mBAAmB;AACzC,YAAM,aAAa,WAAW;AAC9B,UAAI,YAAY;AACd,aAAK,OAAO,MAAM,iBAAiB,sBAAsB;AACzD,eAAO,aAAa;AACpB;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,KAAK,OAAO,KAAK,2BAA2B,YAAY;AAC/E,UAAI,CAAC,UAAU;AACb,cAAM,IAAI,OAAO,iBAAiB,oBAAoB;AAAA,MACxD;AACA,aAAO,aAAa;AAAA,IACtB;AAEA,UAAM,eAAe,cAAAH,QAAE,QAAQ,QAAQ,CAAC,IAAI,MAAM,QAAQ,sBAAsB,CAAC,CAAC;AAClF,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,aAA+C;AACvE,UAAM,SAAiC,CAAC;AACxC,eAAW,UAAU,aAAa;AAChC,YAAM,CAAC,KAAK,KAAK,IAAI,KAAK,WAAW,QAAQ,GAAG;AAChD,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,OAAO;AAAA,UACf,WAAW;AAAA,QACb;AAAA,MACF;AACA,aAAO,OAAQ;AAAA,IACjB;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,CAAC,MAAc,cAAoD;AACtF,UAAM,QAAQ,KAAK,QAAQ,SAAS;AACpC,QAAI,UAAU,IAAI;AAChB,aAAO,CAAC,MAAM,MAAS;AAAA,IACzB;AACA,WAAO,CAAC,KAAK,MAAM,GAAG,KAAK,GAAG,KAAK,MAAM,QAAQ,CAAC,CAAC;AAAA,EACrD;AACF;",
4
+ "sourcesContent": ["import type * as bpclient from '@botpress/client'\nimport type * as bpsdk from '@botpress/sdk'\nimport type { YargsConfig } from '@bpinternal/yargs-extra'\nimport chalk from 'chalk'\nimport fs from 'fs'\nimport _ from 'lodash'\nimport pathlib from 'path'\nimport * as codegen from '../code-generation'\nimport type * as config from '../config'\nimport * as consts from '../consts'\nimport * as errors from '../errors'\nimport type { CommandArgv, CommandDefinition } from '../typings'\nimport * as utils from '../utils'\nimport { GlobalCommand } from './global-command'\n\nexport type ProjectCommandDefinition = CommandDefinition<typeof config.schemas.project>\nexport type ProjectCache = { botId: string; devId: string }\n\ntype ConfigurableProjectPaths = { entryPoint: string; outDir: string; workDir: string }\ntype ConstantProjectPaths = typeof consts.fromOutDir & typeof consts.fromWorkDir\ntype AllProjectPaths = ConfigurableProjectPaths & ConstantProjectPaths\n\nclass ProjectPaths extends utils.path.PathStore<keyof AllProjectPaths> {\n public constructor(argv: CommandArgv<ProjectCommandDefinition>) {\n const absWorkDir = utils.path.absoluteFrom(utils.path.cwd(), argv.workDir)\n const absEntrypoint = utils.path.absoluteFrom(absWorkDir, argv.entryPoint)\n const absOutDir = utils.path.absoluteFrom(absWorkDir, argv.outDir)\n super({\n workDir: absWorkDir,\n entryPoint: absEntrypoint,\n outDir: absOutDir,\n ..._.mapValues(consts.fromOutDir, (p) => utils.path.absoluteFrom(absOutDir, p)),\n ..._.mapValues(consts.fromWorkDir, (p) => utils.path.absoluteFrom(absWorkDir, p)),\n })\n }\n}\n\nexport abstract class ProjectCommand<C extends ProjectCommandDefinition> extends GlobalCommand<C> {\n protected get projectPaths() {\n return new ProjectPaths(this.argv)\n }\n\n protected get projectCache() {\n return new utils.cache.FSKeyValueCache<ProjectCache>(this.projectPaths.abs.projectCacheFile)\n }\n\n protected async readIntegrationDefinitionFromFS(): Promise<bpsdk.IntegrationDefinition | undefined> {\n const abs = this.projectPaths.abs\n const rel = this.projectPaths.rel('workDir')\n\n if (!fs.existsSync(abs.definition)) {\n this.logger.debug(`Integration definition not found at ${rel.definition}`)\n return\n }\n\n const { outputFiles } = await utils.esbuild.buildEntrypoint({\n cwd: abs.workDir,\n outfile: '',\n entrypoint: rel.definition,\n write: false,\n })\n\n const artifact = outputFiles[0]\n if (!artifact) {\n throw new errors.BotpressCLIError('Could not read integration definition')\n }\n\n const { default: definition } = utils.require.requireJsCode<{ default: bpsdk.IntegrationDefinition }>(artifact.text)\n return definition\n }\n\n protected async writeGeneratedFilesToOutFolder(files: codegen.File[]) {\n for (const file of files) {\n const filePath = utils.path.absoluteFrom(this.projectPaths.abs.outDir, file.path)\n const dirPath = pathlib.dirname(filePath)\n await fs.promises.mkdir(dirPath, { recursive: true })\n await fs.promises.writeFile(filePath, file.content)\n }\n }\n\n protected displayWebhookUrls(bot: bpclient.Bot) {\n if (!_.keys(bot.integrations).length) {\n this.logger.debug('No integrations in bot')\n return\n }\n\n this.logger.log('Integrations:')\n for (const integration of Object.values(bot.integrations)) {\n if (!integration.enabled) {\n this.logger.log(`${chalk.grey(integration.name)} ${chalk.italic('(disabled)')}: ${integration.webhookUrl}`, {\n prefix: { symbol: '\u25CB', indent: 2 },\n })\n } else {\n this.logger.log(`${chalk.bold(integration.name)} : ${integration.webhookUrl}`, {\n prefix: { symbol: '\u25CF', indent: 2 },\n })\n }\n }\n }\n\n protected async promptSecrets(\n integrationDef: bpsdk.IntegrationDefinition,\n argv: YargsConfig<typeof config.schemas.secrets>\n ): Promise<Record<string, string>> {\n const { secrets: secretDefinitions } = integrationDef\n if (!secretDefinitions) {\n return {}\n }\n\n const secretArgv = this._parseArgvSecrets(argv.secrets)\n const invalidSecret = Object.keys(secretArgv).find((s) => !secretDefinitions.includes(s))\n if (invalidSecret) {\n throw new errors.BotpressCLIError(`Secret ${invalidSecret} is not defined in integration definition`)\n }\n\n const values: Record<string, string> = {}\n for (const secretDef of secretDefinitions) {\n const argvSecret = secretArgv[secretDef]\n if (argvSecret) {\n this.logger.debug(`Using secret \"${secretDef}\" from argv`)\n values[secretDef] = argvSecret\n continue\n }\n\n const prompted = await this.prompt.text(`Enter value for secret \"${secretDef}\"`)\n if (!prompted) {\n throw new errors.BotpressCLIError('Secret is required')\n }\n values[secretDef] = prompted\n }\n\n const envVariables = _.mapKeys(values, (_v, k) => codegen.secretEnvVariableName(k))\n return envVariables\n }\n\n private _parseArgvSecrets(argvSecrets: string[]): Record<string, string> {\n const parsed: Record<string, string> = {}\n for (const secret of argvSecrets) {\n const [key, value] = this._splitOnce(secret, '=')\n if (!value) {\n throw new errors.BotpressCLIError(\n `Secret \"${key}\" is missing a value. Expected format: \"SECRET_NAME=secretValue\"`\n )\n }\n parsed[key!] = value\n }\n\n return parsed\n }\n\n private _splitOnce = (text: string, separator: string): [string, string | undefined] => {\n const index = text.indexOf(separator)\n if (index === -1) {\n return [text, undefined]\n }\n return [text.slice(0, index), text.slice(index + 1)]\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["_", "fs", "pathlib", "chalk"]
7
7
  }
@@ -18,10 +18,11 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var record_utils_exports = {};
20
20
  __export(record_utils_exports, {
21
- setOnNullMissingValues: () => setOnNullMissingValues
21
+ setNullOnMissingValues: () => setNullOnMissingValues,
22
+ zipObjects: () => zipObjects
22
23
  });
23
24
  module.exports = __toCommonJS(record_utils_exports);
24
- const setOnNullMissingValues = (record = {}, oldRecord = {}) => {
25
+ const setNullOnMissingValues = (record = {}, oldRecord = {}) => {
25
26
  const newRecord = {};
26
27
  for (const [key, value] of Object.entries(record)) {
27
28
  newRecord[key] = value;
@@ -33,8 +34,17 @@ const setOnNullMissingValues = (record = {}, oldRecord = {}) => {
33
34
  }
34
35
  return newRecord;
35
36
  };
37
+ const zipObjects = (recordA, recordB) => {
38
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(recordA), ...Object.keys(recordB)]);
39
+ const newRecord = {};
40
+ for (const key of allKeys) {
41
+ newRecord[key] = [recordA[key] ?? null, recordB[key] ?? null];
42
+ }
43
+ return newRecord;
44
+ };
36
45
  // Annotate the CommonJS export names for ESM import in node:
37
46
  0 && (module.exports = {
38
- setOnNullMissingValues
47
+ setNullOnMissingValues,
48
+ zipObjects
39
49
  });
40
50
  //# sourceMappingURL=record-utils.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/record-utils.ts"],
4
- "sourcesContent": ["export const setOnNullMissingValues = <A, B>(\n record: Record<string, A> = {},\n oldRecord: Record<string, B> = {}\n): Record<string, A | null> => {\n const newRecord: Record<string, A | null> = {}\n\n for (const [key, value] of Object.entries(record)) {\n newRecord[key] = value\n }\n\n for (const value of Object.keys(oldRecord)) {\n if (!record[value]) {\n newRecord[value] = null\n }\n }\n\n return newRecord\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,yBAAyB,CACpC,SAA4B,CAAC,GAC7B,YAA+B,CAAC,MACH;AAC7B,QAAM,YAAsC,CAAC;AAE7C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,cAAU,OAAO;AAAA,EACnB;AAEA,aAAW,SAAS,OAAO,KAAK,SAAS,GAAG;AAC1C,QAAI,CAAC,OAAO,QAAQ;AAClB,gBAAU,SAAS;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["export const setNullOnMissingValues = <A, B>(\n record: Record<string, A> = {},\n oldRecord: Record<string, B> = {}\n): Record<string, A | null> => {\n const newRecord: Record<string, A | null> = {}\n\n for (const [key, value] of Object.entries(record)) {\n newRecord[key] = value\n }\n\n for (const value of Object.keys(oldRecord)) {\n if (!record[value]) {\n newRecord[value] = null\n }\n }\n\n return newRecord\n}\n\nexport const zipObjects = <A, B>(\n recordA: Record<string, A>,\n recordB: Record<string, B>\n): Record<string, [A | null, B | null]> => {\n const allKeys = new Set([...Object.keys(recordA), ...Object.keys(recordB)])\n const newRecord: Record<string, [A | null, B | null]> = {}\n\n for (const key of allKeys) {\n newRecord[key] = [recordA[key] ?? null, recordB[key] ?? null]\n }\n\n return newRecord\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,yBAAyB,CACpC,SAA4B,CAAC,GAC7B,YAA+B,CAAC,MACH;AAC7B,QAAM,YAAsC,CAAC;AAE7C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,cAAU,OAAO;AAAA,EACnB;AAEA,aAAW,SAAS,OAAO,KAAK,SAAS,GAAG;AAC1C,QAAI,CAAC,OAAO,QAAQ;AAClB,gBAAU,SAAS;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AACT;AAEO,MAAM,aAAa,CACxB,SACA,YACyC;AACzC,QAAM,UAAU,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC;AAC1E,QAAM,YAAkD,CAAC;AAEzD,aAAW,OAAO,SAAS;AACzB,cAAU,OAAO,CAAC,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,IAAI;AAAA,EAC9D;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
18
+ mod
19
+ ));
20
+ var import_vitest = require("vitest");
21
+ var recordUtils = __toESM(require("./record-utils"));
22
+ (0, import_vitest.test)("zip objects should return both values only when key is defined in both objects", () => {
23
+ const objA = { a: 1, b: 2, c: 3 };
24
+ const objB = { c: 4, d: 5 };
25
+ const zipped = recordUtils.zipObjects(objA, objB);
26
+ const expected = {
27
+ a: [1, null],
28
+ b: [2, null],
29
+ c: [3, 4],
30
+ d: [null, 5]
31
+ };
32
+ (0, import_vitest.expect)(zipped).toEqual(expected);
33
+ });
34
+ //# sourceMappingURL=record-utils.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/record-utils.test.ts"],
4
+ "sourcesContent": ["import { test, expect } from 'vitest'\nimport * as recordUtils from './record-utils'\n\ntest('zip objects should return both values only when key is defined in both objects', () => {\n const objA = { a: 1, b: 2, c: 3 }\n const objB = { c: 4, d: 5 }\n\n const zipped = recordUtils.zipObjects(objA, objB)\n\n const expected = {\n a: [1, null],\n b: [2, null],\n c: [3, 4],\n d: [null, 5],\n }\n\n expect(zipped).toEqual(expected)\n})\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;AAAA,oBAA6B;AAC7B,kBAA6B;AAAA,IAE7B,oBAAK,kFAAkF,MAAM;AAC3F,QAAM,OAAO,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AAChC,QAAM,OAAO,EAAE,GAAG,GAAG,GAAG,EAAE;AAE1B,QAAM,SAAS,YAAY,WAAW,MAAM,IAAI;AAEhD,QAAM,WAAW;AAAA,IACf,GAAG,CAAC,GAAG,IAAI;AAAA,IACX,GAAG,CAAC,GAAG,IAAI;AAAA,IACX,GAAG,CAAC,GAAG,CAAC;AAAA,IACR,GAAG,CAAC,MAAM,CAAC;AAAA,EACb;AAEA,4BAAO,MAAM,EAAE,QAAQ,QAAQ;AACjC,CAAC;",
6
+ "names": []
7
+ }
package/e2e/api.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { Client } from '@botpress/client'
2
+
3
+ export type ApiBot = Awaited<ReturnType<Client['listBots']>>['bots'][0]
4
+ export const fetchAllBots = async (client: Client): Promise<ApiBot[]> => {
5
+ let allBots: ApiBot[] = []
6
+ let nextToken: string | undefined = undefined
7
+ do {
8
+ const { bots, meta } = await client.listBots({ nextToken })
9
+ allBots = allBots.concat(bots)
10
+ nextToken = meta.nextToken
11
+ } while (nextToken)
12
+ return allBots
13
+ }
14
+
15
+ export type ApiIntegration = Awaited<ReturnType<Client['listIntegrations']>>['integrations'][0]
16
+ export const fetchAllIntegrations = async (client: Client): Promise<ApiIntegration[]> => {
17
+ let allIntegrations: ApiIntegration[] = []
18
+ let nextToken: string | undefined = undefined
19
+ do {
20
+ const { integrations, meta } = await client.listIntegrations({ nextToken })
21
+ allIntegrations = allIntegrations.concat(integrations)
22
+ nextToken = meta.nextToken
23
+ } while (nextToken)
24
+ return allIntegrations
25
+ }
@@ -0,0 +1,20 @@
1
+ import * as consts from '../src/consts'
2
+
3
+ const noBuild = false
4
+ const secrets = [] satisfies string[]
5
+ const sourceMap = false
6
+ const verbose = false
7
+ const confirm = true
8
+ const json = false
9
+ const entryPoint = consts.defaultEntrypoint
10
+ const outDir = consts.defaultOutputFolder
11
+ export default {
12
+ noBuild,
13
+ secrets,
14
+ sourceMap,
15
+ verbose,
16
+ confirm,
17
+ json,
18
+ entryPoint,
19
+ outDir,
20
+ }
package/e2e/index.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { Logger } from '@bpinternal/log4bot'
2
+ import yargs, { YargsConfig, YargsSchema } from '@bpinternal/yargs-extra'
3
+ import * as consts from '../src/consts'
4
+ import { createDeployBot } from './tests/create-deploy-bot'
5
+ import { createDeployIntegration } from './tests/create-deploy-integration'
6
+ import { devBot } from './tests/dev-bot'
7
+ import { Test } from './typings'
8
+ import { sleep, TmpDirectory } from './utils'
9
+
10
+ const tests: Test[] = [createDeployBot, createDeployIntegration, devBot]
11
+
12
+ const timeout = (ms: number) =>
13
+ sleep(ms).then(() => {
14
+ throw new Error(`Timeout after ${ms}ms`)
15
+ })
16
+
17
+ const TIMEOUT = 20_000
18
+
19
+ const configSchema = {
20
+ timeout: {
21
+ type: 'number',
22
+ default: TIMEOUT,
23
+ },
24
+ verbose: {
25
+ type: 'boolean',
26
+ default: false,
27
+ alias: 'v',
28
+ },
29
+ filter: {
30
+ type: 'string',
31
+ },
32
+ workspaceId: {
33
+ type: 'string',
34
+ demandOption: true,
35
+ },
36
+ token: {
37
+ type: 'string',
38
+ demandOption: true,
39
+ },
40
+ apiUrl: {
41
+ type: 'string',
42
+ default: consts.defaultBotpressApiUrl,
43
+ },
44
+ tunnelUrl: {
45
+ type: 'string',
46
+ default: consts.defaultTunnelUrl,
47
+ },
48
+ } satisfies YargsSchema
49
+
50
+ const main = async (argv: YargsConfig<typeof configSchema>): Promise<never> => {
51
+ const logger = new Logger('e2e', { level: argv.verbose ? 'debug' : 'info' })
52
+
53
+ const filterRegex = argv.filter ? new RegExp(argv.filter) : null
54
+ const filteredTests = tests.filter(({ name }) => (filterRegex ? filterRegex.test(name) : true))
55
+ logger.info(`Running ${filteredTests.length} / ${tests.length} tests`)
56
+
57
+ for (const { name, handler } of filteredTests) {
58
+ const logLine = `### Running test: "${name}" ###`
59
+ const logPad = '#'.repeat(logLine.length)
60
+ logger.info(logPad)
61
+ logger.info(logLine)
62
+ logger.info(logPad + '\n')
63
+
64
+ const tmpDir = TmpDirectory.create()
65
+ try {
66
+ const t0 = Date.now()
67
+ await Promise.race([handler({ tmpDir: tmpDir.path, ...argv }), timeout(argv.timeout)])
68
+ const t1 = Date.now()
69
+ logger.info(`SUCCESS: "${name}" (${t1 - t0}ms)`)
70
+ } catch (thrown) {
71
+ const err = thrown instanceof Error ? thrown : new Error(`${thrown}`)
72
+ logger.attachError(err).error(`FAILURE: "${name}"`)
73
+ process.exit(1)
74
+ } finally {
75
+ tmpDir.cleanup()
76
+ }
77
+ }
78
+
79
+ logger.info('All tests passed')
80
+ process.exit(0)
81
+ }
82
+
83
+ void yargs.command('$0', 'Run E2E Tests', configSchema, main).parse()
@@ -0,0 +1,50 @@
1
+ import { Client } from '@botpress/client'
2
+ import pathlib from 'path'
3
+ import * as uuid from 'uuid'
4
+ import impl from '../../src/command-implementations'
5
+ import { ApiBot, fetchAllBots } from '../api'
6
+ import defaults from '../defaults'
7
+ import { Test } from '../typings'
8
+ import * as utils from '../utils'
9
+
10
+ const fetchBot = async (client: Client, botName: string): Promise<ApiBot | undefined> => {
11
+ const bots = await fetchAllBots(client)
12
+ return bots.find(({ name }) => name === botName)
13
+ }
14
+
15
+ export const createDeployBot: Test = {
16
+ name: 'cli should allow creating, building, deploying and mannaging a bot',
17
+ handler: async ({ tmpDir, ...creds }) => {
18
+ const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
19
+ const baseDir = pathlib.join(tmpDir, 'bots')
20
+ const botName = uuid.v4()
21
+ const botDir = pathlib.join(baseDir, botName)
22
+
23
+ const argv = {
24
+ ...defaults,
25
+ botpressHome: botpressHomeDir,
26
+ confirm: true,
27
+ ...creds,
28
+ }
29
+
30
+ const client = new Client({ host: creds.apiUrl, token: creds.token, workspaceId: creds.workspaceId })
31
+
32
+ await impl.init({ ...argv, workDir: baseDir, name: botName, type: 'bot' }).then(utils.handleExitCode)
33
+ await utils.npmInstall({ workDir: botDir }).then(utils.handleExitCode)
34
+ await impl.build({ ...argv, workDir: botDir }).then(utils.handleExitCode)
35
+ await impl.login({ ...argv }).then(utils.handleExitCode)
36
+ await impl.bots.subcommands.create({ ...argv, name: botName }).then(utils.handleExitCode)
37
+
38
+ const bot = await fetchBot(client, botName)
39
+ if (!bot) {
40
+ throw new Error(`Bot ${botName} should have been created`)
41
+ }
42
+
43
+ await impl.deploy({ ...argv, workDir: botDir, createNewBot: false, botId: bot.id }).then(utils.handleExitCode)
44
+ await impl.bots.subcommands.delete({ ...argv, botRef: bot.id }).then(utils.handleExitCode)
45
+
46
+ if (await fetchBot(client, botName)) {
47
+ throw new Error(`Bot ${botName} should have been deleted`)
48
+ }
49
+ },
50
+ }
@@ -0,0 +1,55 @@
1
+ import { Client } from '@botpress/client'
2
+
3
+ import pathlib from 'path'
4
+ import * as uuid from 'uuid'
5
+ import impl from '../../src/command-implementations'
6
+ import { ApiIntegration, fetchAllIntegrations } from '../api'
7
+ import defaults from '../defaults'
8
+ import { Test } from '../typings'
9
+ import * as utils from '../utils'
10
+
11
+ const fetchIntegration = async (client: Client, integrationName: string): Promise<ApiIntegration | undefined> => {
12
+ const integrations = await fetchAllIntegrations(client)
13
+ return integrations.find(({ name }) => name === integrationName)
14
+ }
15
+
16
+ export const createDeployIntegration: Test = {
17
+ name: 'cli should allow creating, building, deploying and mannaging an integration',
18
+ handler: async ({ tmpDir, ...creds }) => {
19
+ const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
20
+ const baseDir = pathlib.join(tmpDir, 'integrations')
21
+ const integrationName = `myintegration-${uuid.v4()}`.replace(/-/g, '')
22
+ const integrationDir = pathlib.join(baseDir, integrationName)
23
+
24
+ const argv = {
25
+ ...defaults,
26
+ botpressHome: botpressHomeDir,
27
+ confirm: true,
28
+ ...creds,
29
+ }
30
+
31
+ const client = new Client({ host: creds.apiUrl, token: creds.token, workspaceId: creds.workspaceId })
32
+
33
+ await impl
34
+ .init({ ...argv, workDir: baseDir, name: integrationName, type: 'integration' })
35
+ .then(utils.handleExitCode)
36
+ await utils.npmInstall({ workDir: integrationDir }).then(utils.handleExitCode)
37
+ await impl.build({ ...argv, workDir: integrationDir }).then(utils.handleExitCode)
38
+ await impl.login({ ...argv }).then(utils.handleExitCode)
39
+
40
+ await impl
41
+ .deploy({ ...argv, createNewBot: undefined, botId: undefined, workDir: integrationDir })
42
+ .then(utils.handleExitCode)
43
+
44
+ const integration = await fetchIntegration(client, integrationName)
45
+ if (!integration) {
46
+ throw new Error(`Integration ${integrationName} should have been created`)
47
+ }
48
+
49
+ await impl.integrations.subcommands.delete({ ...argv, integrationRef: integration.id }).then(utils.handleExitCode)
50
+
51
+ if (await fetchIntegration(client, integrationName)) {
52
+ throw new Error(`Integration ${integrationName} should have been deleted`)
53
+ }
54
+ },
55
+ }
@@ -0,0 +1,57 @@
1
+ import findProcess from 'find-process'
2
+ import pathlib from 'path'
3
+ import * as uuid from 'uuid'
4
+ import impl from '../../src/command-implementations'
5
+ import defaults from '../defaults'
6
+ import { Test } from '../typings'
7
+ import * as utils from '../utils'
8
+
9
+ const handleExitCode = ({ exitCode }: { exitCode: number }) => {
10
+ if (exitCode !== 0) {
11
+ throw new Error(`Command exited with code ${exitCode}`)
12
+ }
13
+ }
14
+
15
+ const PORT = 8075
16
+
17
+ export const devBot: Test = {
18
+ name: 'cli should allow creating and running a bot locally',
19
+ handler: async ({ tmpDir, tunnelUrl, ...creds }) => {
20
+ const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
21
+ const baseDir = pathlib.join(tmpDir, 'bots')
22
+ const botName = uuid.v4()
23
+ const botDir = pathlib.join(baseDir, botName)
24
+
25
+ const argv = {
26
+ ...defaults,
27
+ botpressHome: botpressHomeDir,
28
+ confirm: true,
29
+ ...creds,
30
+ }
31
+
32
+ await impl.init({ ...argv, workDir: baseDir, name: botName, type: 'bot' }).then(handleExitCode)
33
+ await utils.npmInstall({ workDir: botDir }).then(handleExitCode)
34
+ await impl.login({ ...argv }).then(handleExitCode)
35
+
36
+ const cmdPromise = impl.dev({ ...argv, workDir: botDir, port: PORT, tunnelUrl }).then(handleExitCode)
37
+ await utils.sleep(5000)
38
+
39
+ const allProcess = await findProcess('port', PORT)
40
+ if (allProcess.length === 0) {
41
+ throw new Error(`Expected to find a process listening on port ${PORT}`)
42
+ }
43
+ if (allProcess.length > 1) {
44
+ throw new Error(`Expected to find only one process listening on port ${PORT}`)
45
+ }
46
+ const botProcess = allProcess[0]
47
+
48
+ /**
49
+ * TODO:
50
+ * - try calling the Bot locally to see if it works
51
+ * - allow listing dev bots in API and find the one we just created (by name)
52
+ */
53
+
54
+ process.kill(botProcess.pid)
55
+ await cmdPromise
56
+ },
57
+ }
package/e2e/typings.ts ADDED
@@ -0,0 +1,12 @@
1
+ export type TestProps = {
2
+ tmpDir: string
3
+ workspaceId: string
4
+ token: string
5
+ apiUrl: string
6
+ tunnelUrl: string
7
+ }
8
+
9
+ export type Test = {
10
+ name: string
11
+ handler: (props: TestProps) => Promise<void>
12
+ }
package/e2e/utils.ts ADDED
@@ -0,0 +1,42 @@
1
+ import childprocess from 'child_process'
2
+ import tmp from 'tmp'
3
+
4
+ export const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))
5
+
6
+ export class TmpDirectory {
7
+ private _closed = false
8
+
9
+ public static create() {
10
+ return new TmpDirectory(tmp.dirSync({ unsafeCleanup: true }))
11
+ }
12
+
13
+ private constructor(private _res: tmp.DirResult) {}
14
+
15
+ public get path() {
16
+ if (this._closed) {
17
+ throw new Error('Cannot access tmp directory after cleanup')
18
+ }
19
+ return this._res.name
20
+ }
21
+
22
+ public cleanup() {
23
+ if (this._closed) {
24
+ return
25
+ }
26
+ this._res.removeCallback()
27
+ }
28
+ }
29
+
30
+ export const npmInstall = async ({ workDir }: { workDir: string }) => {
31
+ const { status } = childprocess.spawnSync('pnpm', ['install'], {
32
+ cwd: workDir,
33
+ stdio: 'inherit',
34
+ })
35
+ return { exitCode: status ?? 0 }
36
+ }
37
+
38
+ export const handleExitCode = ({ exitCode }: { exitCode: number }) => {
39
+ if (exitCode !== 0) {
40
+ throw new Error(`Command exited with code ${exitCode}`)
41
+ }
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botpress/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Botpress CLI",
5
5
  "scripts": {
6
6
  "build": "pnpm run bundle && pnpm run template:gen",
@@ -8,7 +8,9 @@
8
8
  "start": "node dist/index.js",
9
9
  "type:check": "tsc --noEmit",
10
10
  "bundle": "ts-node -T build.ts",
11
- "template:gen": "pnpm -r --stream -F echo-bot -F empty-integration exec bp gen"
11
+ "template:gen": "pnpm -r --stream -F echo-bot -F empty-integration exec bp gen",
12
+ "e2e:test": "ts-node -T ./e2e",
13
+ "unit:test": "pnpm vitest --run"
12
14
  },
13
15
  "keywords": [],
14
16
  "author": "",
@@ -18,8 +20,8 @@
18
20
  },
19
21
  "main": "dist/index.js",
20
22
  "dependencies": {
21
- "@botpress/client": "0.1.3",
22
- "@bpinternal/tunnel": "^0.0.2",
23
+ "@botpress/client": "0.2.0",
24
+ "@bpinternal/tunnel": "^0.1.0",
23
25
  "@bpinternal/yargs-extra": "^0.0.3",
24
26
  "@parcel/watcher": "^2.1.0",
25
27
  "@types/lodash": "^4.14.191",
@@ -45,12 +47,16 @@
45
47
  "zod-to-json-schema": "^3.20.1"
46
48
  },
47
49
  "devDependencies": {
48
- "@botpress/sdk": "0.1.5",
50
+ "@botpress/sdk": "0.1.6",
51
+ "@bpinternal/log4bot": "^0.0.4",
49
52
  "@types/bluebird": "^3.5.38",
50
53
  "@types/prompts": "^2.0.14",
51
54
  "@types/semver": "^7.3.11",
55
+ "@types/tmp": "^0.2.3",
52
56
  "@types/uuid": "^9.0.1",
57
+ "find-process": "^1.4.7",
53
58
  "glob": "^9.3.4",
59
+ "tmp": "^0.2.1",
54
60
  "ts-node": "^10.9.1"
55
61
  },
56
62
  "engines": {