@botpress/cli 4.17.11 → 4.17.13
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/.turbo/turbo-build.log
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
|
|
2
|
-
> @botpress/cli@4.17.
|
|
2
|
+
> @botpress/cli@4.17.13 build /home/runner/work/botpress/botpress/packages/cli
|
|
3
3
|
> pnpm run bundle && pnpm run template:gen
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @botpress/cli@4.17.
|
|
6
|
+
> @botpress/cli@4.17.13 bundle /home/runner/work/botpress/botpress/packages/cli
|
|
7
7
|
> ts-node -T build.ts
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
> @botpress/cli@4.17.
|
|
10
|
+
> @botpress/cli@4.17.13 template:gen /home/runner/work/botpress/botpress/packages/cli
|
|
11
11
|
> pnpm -r --stream -F @bp-templates/* exec bp gen
|
|
12
12
|
|
|
13
|
-
🤖 Botpress CLI v4.17.
|
|
14
|
-
🤖 Botpress CLI v4.17.
|
|
15
|
-
🤖 Botpress CLI v4.17.
|
|
16
|
-
🤖 Botpress CLI v4.17.
|
|
17
|
-
○ Generating typings for integration
|
|
13
|
+
🤖 Botpress CLI v4.17.13
|
|
14
|
+
🤖 Botpress CLI v4.17.13
|
|
15
|
+
🤖 Botpress CLI v4.17.13
|
|
16
|
+
🤖 Botpress CLI v4.17.13
|
|
17
|
+
○ Generating typings for integration empty-integration...
|
|
18
18
|
✓ Typings available at .botpress
|
|
19
19
|
|
|
20
20
|
○ Generating typings for plugin empty-plugin...
|
|
21
21
|
✓ Typings available at .botpress
|
|
22
22
|
|
|
23
|
-
○ Generating typings for
|
|
23
|
+
○ Generating typings for bot...
|
|
24
|
+
○ Generating typings for integration hello-world...
|
|
24
25
|
✓ Typings available at .botpress
|
|
25
26
|
|
|
26
|
-
○ Generating typings for bot...
|
|
27
27
|
✓ Typings available at .botpress
|
|
28
28
|
|
|
29
|
-
🤖 Botpress CLI v4.17.
|
|
29
|
+
🤖 Botpress CLI v4.17.13
|
|
30
30
|
○ Generating typings for integration webhook-message...
|
|
31
31
|
✓ Typings available at .botpress
|
|
32
32
|
|
|
@@ -33,6 +33,7 @@ __export(dev_command_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(dev_command_exports);
|
|
34
34
|
var import_axios = __toESM(require("axios"));
|
|
35
35
|
var import_chalk = __toESM(require("chalk"));
|
|
36
|
+
var import_lodash = require("lodash");
|
|
36
37
|
var pathlib = __toESM(require("path"));
|
|
37
38
|
var uuid = __toESM(require("uuid"));
|
|
38
39
|
var apiUtils = __toESM(require("../api"));
|
|
@@ -48,6 +49,7 @@ const TUNNEL_HELLO_INTERVAL = 5e3;
|
|
|
48
49
|
const FILEWATCHER_DEBOUNCE_MS = 2e3;
|
|
49
50
|
class DevCommand extends import_project_command.ProjectCommand {
|
|
50
51
|
_initialDef = void 0;
|
|
52
|
+
_cacheDevRequestBody;
|
|
51
53
|
async run() {
|
|
52
54
|
this.logger.warn("This command is experimental and subject to breaking changes without notice.");
|
|
53
55
|
const api = await this.ensureLoginAndCreateClient(this.argv);
|
|
@@ -289,8 +291,6 @@ class DevCommand extends import_project_command.ProjectCommand {
|
|
|
289
291
|
createLine.commit();
|
|
290
292
|
await this.projectCache.set("devId", bot.id);
|
|
291
293
|
}
|
|
292
|
-
const updateLine = this.logger.line();
|
|
293
|
-
updateLine.started("Deploying dev bot...");
|
|
294
294
|
const updateBotBody = apiUtils.prepareUpdateBotBody(
|
|
295
295
|
{
|
|
296
296
|
...await apiUtils.prepareCreateBotBody(botDef),
|
|
@@ -300,6 +300,12 @@ class DevCommand extends import_project_command.ProjectCommand {
|
|
|
300
300
|
},
|
|
301
301
|
bot
|
|
302
302
|
);
|
|
303
|
+
if (!await this._didDefinitionChange(updateBotBody)) {
|
|
304
|
+
this.logger.log("Skipping deployment step. No changes found in bot.definition.ts");
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const updateLine = this.logger.line();
|
|
308
|
+
updateLine.started("Deploying dev bot...");
|
|
303
309
|
const { bot: updatedBot } = await api.client.updateBot(updateBotBody).catch((thrown) => {
|
|
304
310
|
throw errors.BotpressCLIError.wrap(thrown, "Could not deploy dev bot");
|
|
305
311
|
});
|
|
@@ -315,6 +321,11 @@ ${Object.entries(failedIntegrations).map(([key, int]) => `\u2022 ${key}: ${int.s
|
|
|
315
321
|
await tablesPublisher.deployTables({ botId: updatedBot.id, botDefinition: botDef });
|
|
316
322
|
this.displayWebhookUrls(updatedBot);
|
|
317
323
|
}
|
|
324
|
+
async _didDefinitionChange(body) {
|
|
325
|
+
const didChange = !(0, import_lodash.isEqual)(body, this._cacheDevRequestBody);
|
|
326
|
+
this._cacheDevRequestBody = { ...body };
|
|
327
|
+
return didChange;
|
|
328
|
+
}
|
|
318
329
|
_forwardTunnelRequest = async (baseUrl, request) => {
|
|
319
330
|
const axiosConfig = {
|
|
320
331
|
method: request.method,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/command-implementations/dev-command.ts"],
|
|
4
|
-
"sourcesContent": ["import type * as client from '@botpress/client'\nimport type * as sdk from '@botpress/sdk'\nimport { TunnelRequest, TunnelResponse } from '@bpinternal/tunnel'\nimport axios, { AxiosRequestConfig, AxiosResponse } from 'axios'\nimport chalk from 'chalk'\nimport * as pathlib from 'path'\nimport * as uuid from 'uuid'\nimport * as apiUtils from '../api'\nimport type commandDefinitions from '../command-definitions'\nimport * as errors from '../errors'\nimport * as tables from '../tables'\nimport * as utils from '../utils'\nimport { Worker } from '../worker'\nimport { BuildCommand } from './build-command'\nimport { ProjectCommand, ProjectDefinition } from './project-command'\n\nconst DEFAULT_BOT_PORT = 8075\nconst DEFAULT_INTEGRATION_PORT = 8076\nconst TUNNEL_HELLO_INTERVAL = 5000\nconst FILEWATCHER_DEBOUNCE_MS = 2000\n\nexport type DevCommandDefinition = typeof commandDefinitions.dev\nexport class DevCommand extends ProjectCommand<DevCommandDefinition> {\n private _initialDef: ProjectDefinition | undefined = undefined\n\n public async run(): Promise<void> {\n this.logger.warn('This command is experimental and subject to breaking changes without notice.')\n\n const api = await this.ensureLoginAndCreateClient(this.argv)\n\n const projectDef = await this.readProjectDefinitionFromFS()\n if (projectDef.type === 'interface') {\n throw new errors.BotpressCLIError('This feature is not available for interfaces.')\n }\n this._initialDef = projectDef\n\n let env: Record<string, string> = {\n ...process.env,\n BP_API_URL: api.url,\n BP_TOKEN: api.token,\n }\n\n let defaultPort = DEFAULT_BOT_PORT\n if (this._initialDef.type === 'integration') {\n defaultPort = DEFAULT_INTEGRATION_PORT\n // TODO: store secrets in local cache to avoid prompting every time\n const secretEnvVariables = await this.promptSecrets(this._initialDef.definition, this.argv, { formatEnv: true })\n const nonNullSecretEnvVariables = utils.records.filterValues(secretEnvVariables, utils.guards.is.notNull)\n env = { ...env, ...nonNullSecretEnvVariables }\n }\n\n const port = this.argv.port ?? defaultPort\n\n const urlParseResult = utils.url.parse(this.argv.tunnelUrl)\n if (urlParseResult.status === 'error') {\n throw new errors.BotpressCLIError(`Invalid tunnel URL: ${urlParseResult.error}`)\n }\n\n const cachedTunnelId = await this.projectCache.get('tunnelId')\n\n let tunnelId: string\n if (this.argv.tunnelId) {\n tunnelId = this.argv.tunnelId\n } else if (cachedTunnelId) {\n tunnelId = cachedTunnelId\n } else {\n tunnelId = uuid.v4()\n }\n\n if (cachedTunnelId !== tunnelId) {\n await this.projectCache.set('tunnelId', tunnelId)\n }\n\n const { url: parsedTunnelUrl } = urlParseResult\n const isSecured = parsedTunnelUrl.protocol === 'https' || parsedTunnelUrl.protocol === 'wss'\n\n const wsTunnelUrl: string = utils.url.format({ ...parsedTunnelUrl, protocol: isSecured ? 'wss' : 'ws' })\n const httpTunnelUrl: string = utils.url.format({\n ...parsedTunnelUrl,\n protocol: isSecured ? 'https' : 'http',\n path: `/${tunnelId}`,\n })\n\n let worker: Worker | undefined = undefined\n\n const supervisor = new utils.tunnel.TunnelSupervisor(wsTunnelUrl, tunnelId, this.logger)\n supervisor.events.on('connected', ({ tunnel }) => {\n // prevents the tunnel from closing due to inactivity\n const timer = setInterval(() => {\n if (tunnel.closed) {\n return handleClose()\n }\n tunnel.hello()\n }, TUNNEL_HELLO_INTERVAL)\n const handleClose = (): void => clearInterval(timer)\n tunnel.events.on('close', handleClose)\n\n tunnel.events.on('request', (req) => {\n if (!worker) {\n this.logger.debug('Worker not ready yet, ignoring request')\n tunnel.send({ requestId: req.id, status: 503, body: 'Worker not ready yet' })\n return\n }\n\n void this._forwardTunnelRequest(`http://localhost:${port}`, req)\n .then((res) => {\n tunnel.send(res)\n })\n .catch((thrown) => {\n const err = errors.BotpressCLIError.wrap(thrown, 'An error occurred while handling request')\n this.logger.error(err.message)\n tunnel.send({\n requestId: req.id,\n status: 500,\n body: err.message,\n })\n })\n })\n })\n\n supervisor.events.on('manuallyClosed', () => {\n this.logger.debug('Tunnel manually closed')\n })\n\n await supervisor.start()\n\n await this._runBuild()\n await this._deploy(api, httpTunnelUrl)\n worker = await this._spawnWorker(env, port)\n\n try {\n const watcher = await utils.filewatcher.FileWatcher.watch(\n this.argv.workDir,\n async (events) => {\n if (!worker) {\n this.logger.debug('Worker not ready yet, ignoring file change event')\n return\n }\n\n const typescriptEvents = events.filter((e) => pathlib.extname(e.path) === '.ts')\n if (typescriptEvents.length === 0) {\n return\n }\n\n this.logger.log('Changes detected, rebuilding')\n await this._restart(api, worker, httpTunnelUrl)\n },\n {\n ignore: [this.projectPaths.abs.outDir],\n debounceMs: FILEWATCHER_DEBOUNCE_MS,\n }\n )\n\n await Promise.race([worker.wait(), watcher.wait(), supervisor.wait()])\n\n if (worker.running) {\n await worker.kill()\n }\n await watcher.close()\n supervisor.close()\n } catch (thrown) {\n throw errors.BotpressCLIError.wrap(thrown, 'An error occurred while running the dev server')\n } finally {\n if (worker.running) {\n await worker.kill()\n }\n }\n }\n\n private _restart = async (api: apiUtils.ApiClient, worker: Worker, tunnelUrl: string) => {\n try {\n await this._runBuild()\n } catch (thrown) {\n const error = errors.BotpressCLIError.wrap(thrown, 'Build failed')\n this.logger.error(error.message)\n return\n }\n\n await this._deploy(api, tunnelUrl)\n await worker.reload()\n }\n\n private _deploy = async (api: apiUtils.ApiClient, tunnelUrl: string) => {\n const projectDef = await this.readProjectDefinitionFromFS()\n\n if (projectDef.type === 'interface') {\n throw new errors.BotpressCLIError('This feature is not available for interfaces.')\n }\n if (projectDef.type === 'integration') {\n this._checkSecrets(projectDef.definition)\n return await this._deployDevIntegration(api, tunnelUrl, projectDef.definition)\n }\n if (projectDef.type === 'bot') {\n return await this._deployDevBot(api, tunnelUrl, projectDef.definition)\n }\n throw new errors.UnsupportedProjectType()\n }\n\n private _checkSecrets(integrationDef: sdk.IntegrationDefinition) {\n if (this._initialDef?.type !== 'integration') {\n return\n }\n const initialSecrets = this._initialDef?.definition.secrets ?? {}\n const currentSecrets = integrationDef.secrets ?? {}\n const newSecrets = Object.keys(currentSecrets).filter((s) => !initialSecrets[s])\n if (newSecrets.length > 0) {\n throw new errors.BotpressCLIError('Secrets were added while the server was running. A restart is required.')\n }\n }\n\n private _spawnWorker = async (env: Record<string, string>, port: number) => {\n const outfile = this.projectPaths.abs.outFileCJS\n const importPath = utils.path.toUnix(outfile)\n const code = `require('${importPath}').default.start(${port})`\n const worker = await Worker.spawn(\n {\n type: 'code',\n code,\n env,\n },\n this.logger\n ).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, 'Could not start dev worker')\n })\n\n return worker\n }\n\n private _runBuild() {\n return new BuildCommand(this.api, this.prompt, this.logger, this.argv).run()\n }\n\n private async _deployDevIntegration(\n api: apiUtils.ApiClient,\n externalUrl: string,\n integrationDef: sdk.IntegrationDefinition\n ): Promise<void> {\n const devId = await this.projectCache.get('devId')\n\n let integration: client.Integration | undefined = undefined\n\n if (devId) {\n const resp = await api.client.getIntegration({ id: devId }).catch(async (thrown) => {\n const err = errors.BotpressCLIError.wrap(thrown, `Could not find existing dev integration with id \"${devId}\"`)\n this.logger.warn(err.message)\n return { integration: undefined }\n })\n\n if (resp.integration?.dev) {\n integration = resp.integration\n } else {\n await this.projectCache.rm('devId')\n }\n }\n\n const line = this.logger.line()\n line.started(`Deploying dev integration ${chalk.bold(integrationDef.name)}...`)\n\n const createIntegrationBody = {\n ...(await this.prepareCreateIntegrationBody(integrationDef)),\n ...(await this.prepareIntegrationDependencies(integrationDef, api)),\n url: externalUrl,\n }\n\n if (integration) {\n const updateIntegrationBody = apiUtils.prepareUpdateIntegrationBody(\n { ...createIntegrationBody, id: integration.id },\n integration\n )\n\n const resp = await api.client.updateIntegration(updateIntegrationBody).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, `Could not update dev integration \"${integrationDef.name}\"`)\n })\n integration = resp.integration\n } else {\n const resp = await api.client.createIntegration({ ...createIntegrationBody, dev: true }).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, `Could not deploy dev integration \"${integrationDef.name}\"`)\n })\n integration = resp.integration\n }\n\n line.success(`Dev Integration deployed with id \"${integration.id}\" at \"${externalUrl}\"`)\n line.commit()\n\n await this.projectCache.set('devId', integration.id)\n }\n\n private async _deployDevBot(api: apiUtils.ApiClient, externalUrl: string, botDef: sdk.BotDefinition): Promise<void> {\n const devId = await this.projectCache.get('devId')\n\n let bot: client.Bot | undefined = undefined\n\n if (devId) {\n const resp = await api.client.getBot({ id: devId }).catch(async (thrown) => {\n const err = errors.BotpressCLIError.wrap(thrown, `Could not find existing dev bot with id \"${devId}\"`)\n this.logger.warn(err.message)\n return { bot: undefined }\n })\n\n if (resp.bot?.dev) {\n bot = resp.bot\n } else {\n await this.projectCache.rm('devId')\n }\n }\n\n if (!bot) {\n const createLine = this.logger.line()\n createLine.started('Creating dev bot...')\n const resp = await api.client\n .createBot({\n dev: true,\n url: externalUrl,\n })\n .catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, 'Could not deploy dev bot')\n })\n\n bot = resp.bot\n createLine.log('Dev Bot created')\n createLine.commit()\n await this.projectCache.set('devId', bot.id)\n }\n\n const updateLine = this.logger.line()\n updateLine.started('Deploying dev bot...')\n\n const updateBotBody = apiUtils.prepareUpdateBotBody(\n {\n ...(await apiUtils.prepareCreateBotBody(botDef)),\n ...(await this.prepareBotDependencies(botDef, api)),\n id: bot.id,\n url: externalUrl,\n },\n bot\n )\n\n const { bot: updatedBot } = await api.client.updateBot(updateBotBody).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, 'Could not deploy dev bot')\n })\n\n this.validateIntegrationRegistration(updatedBot, (failedIntegrations) => {\n throw new errors.BotpressCLIError(\n `Some integrations failed to register:\\n${Object.entries(failedIntegrations)\n .map(([key, int]) => `\u2022 ${key}: ${int.statusReason}`)\n .join('\\n')}`\n )\n })\n\n updateLine.success(`Dev Bot deployed with id \"${updatedBot.id}\" at \"${externalUrl}\"`)\n updateLine.commit()\n\n const tablesPublisher = new tables.TablesPublisher({ api, logger: this.logger, prompt: this.prompt })\n await tablesPublisher.deployTables({ botId: updatedBot.id, botDefinition: botDef })\n\n this.displayWebhookUrls(updatedBot)\n }\n\n private _forwardTunnelRequest = async (baseUrl: string, request: TunnelRequest): Promise<TunnelResponse> => {\n const axiosConfig = {\n method: request.method,\n url: this._formatLocalUrl(baseUrl, request),\n headers: request.headers,\n data: request.body,\n responseType: 'text',\n validateStatus: () => true,\n } satisfies AxiosRequestConfig\n\n this.logger.debug(`Forwarding request to ${axiosConfig.url}`)\n const response = await axios(axiosConfig)\n this.logger.debug('Sending back response up the tunnel')\n\n return {\n requestId: request.id,\n status: response.status,\n headers: this._getHeaders(response.headers),\n body: response.data,\n }\n }\n\n private _formatLocalUrl = (baseUrl: string, req: TunnelRequest): string => {\n if (req.query) {\n return `${baseUrl}${req.path}?${req.query}`\n }\n return `${baseUrl}${req.path}`\n }\n\n private _getHeaders = (res: AxiosResponse['headers']): TunnelResponse['headers'] => {\n const headers: TunnelResponse['headers'] = {}\n for (const key in res) {\n if (typeof res[key] === 'string' || typeof res[key] === 'number') {\n headers[key] = String(res[key])\n }\n }\n return headers\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,mBAAyD;AACzD,mBAAkB;AAClB,cAAyB;AACzB,WAAsB;AACtB,eAA0B;AAE1B,aAAwB;AACxB,aAAwB;AACxB,YAAuB;AACvB,oBAAuB;AACvB,2BAA6B;AAC7B,6BAAkD;AAElD,MAAM,mBAAmB;AACzB,MAAM,2BAA2B;AACjC,MAAM,wBAAwB;AAC9B,MAAM,0BAA0B;AAGzB,MAAM,mBAAmB,sCAAqC;AAAA,EAC3D,cAA6C;AAAA,
|
|
4
|
+
"sourcesContent": ["import type * as client from '@botpress/client'\nimport type * as sdk from '@botpress/sdk'\nimport { TunnelRequest, TunnelResponse } from '@bpinternal/tunnel'\nimport axios, { AxiosRequestConfig, AxiosResponse } from 'axios'\nimport chalk from 'chalk'\nimport { isEqual } from 'lodash'\nimport * as pathlib from 'path'\nimport * as uuid from 'uuid'\nimport * as apiUtils from '../api'\nimport type commandDefinitions from '../command-definitions'\nimport * as errors from '../errors'\nimport * as tables from '../tables'\nimport * as utils from '../utils'\nimport { Worker } from '../worker'\nimport { BuildCommand } from './build-command'\nimport { ProjectCommand, ProjectDefinition } from './project-command'\n\nconst DEFAULT_BOT_PORT = 8075\nconst DEFAULT_INTEGRATION_PORT = 8076\nconst TUNNEL_HELLO_INTERVAL = 5000\nconst FILEWATCHER_DEBOUNCE_MS = 2000\n\nexport type DevCommandDefinition = typeof commandDefinitions.dev\nexport class DevCommand extends ProjectCommand<DevCommandDefinition> {\n private _initialDef: ProjectDefinition | undefined = undefined\n private _cacheDevRequestBody: apiUtils.UpdateBotRequestBody | apiUtils.UpdateIntegrationRequestBody | undefined\n\n public async run(): Promise<void> {\n this.logger.warn('This command is experimental and subject to breaking changes without notice.')\n\n const api = await this.ensureLoginAndCreateClient(this.argv)\n\n const projectDef = await this.readProjectDefinitionFromFS()\n if (projectDef.type === 'interface') {\n throw new errors.BotpressCLIError('This feature is not available for interfaces.')\n }\n this._initialDef = projectDef\n\n let env: Record<string, string> = {\n ...process.env,\n BP_API_URL: api.url,\n BP_TOKEN: api.token,\n }\n\n let defaultPort = DEFAULT_BOT_PORT\n if (this._initialDef.type === 'integration') {\n defaultPort = DEFAULT_INTEGRATION_PORT\n // TODO: store secrets in local cache to avoid prompting every time\n const secretEnvVariables = await this.promptSecrets(this._initialDef.definition, this.argv, { formatEnv: true })\n const nonNullSecretEnvVariables = utils.records.filterValues(secretEnvVariables, utils.guards.is.notNull)\n env = { ...env, ...nonNullSecretEnvVariables }\n }\n\n const port = this.argv.port ?? defaultPort\n\n const urlParseResult = utils.url.parse(this.argv.tunnelUrl)\n if (urlParseResult.status === 'error') {\n throw new errors.BotpressCLIError(`Invalid tunnel URL: ${urlParseResult.error}`)\n }\n\n const cachedTunnelId = await this.projectCache.get('tunnelId')\n\n let tunnelId: string\n if (this.argv.tunnelId) {\n tunnelId = this.argv.tunnelId\n } else if (cachedTunnelId) {\n tunnelId = cachedTunnelId\n } else {\n tunnelId = uuid.v4()\n }\n\n if (cachedTunnelId !== tunnelId) {\n await this.projectCache.set('tunnelId', tunnelId)\n }\n\n const { url: parsedTunnelUrl } = urlParseResult\n const isSecured = parsedTunnelUrl.protocol === 'https' || parsedTunnelUrl.protocol === 'wss'\n\n const wsTunnelUrl: string = utils.url.format({ ...parsedTunnelUrl, protocol: isSecured ? 'wss' : 'ws' })\n const httpTunnelUrl: string = utils.url.format({\n ...parsedTunnelUrl,\n protocol: isSecured ? 'https' : 'http',\n path: `/${tunnelId}`,\n })\n\n let worker: Worker | undefined = undefined\n\n const supervisor = new utils.tunnel.TunnelSupervisor(wsTunnelUrl, tunnelId, this.logger)\n supervisor.events.on('connected', ({ tunnel }) => {\n // prevents the tunnel from closing due to inactivity\n const timer = setInterval(() => {\n if (tunnel.closed) {\n return handleClose()\n }\n tunnel.hello()\n }, TUNNEL_HELLO_INTERVAL)\n const handleClose = (): void => clearInterval(timer)\n tunnel.events.on('close', handleClose)\n\n tunnel.events.on('request', (req) => {\n if (!worker) {\n this.logger.debug('Worker not ready yet, ignoring request')\n tunnel.send({ requestId: req.id, status: 503, body: 'Worker not ready yet' })\n return\n }\n\n void this._forwardTunnelRequest(`http://localhost:${port}`, req)\n .then((res) => {\n tunnel.send(res)\n })\n .catch((thrown) => {\n const err = errors.BotpressCLIError.wrap(thrown, 'An error occurred while handling request')\n this.logger.error(err.message)\n tunnel.send({\n requestId: req.id,\n status: 500,\n body: err.message,\n })\n })\n })\n })\n\n supervisor.events.on('manuallyClosed', () => {\n this.logger.debug('Tunnel manually closed')\n })\n\n await supervisor.start()\n\n await this._runBuild()\n await this._deploy(api, httpTunnelUrl)\n worker = await this._spawnWorker(env, port)\n\n try {\n const watcher = await utils.filewatcher.FileWatcher.watch(\n this.argv.workDir,\n async (events) => {\n if (!worker) {\n this.logger.debug('Worker not ready yet, ignoring file change event')\n return\n }\n\n const typescriptEvents = events.filter((e) => pathlib.extname(e.path) === '.ts')\n if (typescriptEvents.length === 0) {\n return\n }\n\n this.logger.log('Changes detected, rebuilding')\n await this._restart(api, worker, httpTunnelUrl)\n },\n {\n ignore: [this.projectPaths.abs.outDir],\n debounceMs: FILEWATCHER_DEBOUNCE_MS,\n }\n )\n\n await Promise.race([worker.wait(), watcher.wait(), supervisor.wait()])\n\n if (worker.running) {\n await worker.kill()\n }\n await watcher.close()\n supervisor.close()\n } catch (thrown) {\n throw errors.BotpressCLIError.wrap(thrown, 'An error occurred while running the dev server')\n } finally {\n if (worker.running) {\n await worker.kill()\n }\n }\n }\n\n private _restart = async (api: apiUtils.ApiClient, worker: Worker, tunnelUrl: string) => {\n try {\n await this._runBuild()\n } catch (thrown) {\n const error = errors.BotpressCLIError.wrap(thrown, 'Build failed')\n this.logger.error(error.message)\n return\n }\n\n await this._deploy(api, tunnelUrl)\n await worker.reload()\n }\n\n private _deploy = async (api: apiUtils.ApiClient, tunnelUrl: string) => {\n const projectDef = await this.readProjectDefinitionFromFS()\n\n if (projectDef.type === 'interface') {\n throw new errors.BotpressCLIError('This feature is not available for interfaces.')\n }\n if (projectDef.type === 'integration') {\n this._checkSecrets(projectDef.definition)\n return await this._deployDevIntegration(api, tunnelUrl, projectDef.definition)\n }\n if (projectDef.type === 'bot') {\n return await this._deployDevBot(api, tunnelUrl, projectDef.definition)\n }\n throw new errors.UnsupportedProjectType()\n }\n\n private _checkSecrets(integrationDef: sdk.IntegrationDefinition) {\n if (this._initialDef?.type !== 'integration') {\n return\n }\n const initialSecrets = this._initialDef?.definition.secrets ?? {}\n const currentSecrets = integrationDef.secrets ?? {}\n const newSecrets = Object.keys(currentSecrets).filter((s) => !initialSecrets[s])\n if (newSecrets.length > 0) {\n throw new errors.BotpressCLIError('Secrets were added while the server was running. A restart is required.')\n }\n }\n\n private _spawnWorker = async (env: Record<string, string>, port: number) => {\n const outfile = this.projectPaths.abs.outFileCJS\n const importPath = utils.path.toUnix(outfile)\n const code = `require('${importPath}').default.start(${port})`\n const worker = await Worker.spawn(\n {\n type: 'code',\n code,\n env,\n },\n this.logger\n ).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, 'Could not start dev worker')\n })\n\n return worker\n }\n\n private _runBuild() {\n return new BuildCommand(this.api, this.prompt, this.logger, this.argv).run()\n }\n\n private async _deployDevIntegration(\n api: apiUtils.ApiClient,\n externalUrl: string,\n integrationDef: sdk.IntegrationDefinition\n ): Promise<void> {\n const devId = await this.projectCache.get('devId')\n\n let integration: client.Integration | undefined = undefined\n\n if (devId) {\n const resp = await api.client.getIntegration({ id: devId }).catch(async (thrown) => {\n const err = errors.BotpressCLIError.wrap(thrown, `Could not find existing dev integration with id \"${devId}\"`)\n this.logger.warn(err.message)\n return { integration: undefined }\n })\n\n if (resp.integration?.dev) {\n integration = resp.integration\n } else {\n await this.projectCache.rm('devId')\n }\n }\n\n const line = this.logger.line()\n line.started(`Deploying dev integration ${chalk.bold(integrationDef.name)}...`)\n\n const createIntegrationBody = {\n ...(await this.prepareCreateIntegrationBody(integrationDef)),\n ...(await this.prepareIntegrationDependencies(integrationDef, api)),\n url: externalUrl,\n }\n\n if (integration) {\n const updateIntegrationBody = apiUtils.prepareUpdateIntegrationBody(\n { ...createIntegrationBody, id: integration.id },\n integration\n )\n\n const resp = await api.client.updateIntegration(updateIntegrationBody).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, `Could not update dev integration \"${integrationDef.name}\"`)\n })\n integration = resp.integration\n } else {\n const resp = await api.client.createIntegration({ ...createIntegrationBody, dev: true }).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, `Could not deploy dev integration \"${integrationDef.name}\"`)\n })\n integration = resp.integration\n }\n\n line.success(`Dev Integration deployed with id \"${integration.id}\" at \"${externalUrl}\"`)\n line.commit()\n\n await this.projectCache.set('devId', integration.id)\n }\n\n private async _deployDevBot(api: apiUtils.ApiClient, externalUrl: string, botDef: sdk.BotDefinition): Promise<void> {\n const devId = await this.projectCache.get('devId')\n\n let bot: client.Bot | undefined = undefined\n\n if (devId) {\n const resp = await api.client.getBot({ id: devId }).catch(async (thrown) => {\n const err = errors.BotpressCLIError.wrap(thrown, `Could not find existing dev bot with id \"${devId}\"`)\n this.logger.warn(err.message)\n return { bot: undefined }\n })\n\n if (resp.bot?.dev) {\n bot = resp.bot\n } else {\n await this.projectCache.rm('devId')\n }\n }\n\n if (!bot) {\n const createLine = this.logger.line()\n createLine.started('Creating dev bot...')\n const resp = await api.client\n .createBot({\n dev: true,\n url: externalUrl,\n })\n .catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, 'Could not deploy dev bot')\n })\n\n bot = resp.bot\n createLine.log('Dev Bot created')\n createLine.commit()\n await this.projectCache.set('devId', bot.id)\n }\n\n const updateBotBody = apiUtils.prepareUpdateBotBody(\n {\n ...(await apiUtils.prepareCreateBotBody(botDef)),\n ...(await this.prepareBotDependencies(botDef, api)),\n id: bot.id,\n url: externalUrl,\n },\n bot\n )\n\n if (!(await this._didDefinitionChange(updateBotBody))) {\n this.logger.log('Skipping deployment step. No changes found in bot.definition.ts')\n return\n }\n const updateLine = this.logger.line()\n updateLine.started('Deploying dev bot...')\n\n const { bot: updatedBot } = await api.client.updateBot(updateBotBody).catch((thrown) => {\n throw errors.BotpressCLIError.wrap(thrown, 'Could not deploy dev bot')\n })\n\n this.validateIntegrationRegistration(updatedBot, (failedIntegrations) => {\n throw new errors.BotpressCLIError(\n `Some integrations failed to register:\\n${Object.entries(failedIntegrations)\n .map(([key, int]) => `\u2022 ${key}: ${int.statusReason}`)\n .join('\\n')}`\n )\n })\n\n updateLine.success(`Dev Bot deployed with id \"${updatedBot.id}\" at \"${externalUrl}\"`)\n updateLine.commit()\n\n const tablesPublisher = new tables.TablesPublisher({ api, logger: this.logger, prompt: this.prompt })\n await tablesPublisher.deployTables({ botId: updatedBot.id, botDefinition: botDef })\n\n this.displayWebhookUrls(updatedBot)\n }\n\n private async _didDefinitionChange(body: apiUtils.UpdateBotRequestBody | apiUtils.UpdateIntegrationRequestBody) {\n const didChange = !isEqual(body, this._cacheDevRequestBody)\n this._cacheDevRequestBody = { ...body }\n return didChange\n }\n\n private _forwardTunnelRequest = async (baseUrl: string, request: TunnelRequest): Promise<TunnelResponse> => {\n const axiosConfig = {\n method: request.method,\n url: this._formatLocalUrl(baseUrl, request),\n headers: request.headers,\n data: request.body,\n responseType: 'text',\n validateStatus: () => true,\n } satisfies AxiosRequestConfig\n\n this.logger.debug(`Forwarding request to ${axiosConfig.url}`)\n const response = await axios(axiosConfig)\n this.logger.debug('Sending back response up the tunnel')\n\n return {\n requestId: request.id,\n status: response.status,\n headers: this._getHeaders(response.headers),\n body: response.data,\n }\n }\n\n private _formatLocalUrl = (baseUrl: string, req: TunnelRequest): string => {\n if (req.query) {\n return `${baseUrl}${req.path}?${req.query}`\n }\n return `${baseUrl}${req.path}`\n }\n\n private _getHeaders = (res: AxiosResponse['headers']): TunnelResponse['headers'] => {\n const headers: TunnelResponse['headers'] = {}\n for (const key in res) {\n if (typeof res[key] === 'string' || typeof res[key] === 'number') {\n headers[key] = String(res[key])\n }\n }\n return headers\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["chalk", "axios"]
|
|
7
7
|
}
|
|
@@ -117,6 +117,9 @@ class TunnelSupervisor {
|
|
|
117
117
|
});
|
|
118
118
|
tunnel.events.on("close", ({ code, reason, target, type, wasClean }) => {
|
|
119
119
|
this._logger.error(`Tunnel closed: ${code} ${reason}`);
|
|
120
|
+
if (code === import_tunnel.errors.CLOSE_CODES.TUNNEL_ID_CONFLICT) {
|
|
121
|
+
throw new Error("Cannot start: Tunnel Id is already used, choose a different tunnel id.");
|
|
122
|
+
}
|
|
120
123
|
this._reconnectSync({ type: "close", ev: { code, reason, target, type, wasClean } });
|
|
121
124
|
});
|
|
122
125
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/tunnel-utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { TunnelTail, ClientCloseEvent, ClientErrorEvent } from '@bpinternal/tunnel'\nimport { Logger } from '../logger'\nimport { EventEmitter } from './event-emitter'\n\nexport type ReconnectionTriggerEvent =\n | {\n type: 'init'\n ev: null\n }\n | {\n type: 'error'\n ev: ClientErrorEvent\n }\n | {\n type: 'close'\n ev: ClientCloseEvent\n }\n\nexport type ReconnectedEvent = {\n tunnel: TunnelTail\n ev: ReconnectionTriggerEvent\n}\n\nexport class ReconnectionFailedError extends Error {\n public constructor(public readonly event: ReconnectionTriggerEvent) {\n const reason = ReconnectionFailedError._reason(event)\n super(`Reconnection failed: ${reason}`)\n }\n\n private static _reason(event: ReconnectionTriggerEvent): string {\n if (event.type === 'error') {\n return 'error'\n }\n\n if (event.type === 'close') {\n return `${event.ev.code} ${event.ev.reason}`\n }\n\n return 'init'\n }\n}\n\nexport class TunnelSupervisor {\n private _tunnel?: TunnelTail\n private _closed = false\n private _started = false\n\n public readonly events = new EventEmitter<{\n connectionFailed: ReconnectionTriggerEvent\n manuallyClosed: null\n connected: {\n tunnel: TunnelTail\n ev: ReconnectionTriggerEvent\n }\n }>()\n\n public constructor(\n private _tunnelUrl: string,\n private _tunnelId: string,\n private _logger: Logger\n ) {}\n\n public async start(): Promise<void> {\n if (this._closed) {\n throw new Error('Cannot start: Tunnel is closed')\n }\n if (this._started) {\n throw new Error('Cannot start: Tunnel is already started')\n }\n\n this._started = true\n const tunnel = await this._reconnect({ type: 'init', ev: null })\n this._tunnel = tunnel\n }\n\n public get closed(): boolean {\n return this._closed\n }\n\n /**\n * @returns Promise that rejects when a reconnection attempt fails and resolves when the tunnel is closed manually\n */\n public async wait(): Promise<void> {\n if (this._closed) {\n throw new Error('Cannot wait: Tunnel is closed')\n }\n\n return new Promise((resolve, reject) => {\n this.events.on('connectionFailed', (ev) => {\n reject(new ReconnectionFailedError(ev))\n })\n\n this.events.on('manuallyClosed', () => {\n resolve()\n })\n })\n }\n\n public close(): void {\n if (this._closed) {\n return\n }\n\n this._closed = true\n this._tunnel?.close()\n this.events.emit('manuallyClosed', null)\n }\n\n private _reconnectSync(ev: ReconnectionTriggerEvent): void {\n void this._reconnect(ev)\n .then((t) => {\n this._tunnel = t\n })\n .catch(() => this.events.emit('connectionFailed', ev))\n }\n\n private async _reconnect(ev: ReconnectionTriggerEvent): Promise<TunnelTail> {\n const newTunnel = async () => {\n const tunnel = await TunnelTail.new(this._tunnelUrl, this._tunnelId)\n this._registerListeners(tunnel)\n this.events.emit('connected', { tunnel, ev })\n return tunnel\n }\n\n if (ev.type === 'init') {\n // skip logging on the first connection attempt\n return newTunnel()\n }\n\n const line = this._logger.line()\n line.started('Reconnecting tunnel...')\n const tunnel = await newTunnel()\n line.success('Reconnected')\n line.commit()\n return tunnel\n }\n\n private _registerListeners(tunnel: TunnelTail) {\n tunnel.events.on('error', ({ target, type }) => {\n this._logger.error(`Tunnel error: ${type}`)\n this._reconnectSync({ type: 'error', ev: { target, type } })\n })\n tunnel.events.on('close', ({ code, reason, target, type, wasClean }) => {\n this._logger.error(`Tunnel closed: ${code} ${reason}`)\n this._reconnectSync({ type: 'close', ev: { code, reason, target, type, wasClean } })\n })\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { TunnelTail, ClientCloseEvent, ClientErrorEvent, errors } from '@bpinternal/tunnel'\nimport { Logger } from '../logger'\nimport { EventEmitter } from './event-emitter'\n\nexport type ReconnectionTriggerEvent =\n | {\n type: 'init'\n ev: null\n }\n | {\n type: 'error'\n ev: ClientErrorEvent\n }\n | {\n type: 'close'\n ev: ClientCloseEvent\n }\n\nexport type ReconnectedEvent = {\n tunnel: TunnelTail\n ev: ReconnectionTriggerEvent\n}\n\nexport class ReconnectionFailedError extends Error {\n public constructor(public readonly event: ReconnectionTriggerEvent) {\n const reason = ReconnectionFailedError._reason(event)\n super(`Reconnection failed: ${reason}`)\n }\n\n private static _reason(event: ReconnectionTriggerEvent): string {\n if (event.type === 'error') {\n return 'error'\n }\n\n if (event.type === 'close') {\n return `${event.ev.code} ${event.ev.reason}`\n }\n\n return 'init'\n }\n}\n\nexport class TunnelSupervisor {\n private _tunnel?: TunnelTail\n private _closed = false\n private _started = false\n\n public readonly events = new EventEmitter<{\n connectionFailed: ReconnectionTriggerEvent\n manuallyClosed: null\n connected: {\n tunnel: TunnelTail\n ev: ReconnectionTriggerEvent\n }\n }>()\n\n public constructor(\n private _tunnelUrl: string,\n private _tunnelId: string,\n private _logger: Logger\n ) {}\n\n public async start(): Promise<void> {\n if (this._closed) {\n throw new Error('Cannot start: Tunnel is closed')\n }\n if (this._started) {\n throw new Error('Cannot start: Tunnel is already started')\n }\n\n this._started = true\n const tunnel = await this._reconnect({ type: 'init', ev: null })\n this._tunnel = tunnel\n }\n\n public get closed(): boolean {\n return this._closed\n }\n\n /**\n * @returns Promise that rejects when a reconnection attempt fails and resolves when the tunnel is closed manually\n */\n public async wait(): Promise<void> {\n if (this._closed) {\n throw new Error('Cannot wait: Tunnel is closed')\n }\n\n return new Promise((resolve, reject) => {\n this.events.on('connectionFailed', (ev) => {\n reject(new ReconnectionFailedError(ev))\n })\n\n this.events.on('manuallyClosed', () => {\n resolve()\n })\n })\n }\n\n public close(): void {\n if (this._closed) {\n return\n }\n\n this._closed = true\n this._tunnel?.close()\n this.events.emit('manuallyClosed', null)\n }\n\n private _reconnectSync(ev: ReconnectionTriggerEvent): void {\n void this._reconnect(ev)\n .then((t) => {\n this._tunnel = t\n })\n .catch(() => this.events.emit('connectionFailed', ev))\n }\n\n private async _reconnect(ev: ReconnectionTriggerEvent): Promise<TunnelTail> {\n const newTunnel = async () => {\n const tunnel = await TunnelTail.new(this._tunnelUrl, this._tunnelId)\n this._registerListeners(tunnel)\n this.events.emit('connected', { tunnel, ev })\n return tunnel\n }\n\n if (ev.type === 'init') {\n // skip logging on the first connection attempt\n return newTunnel()\n }\n\n const line = this._logger.line()\n line.started('Reconnecting tunnel...')\n const tunnel = await newTunnel()\n line.success('Reconnected')\n line.commit()\n return tunnel\n }\n\n private _registerListeners(tunnel: TunnelTail) {\n tunnel.events.on('error', ({ target, type }) => {\n this._logger.error(`Tunnel error: ${type}`)\n this._reconnectSync({ type: 'error', ev: { target, type } })\n })\n tunnel.events.on('close', ({ code, reason, target, type, wasClean }) => {\n this._logger.error(`Tunnel closed: ${code} ${reason}`)\n\n if (code === errors.CLOSE_CODES.TUNNEL_ID_CONFLICT) {\n throw new Error('Cannot start: Tunnel Id is already used, choose a different tunnel id.')\n }\n\n this._reconnectSync({ type: 'close', ev: { code, reason, target, type, wasClean } })\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["tunnel"]
|
|
7
7
|
}
|