@botonic/nx-plugin 2.30.0 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/executors.json +0 -5
- package/package.json +1 -1
- package/src/executors/delete-bot/executor.js +0 -2
- package/src/executors/deploy-to-hubtype/executor.js +24 -160
- package/src/executors/e2e-webchat/botonic-package-publish.spec.ts +7 -11
- package/src/executors/integrate-provider/executor.js +0 -2
- package/src/executors/login-to-hubtype/executor.js +0 -2
- package/src/executors/logout-from-hubtype/executor.js +2 -2
- package/src/executors/serve-bot/executor.js +142 -24
- package/src/executors/serve-bot/schema.json +13 -5
- package/src/generators/bot-app/files/vite/node.config.ts.template +2 -7
- package/src/lib/api-service.d.ts +19 -20
- package/src/lib/api-service.js +150 -82
- package/src/lib/constants.d.ts +2 -3
- package/src/lib/constants.js +6 -9
- package/src/lib/credentials-handler.d.ts +9 -18
- package/src/lib/credentials-handler.js +42 -24
- package/src/lib/interfaces.d.ts +10 -13
- package/src/lib/util/executor-helpers.d.ts +52 -23
- package/src/lib/util/executor-helpers.js +494 -106
- package/src/plugin.js +5 -14
- package/src/executors/deploy-local-runtime/executor.d.ts +0 -5
- package/src/executors/deploy-local-runtime/executor.js +0 -148
- package/src/executors/deploy-local-runtime/schema.d.js +0 -16
- package/src/executors/deploy-local-runtime/schema.json +0 -34
- package/src/generators/bot-app/files/vite/botonic-ssr-deps.ts.template +0 -56
- package/src/generators/preset/files/package.json +0 -26
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 2.31.0 (2026-06-15)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- refactored botonic/HT credentials stored and serve/deploy bots flow refinement ([#934](https://github.com/metis-ai/hubtype-product/pull/934))
|
|
6
|
+
- **lambda:** self-contained bundle with noExternal: true #BLT-2432 ([#936](https://github.com/metis-ai/hubtype-product/pull/936))
|
|
7
|
+
- **serve:** replace deploy-local-runtime with integrated dev session registration ([#921](https://github.com/metis-ai/hubtype-product/pull/921))
|
|
8
|
+
|
|
9
|
+
### ❤️ Thank You
|
|
10
|
+
|
|
11
|
+
- David Hidalgo @Davidhidalgo
|
|
12
|
+
- Marc Rabat @vanbasten17
|
|
13
|
+
|
|
1
14
|
## 2.30.0 (2026-06-11)
|
|
2
15
|
|
|
3
16
|
### 🚀 Features
|
package/executors.json
CHANGED
|
@@ -26,11 +26,6 @@
|
|
|
26
26
|
"schema": "./src/executors/deploy-to-hubtype/schema.json",
|
|
27
27
|
"description": "Deploy Botonic app to Hubtype"
|
|
28
28
|
},
|
|
29
|
-
"deploy-local-runtime": {
|
|
30
|
-
"implementation": "./src/executors/deploy-local-runtime/executor",
|
|
31
|
-
"schema": "./src/executors/deploy-local-runtime/schema.json",
|
|
32
|
-
"description": "Deploy Botonic app to Hubtype Local Runtime"
|
|
33
|
-
},
|
|
34
29
|
"integrate-provider": {
|
|
35
30
|
"implementation": "./src/executors/integrate-provider/executor",
|
|
36
31
|
"schema": "./src/executors/integrate-provider/schema.json",
|
package/package.json
CHANGED
|
@@ -22,7 +22,6 @@ __export(executor_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(executor_exports);
|
|
24
24
|
var import_enquirer = require("enquirer");
|
|
25
|
-
var import_path = require("path");
|
|
26
25
|
var import_api_service = require("../../lib/api-service");
|
|
27
26
|
var import_executor_helpers = require("../../lib/util/executor-helpers");
|
|
28
27
|
async function deleteBotExecutor(options, context) {
|
|
@@ -32,7 +31,6 @@ async function deleteBotExecutor(options, context) {
|
|
|
32
31
|
try {
|
|
33
32
|
const { targetEnvironment, environmentVariables } = (0, import_executor_helpers.resolveHubtypeEnvironment)(context, options);
|
|
34
33
|
const botonicApiService = new import_api_service.BotonicAPIService({
|
|
35
|
-
workspaceRoot: (0, import_path.resolve)(context.root),
|
|
36
34
|
environmentVariables,
|
|
37
35
|
targetEnvironment
|
|
38
36
|
});
|
|
@@ -22,16 +22,12 @@ __export(executor_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(executor_exports);
|
|
24
24
|
var import_enquirer = require("enquirer");
|
|
25
|
-
var import_fs = require("fs");
|
|
26
|
-
var import_path = require("path");
|
|
27
|
-
var import_zip_a_folder = require("zip-a-folder");
|
|
28
25
|
var import_api_service = require("../../lib/api-service");
|
|
29
|
-
var import_bot_config = require("../../lib/bot-config");
|
|
30
26
|
var import_executor_helpers = require("../../lib/util/executor-helpers");
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
const CREATE_PROD_CHOICE = "Create Production Bot";
|
|
28
|
+
function botLabel(bot) {
|
|
29
|
+
return bot.is_test ? `${bot.name} (Staging bot)` : `${bot.name} (Production bot)`;
|
|
30
|
+
}
|
|
35
31
|
let PROJECT_ROOT;
|
|
36
32
|
async function deployToHubtypeExecutor(options, context) {
|
|
37
33
|
if (!context.projectName) {
|
|
@@ -42,7 +38,6 @@ async function deployToHubtypeExecutor(options, context) {
|
|
|
42
38
|
const { targetEnvironment, environmentVariables } = (0, import_executor_helpers.resolveHubtypeEnvironment)(context, options);
|
|
43
39
|
const botonicApiService = new import_api_service.BotonicAPIService({
|
|
44
40
|
projectRoot: PROJECT_ROOT,
|
|
45
|
-
workspaceRoot: (0, import_path.resolve)(context.root),
|
|
46
41
|
environmentVariables,
|
|
47
42
|
targetEnvironment
|
|
48
43
|
});
|
|
@@ -69,7 +64,11 @@ async function deployToHubtypeExecutor(options, context) {
|
|
|
69
64
|
} else {
|
|
70
65
|
await handleBotFlow(botonicApiService);
|
|
71
66
|
}
|
|
72
|
-
await
|
|
67
|
+
await (0, import_executor_helpers.deployBotToHubtype)(
|
|
68
|
+
botonicApiService,
|
|
69
|
+
PROJECT_ROOT,
|
|
70
|
+
context.projectName
|
|
71
|
+
);
|
|
73
72
|
return { success: true };
|
|
74
73
|
} catch (error) {
|
|
75
74
|
return (0, import_executor_helpers.handleExecutorError)(error, "Deployment");
|
|
@@ -148,161 +147,26 @@ async function createNewBot(botonicApiService, botName) {
|
|
|
148
147
|
}
|
|
149
148
|
async function selectExistentBot(botonicApiService, bots) {
|
|
150
149
|
console.log("\u{1F4CB} Select a bot to deploy:\n");
|
|
151
|
-
const providersPromises = bots.map((b) => botonicApiService.getProviders(b.id));
|
|
152
|
-
const providers = await Promise.all(providersPromises);
|
|
153
|
-
const botsWithProviders = bots.map((bot2, index) => ({
|
|
154
|
-
...bot2,
|
|
155
|
-
providers: providers[index].data.results
|
|
156
|
-
}));
|
|
157
150
|
const response = await (0, import_enquirer.prompt)({
|
|
158
151
|
type: "select",
|
|
159
|
-
name: "
|
|
152
|
+
name: "bot_id",
|
|
160
153
|
message: "\u{1F916} Please select a bot:",
|
|
161
|
-
choices:
|
|
162
|
-
|
|
163
|
-
name: bot2.
|
|
164
|
-
message: bot2
|
|
165
|
-
}
|
|
166
|
-
|
|
154
|
+
choices: [
|
|
155
|
+
...bots.map((bot2) => ({
|
|
156
|
+
name: bot2.id,
|
|
157
|
+
message: botLabel(bot2)
|
|
158
|
+
})),
|
|
159
|
+
{ name: CREATE_PROD_CHOICE, message: "+ Create Production Bot" }
|
|
160
|
+
]
|
|
167
161
|
});
|
|
168
|
-
const
|
|
169
|
-
|
|
162
|
+
const selectedBotId = response.bot_id;
|
|
163
|
+
if (selectedBotId === CREATE_PROD_CHOICE) {
|
|
164
|
+
await createNewBot(botonicApiService);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const bot = bots.find((b) => b.id === selectedBotId);
|
|
170
168
|
if (bot) {
|
|
171
169
|
botonicApiService.setCurrentBot(bot);
|
|
172
|
-
console.log(`\u2705 Selected bot: ${bot
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
async function deploy(botonicApiService, projectRoot, projectName) {
|
|
176
|
-
console.log("\u{1F528} Preparing your bot for deployment...\n");
|
|
177
|
-
const buildOut = await botonicApiService.build({
|
|
178
|
-
projectRoot,
|
|
179
|
-
projectName
|
|
180
|
-
});
|
|
181
|
-
if (!buildOut) {
|
|
182
|
-
throw new Error("Build failed");
|
|
183
|
-
}
|
|
184
|
-
const botConfigJson = await import_bot_config.BotConfig.get(projectRoot);
|
|
185
|
-
await createBundle(projectRoot);
|
|
186
|
-
const { hasDeployErrors } = await deployBundle(
|
|
187
|
-
botonicApiService,
|
|
188
|
-
botConfigJson
|
|
189
|
-
);
|
|
190
|
-
await displayDeployResults(botonicApiService, { hasDeployErrors });
|
|
191
|
-
(0, import_fs.rmSync)((0, import_path.join)(projectRoot, BOTONIC_BUNDLE_FILE));
|
|
192
|
-
(0, import_file_system.removeRecursively)((0, import_path.join)(projectRoot, BOTONIC_TEMP_DIRNAME));
|
|
193
|
-
botonicApiService.saveAllCredentials();
|
|
194
|
-
}
|
|
195
|
-
async function createBundle(projectRoot) {
|
|
196
|
-
console.log("\u{1F4E6} Creating deployment bundle...");
|
|
197
|
-
if ((0, import_file_system.pathExists)((0, import_path.join)(projectRoot, BOTONIC_TEMP_DIRNAME))) {
|
|
198
|
-
(0, import_file_system.removeRecursively)((0, import_path.join)(projectRoot, BOTONIC_TEMP_DIRNAME));
|
|
199
|
-
}
|
|
200
|
-
const webchatHtmlPath = (0, import_path.join)(projectRoot, "dist", "webchat", "webchat.html");
|
|
201
|
-
const webchatIndexPath = (0, import_path.join)(projectRoot, "dist", "webchat", "index.html");
|
|
202
|
-
if ((0, import_file_system.pathExists)(webchatHtmlPath)) {
|
|
203
|
-
(0, import_fs.renameSync)(webchatHtmlPath, webchatIndexPath);
|
|
204
|
-
}
|
|
205
|
-
const webviewsHtmlPath = (0, import_path.join)(
|
|
206
|
-
projectRoot,
|
|
207
|
-
"dist",
|
|
208
|
-
"webviews",
|
|
209
|
-
"webviews.html"
|
|
210
|
-
);
|
|
211
|
-
const webviewsIndexPath = (0, import_path.join)(projectRoot, "dist", "webviews", "index.html");
|
|
212
|
-
if ((0, import_file_system.pathExists)(webviewsHtmlPath)) {
|
|
213
|
-
(0, import_fs.renameSync)(webviewsHtmlPath, webviewsIndexPath);
|
|
214
|
-
}
|
|
215
|
-
(0, import_file_system.createDir)((0, import_path.join)(projectRoot, BOTONIC_TEMP_DIRNAME));
|
|
216
|
-
(0, import_file_system.copy)(
|
|
217
|
-
(0, import_path.join)(projectRoot, "dist"),
|
|
218
|
-
(0, import_path.join)(projectRoot, BOTONIC_TEMP_DIRNAME, "dist")
|
|
219
|
-
);
|
|
220
|
-
const zipRes = await import_zip_a_folder.ZipAFolder.zip(
|
|
221
|
-
(0, import_path.join)(projectRoot, BOTONIC_TEMP_DIRNAME),
|
|
222
|
-
(0, import_path.join)(projectRoot, BOTONIC_BUNDLE_FILE)
|
|
223
|
-
);
|
|
224
|
-
if (zipRes instanceof Error) {
|
|
225
|
-
throw zipRes;
|
|
226
|
-
}
|
|
227
|
-
const zipStats = (0, import_fs.statSync)((0, import_path.join)(projectRoot, BOTONIC_BUNDLE_FILE));
|
|
228
|
-
console.log("\u2705 Bundle created successfully!");
|
|
229
|
-
if (zipStats.size >= 20 * 10 ** 6) {
|
|
230
|
-
throw new Error(
|
|
231
|
-
`Bundle too large: ${(zipStats.size / 1024 / 1024).toFixed(2)}MB (max: 20MB)`
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async function deployBundle(botonicApiService, botConfigJson) {
|
|
236
|
-
console.log("\u{1F680} Deploying to Hubtype Cloud...");
|
|
237
|
-
try {
|
|
238
|
-
const deploy2 = await botonicApiService.deployBot(
|
|
239
|
-
(0, import_path.join)(PROJECT_ROOT, BOTONIC_BUNDLE_FILE),
|
|
240
|
-
botConfigJson
|
|
241
|
-
);
|
|
242
|
-
if (deploy2.response?.status === 403 || !deploy2.data.deploy_id) {
|
|
243
|
-
throw new Error(
|
|
244
|
-
`Deploy Botonic Error: ${String(deploy2.response?.data?.status)}`
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
console.log("\u23F3 Waiting for deployment to complete...");
|
|
248
|
-
while (true) {
|
|
249
|
-
await (0, import_system.sleep)(500);
|
|
250
|
-
const deployStatus = await botonicApiService.deployStatus(
|
|
251
|
-
deploy2.data.deploy_id
|
|
252
|
-
);
|
|
253
|
-
if (deployStatus.data.is_completed) {
|
|
254
|
-
if (deployStatus.data.status === "deploy_status_completed_ok") {
|
|
255
|
-
console.log("\u2705 Deployment completed successfully!");
|
|
256
|
-
return { hasDeployErrors: false };
|
|
257
|
-
} else {
|
|
258
|
-
throw new Error(deployStatus.data.error);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
} catch (error) {
|
|
263
|
-
console.error("\u274C Deployment failed");
|
|
264
|
-
let reason = String(error);
|
|
265
|
-
if (error.response?.data) {
|
|
266
|
-
reason = error.response.data.join("");
|
|
267
|
-
}
|
|
268
|
-
console.error(`${reason}`);
|
|
269
|
-
return { hasDeployErrors: true };
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
async function displayDeployResults(botonicApiService, { hasDeployErrors }) {
|
|
273
|
-
try {
|
|
274
|
-
const providersRes = await botonicApiService.getProviders();
|
|
275
|
-
const providers = providersRes.data.results;
|
|
276
|
-
if (hasDeployErrors) return false;
|
|
277
|
-
if (!providers.length) {
|
|
278
|
-
const botId = botonicApiService.botInfo().id;
|
|
279
|
-
const accessToken = botonicApiService.getOauth().access_token;
|
|
280
|
-
const links = `Now, you can integrate a channel in:
|
|
281
|
-
https://app.hubtype.com/bots/${botId}/integrations?access_token=${accessToken}`;
|
|
282
|
-
console.log(links);
|
|
283
|
-
} else {
|
|
284
|
-
displayProviders(providers);
|
|
285
|
-
}
|
|
286
|
-
return true;
|
|
287
|
-
} catch (e) {
|
|
288
|
-
console.error(` There was an error getting the providers: ${String(e)}`);
|
|
289
|
-
return false;
|
|
170
|
+
console.log(`\u2705 Selected bot: ${botLabel(bot)}`);
|
|
290
171
|
}
|
|
291
172
|
}
|
|
292
|
-
function displayProviders(providers) {
|
|
293
|
-
console.log("\u{1F389} DEPLOYMENT SUCCESSFUL!");
|
|
294
|
-
console.log("\u{1F680} Your bot is now live and ready to chat!");
|
|
295
|
-
console.log("\u{1F4F1} Your bot is published on:");
|
|
296
|
-
providers.forEach((p) => {
|
|
297
|
-
if (p.provider === "whatsapp")
|
|
298
|
-
console.log(` \u{1F4AC} [WhatsApp] https://wa.me/${p.username}`);
|
|
299
|
-
if (p.provider === "facebook")
|
|
300
|
-
console.log(` \u{1F4AC} [Facebook] https://m.me/${p.username}`);
|
|
301
|
-
if (p.provider === "telegram")
|
|
302
|
-
console.log(` \u{1F4AC} [Telegram] https://t.me/${p.username}`);
|
|
303
|
-
if (p.provider === "twitter")
|
|
304
|
-
console.log(` \u{1F4AC} [Twitter] https://t.me/${p.username}`);
|
|
305
|
-
if (p.provider === "generic")
|
|
306
|
-
console.log(` \u{1F4AC} [Generic] Your app or website`);
|
|
307
|
-
});
|
|
308
|
-
}
|
|
@@ -14,13 +14,13 @@ test.describe('Botonic Publish E2E', () => {
|
|
|
14
14
|
|
|
15
15
|
await page.goto('/')
|
|
16
16
|
await page.waitForLoadState('networkidle')
|
|
17
|
-
await page.waitForSelector('
|
|
17
|
+
await page.waitForSelector('[data-testid="webchat-trigger"]', {
|
|
18
18
|
timeout: 3000,
|
|
19
19
|
})
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
async function ensureWebchatOpen(page: any) {
|
|
23
|
-
const trigger = page.
|
|
23
|
+
const trigger = page.getByTestId('webchat-trigger')
|
|
24
24
|
await expect(trigger).toBeVisible({ timeout: 10000 })
|
|
25
25
|
|
|
26
26
|
const ariaExpanded = await trigger.getAttribute('aria-expanded')
|
|
@@ -29,16 +29,14 @@ test.describe('Botonic Publish E2E', () => {
|
|
|
29
29
|
await page.waitForTimeout(300)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const container = page
|
|
33
|
-
.locator('[class*="webchat-container-module"]')
|
|
34
|
-
.first()
|
|
32
|
+
const container = page.getByTestId('webchat-container')
|
|
35
33
|
await expect(container).toBeVisible({ timeout: 5000 })
|
|
36
34
|
|
|
37
35
|
return { trigger, container }
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
test('webchat trigger renders and opens webchat', async ({ page }) => {
|
|
41
|
-
const trigger = page.
|
|
39
|
+
const trigger = page.getByTestId('webchat-trigger')
|
|
42
40
|
await expect(trigger).toBeVisible({ timeout: 10000 })
|
|
43
41
|
|
|
44
42
|
const ariaExpanded = await trigger.getAttribute('aria-expanded')
|
|
@@ -47,9 +45,7 @@ test.describe('Botonic Publish E2E', () => {
|
|
|
47
45
|
await page.waitForTimeout(300)
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
const container = page
|
|
51
|
-
.locator('[class*="webchat-container-module"]')
|
|
52
|
-
.first()
|
|
48
|
+
const container = page.getByTestId('webchat-container')
|
|
53
49
|
await expect(container).toBeVisible({ timeout: 5000 })
|
|
54
50
|
})
|
|
55
51
|
|
|
@@ -62,7 +58,7 @@ test.describe('Botonic Publish E2E', () => {
|
|
|
62
58
|
await input.fill('hello')
|
|
63
59
|
await input.press('Enter')
|
|
64
60
|
|
|
65
|
-
const messages = page.locator('[
|
|
61
|
+
const messages = page.locator('[data-testid^="webchat-message-"]')
|
|
66
62
|
await expect(messages.first()).toBeVisible({ timeout: 15000 })
|
|
67
63
|
})
|
|
68
64
|
|
|
@@ -78,7 +74,7 @@ test.describe('Botonic Publish E2E', () => {
|
|
|
78
74
|
await expect(sendButton).toBeVisible()
|
|
79
75
|
await sendButton.click()
|
|
80
76
|
|
|
81
|
-
const messages = page.locator('[
|
|
77
|
+
const messages = page.locator('[data-testid^="webchat-message-"]')
|
|
82
78
|
await expect(messages.first()).toBeVisible({ timeout: 15000 })
|
|
83
79
|
})
|
|
84
80
|
})
|
|
@@ -22,7 +22,6 @@ __export(executor_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(executor_exports);
|
|
24
24
|
var import_enquirer = require("enquirer");
|
|
25
|
-
var import_path = require("path");
|
|
26
25
|
var import_api_service = require("../../lib/api-service");
|
|
27
26
|
var import_executor_helpers = require("../../lib/util/executor-helpers");
|
|
28
27
|
let PROJECT_ROOT;
|
|
@@ -32,7 +31,6 @@ async function integrateProviderExecutor(options, context) {
|
|
|
32
31
|
const { targetEnvironment, environmentVariables } = (0, import_executor_helpers.resolveHubtypeEnvironment)(context, options);
|
|
33
32
|
const botonicApiService = new import_api_service.BotonicAPIService({
|
|
34
33
|
projectRoot: PROJECT_ROOT,
|
|
35
|
-
workspaceRoot: (0, import_path.resolve)(context.root),
|
|
36
34
|
environmentVariables,
|
|
37
35
|
targetEnvironment
|
|
38
36
|
});
|
|
@@ -22,7 +22,6 @@ __export(executor_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(executor_exports);
|
|
24
24
|
var import_enquirer = require("enquirer");
|
|
25
|
-
var import_path = require("path");
|
|
26
25
|
var import_api_service = require("../../lib/api-service");
|
|
27
26
|
var import_executor_helpers = require("../../lib/util/executor-helpers");
|
|
28
27
|
async function loginToHubtypeExecutor(options, context) {
|
|
@@ -31,7 +30,6 @@ async function loginToHubtypeExecutor(options, context) {
|
|
|
31
30
|
const { targetEnvironment, environmentVariables } = (0, import_executor_helpers.resolveHubtypeEnvironment)(context, options);
|
|
32
31
|
const botonicApiService = new import_api_service.BotonicAPIService({
|
|
33
32
|
projectRoot,
|
|
34
|
-
workspaceRoot: (0, import_path.resolve)(context.root),
|
|
35
33
|
environmentVariables,
|
|
36
34
|
targetEnvironment
|
|
37
35
|
});
|
|
@@ -30,12 +30,12 @@ async function logoutFromHubtypeExecutor(options, context) {
|
|
|
30
30
|
const homeCredsPath = (0, import_path.join)(
|
|
31
31
|
(0, import_os.homedir)(),
|
|
32
32
|
import_constants.BOTONIC_HOME_DIRNAME,
|
|
33
|
-
import_constants.
|
|
33
|
+
import_constants.ENV_CREDENTIALS_FILENAME
|
|
34
34
|
);
|
|
35
35
|
const workspaceCredsPath = (0, import_path.join)(
|
|
36
36
|
(0, import_path.resolve)(context.root),
|
|
37
37
|
import_constants.BOTONIC_HOME_DIRNAME,
|
|
38
|
-
import_constants.
|
|
38
|
+
import_constants.ENV_CREDENTIALS_FILENAME
|
|
39
39
|
);
|
|
40
40
|
let removed = false;
|
|
41
41
|
if (await (0, import_fs_extra.pathExists)(workspaceCredsPath)) {
|
|
@@ -70,7 +70,10 @@ function writeToLogFile(line) {
|
|
|
70
70
|
}
|
|
71
71
|
function checkFrontailInstalled(cwd) {
|
|
72
72
|
try {
|
|
73
|
-
(0, import_child_process.execSync)(
|
|
73
|
+
(0, import_child_process.execSync)(`${cwd}/node_modules/.bin/frontail --help`, {
|
|
74
|
+
stdio: "pipe",
|
|
75
|
+
cwd
|
|
76
|
+
});
|
|
74
77
|
return true;
|
|
75
78
|
} catch {
|
|
76
79
|
return false;
|
|
@@ -86,10 +89,8 @@ function startFrontail(logPath, port, cwd) {
|
|
|
86
89
|
return null;
|
|
87
90
|
}
|
|
88
91
|
const frontailProcess = (0, import_child_process.spawn)(
|
|
89
|
-
|
|
92
|
+
`${cwd}/node_modules/.bin/frontail`,
|
|
90
93
|
[
|
|
91
|
-
"--no",
|
|
92
|
-
"frontail",
|
|
93
94
|
"--port",
|
|
94
95
|
port.toString(),
|
|
95
96
|
"--theme",
|
|
@@ -205,33 +206,60 @@ async function serveBotExecutor(options, context) {
|
|
|
205
206
|
`${colors.dim}[lambda] No previous Lambda container to clean up.${colors.reset}`
|
|
206
207
|
);
|
|
207
208
|
}
|
|
208
|
-
const logViewerEnabled = options.logViewer === true;
|
|
209
209
|
logFilePath = null;
|
|
210
210
|
const logViewerPort = options.logViewerPort ?? 9001;
|
|
211
211
|
const webchatDevPort = tryReadWebchatDevPort(fullProjectRoot);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
logFilePath
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
212
|
+
logFilePath = initLogFile(fullProjectRoot);
|
|
213
|
+
const frontailProcess = startFrontail(
|
|
214
|
+
logFilePath,
|
|
215
|
+
logViewerPort,
|
|
216
|
+
context.root
|
|
217
|
+
);
|
|
218
|
+
const logViewerInfo = `${colors.dim}Webchat:${colors.reset} ${colors.bold}http://localhost:${webchatDevPort}/?logs=${logViewerPort}${colors.reset} (with logs panel)
|
|
219
|
+
${colors.dim}Log viewer:${colors.reset} http://localhost:${logViewerPort}`;
|
|
219
220
|
const useTunnel = true;
|
|
220
221
|
const tunnelPort = options.tunnelPort ?? 3001;
|
|
221
222
|
let effectiveLambdaEndpoint = options.lambdaEndpoint;
|
|
222
223
|
let startLambda = !options.skipLambda && !effectiveLambdaEndpoint;
|
|
223
224
|
let tunnelDeployResult;
|
|
225
|
+
let devSessionLambdaFunctionName;
|
|
226
|
+
let registeredTunnelUrl;
|
|
227
|
+
let deployedBotId;
|
|
228
|
+
let selectedBotId;
|
|
229
|
+
let deployedApiUrl;
|
|
230
|
+
let deployedAccessToken;
|
|
231
|
+
let deployedRefreshToken;
|
|
232
|
+
let deployedClientId;
|
|
224
233
|
if (useTunnel) {
|
|
225
234
|
await (0, import_executor_helpers.ensureHubtypeLoginBeforeTunnel)(context, fullProjectRoot, {
|
|
226
235
|
env: options.env,
|
|
227
236
|
configuration
|
|
228
237
|
});
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
try {
|
|
239
|
+
selectedBotId = await (0, import_executor_helpers.selectBotForServe)(context, fullProjectRoot, {
|
|
240
|
+
env: options.env,
|
|
241
|
+
configuration,
|
|
242
|
+
botName: options.botName
|
|
243
|
+
});
|
|
244
|
+
} catch (botDeployErr) {
|
|
245
|
+
if ((0, import_executor_helpers.isBotDeployedRestartRequired)(botDeployErr)) {
|
|
246
|
+
if (frontailProcess) {
|
|
247
|
+
frontailProcess.kill("SIGTERM");
|
|
248
|
+
}
|
|
249
|
+
if (process.stdin.isTTY) {
|
|
250
|
+
try {
|
|
251
|
+
process.stdin.setRawMode(false);
|
|
252
|
+
} catch {
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
process.stdin.pause();
|
|
256
|
+
process.stdin.unref();
|
|
257
|
+
return { success: true };
|
|
258
|
+
}
|
|
259
|
+
throw botDeployErr;
|
|
260
|
+
}
|
|
233
261
|
console.log(
|
|
234
|
-
`${colors.dim}Starting tunnel flow (Lambda + cloudflared +
|
|
262
|
+
`${colors.dim}Starting tunnel flow (Lambda + cloudflared + dev session registration)...${colors.reset}
|
|
235
263
|
`
|
|
236
264
|
);
|
|
237
265
|
}
|
|
@@ -256,18 +284,79 @@ ${colors.dim}Log viewer:${colors.reset} http://localhost:${logViewerPort}` :
|
|
|
256
284
|
console.log(`${colors.dim}Tunnel URL: ${tunnelUrl}${colors.reset}
|
|
257
285
|
`);
|
|
258
286
|
console.log(
|
|
259
|
-
`${colors.dim}
|
|
287
|
+
`${colors.dim}Setting up dev bot and writing dev session env vars...${colors.reset}`
|
|
260
288
|
);
|
|
261
289
|
const deployResult = await (0, import_executor_helpers.performDeployLocalRuntimeWithEndpoint)(
|
|
262
290
|
context,
|
|
263
291
|
fullProjectRoot,
|
|
264
292
|
tunnelUrl,
|
|
265
|
-
{
|
|
293
|
+
{
|
|
294
|
+
env: options.env,
|
|
295
|
+
configuration,
|
|
296
|
+
whatsappPhone: options.phone,
|
|
297
|
+
selectedBotId
|
|
298
|
+
}
|
|
266
299
|
);
|
|
267
300
|
tunnelDeployResult = {
|
|
268
301
|
targetEnvironment: deployResult.targetEnvironment,
|
|
269
|
-
environmentVariables: deployResult.environmentVariables
|
|
302
|
+
environmentVariables: deployResult.environmentVariables,
|
|
303
|
+
teardownWebchat: deployResult.teardownWebchat
|
|
270
304
|
};
|
|
305
|
+
devSessionLambdaFunctionName = deployResult.lambdaFunctionName;
|
|
306
|
+
registeredTunnelUrl = tunnelUrl;
|
|
307
|
+
deployedBotId = deployResult.botId ?? "";
|
|
308
|
+
deployedApiUrl = deployResult.apiUrl ?? "https://api.hubtype.com";
|
|
309
|
+
deployedAccessToken = deployResult.accessToken ?? "";
|
|
310
|
+
deployedRefreshToken = deployResult.refreshToken ?? "";
|
|
311
|
+
deployedClientId = deployResult.clientId ?? "";
|
|
312
|
+
const resolvedEnv = ["local", "dev", "dev2", "qa", "prod"].includes(deployResult.targetEnvironment) ? deployResult.targetEnvironment : "local";
|
|
313
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
314
|
+
fullProjectRoot,
|
|
315
|
+
"VITE_DEV_SESSION_URL",
|
|
316
|
+
tunnelUrl,
|
|
317
|
+
resolvedEnv
|
|
318
|
+
);
|
|
319
|
+
if (devSessionLambdaFunctionName) {
|
|
320
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
321
|
+
fullProjectRoot,
|
|
322
|
+
"VITE_DEV_LAMBDA_FUNCTION_NAME",
|
|
323
|
+
devSessionLambdaFunctionName,
|
|
324
|
+
resolvedEnv
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
328
|
+
fullProjectRoot,
|
|
329
|
+
"VITE_DEV_BOT_ID",
|
|
330
|
+
deployResult.botId ?? "",
|
|
331
|
+
resolvedEnv
|
|
332
|
+
);
|
|
333
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
334
|
+
fullProjectRoot,
|
|
335
|
+
"VITE_DEV_API_URL",
|
|
336
|
+
deployResult.apiUrl ?? "https://api.hubtype.com",
|
|
337
|
+
resolvedEnv
|
|
338
|
+
);
|
|
339
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
340
|
+
fullProjectRoot,
|
|
341
|
+
"VITE_DEV_ACCESS_TOKEN",
|
|
342
|
+
deployResult.accessToken ?? "",
|
|
343
|
+
resolvedEnv
|
|
344
|
+
);
|
|
345
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
346
|
+
fullProjectRoot,
|
|
347
|
+
"VITE_DEV_REFRESH_TOKEN",
|
|
348
|
+
deployResult.refreshToken ?? "",
|
|
349
|
+
resolvedEnv
|
|
350
|
+
);
|
|
351
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
352
|
+
fullProjectRoot,
|
|
353
|
+
"VITE_DEV_CLIENT_ID",
|
|
354
|
+
deployResult.clientId ?? "",
|
|
355
|
+
resolvedEnv
|
|
356
|
+
);
|
|
357
|
+
console.log(
|
|
358
|
+
`${colors.dim}VITE_DEV_SESSION_URL written to .env.${resolvedEnv}${colors.reset}`
|
|
359
|
+
);
|
|
271
360
|
effectiveLambdaEndpoint = tunnelUrl;
|
|
272
361
|
startLambda = false;
|
|
273
362
|
}
|
|
@@ -312,7 +401,7 @@ ${colors.webviews}[webviews]${colors.reset} - Webviews Vite dev server
|
|
|
312
401
|
`);
|
|
313
402
|
if (useExternalLambda && !useTunnel) {
|
|
314
403
|
console.log(
|
|
315
|
-
`${colors.dim}\u{1F4A1} Ensure you have run
|
|
404
|
+
`${colors.dim}\u{1F4A1} Ensure you have run the serve target first to register your dev session.${colors.reset}
|
|
316
405
|
`
|
|
317
406
|
);
|
|
318
407
|
}
|
|
@@ -340,13 +429,23 @@ ${colors.webviews}[webviews]${colors.reset} - Webviews Vite dev server
|
|
|
340
429
|
}
|
|
341
430
|
const noOpenEnv = options.open === false ? "VITE_SERVE_OPEN=false " : "";
|
|
342
431
|
const viteEnvAppId = `VITE_HUBTYPE_APP_ID=${effectiveAppId}`;
|
|
343
|
-
const
|
|
344
|
-
const
|
|
432
|
+
const viteDevSessionEnv = registeredTunnelUrl && devSessionLambdaFunctionName ? `VITE_DEV_SESSION_URL=${registeredTunnelUrl} VITE_DEV_LAMBDA_FUNCTION_NAME=${devSessionLambdaFunctionName} ` : "";
|
|
433
|
+
const viteDevCredentialsEnv = deployedBotId ? [
|
|
434
|
+
`VITE_DEV_BOT_ID=${deployedBotId}`,
|
|
435
|
+
`VITE_DEV_API_URL=${deployedApiUrl ?? "https://api.hubtype.com"}`,
|
|
436
|
+
`VITE_DEV_ACCESS_TOKEN=${deployedAccessToken ?? ""}`,
|
|
437
|
+
`VITE_DEV_REFRESH_TOKEN=${deployedRefreshToken ?? ""}`,
|
|
438
|
+
`VITE_DEV_CLIENT_ID=${deployedClientId ?? ""}`
|
|
439
|
+
].join(" ") + " " : "";
|
|
440
|
+
const logViewerEnv = `LOG_VIEWER_PORT=${logViewerPort} `;
|
|
441
|
+
const botHasWhatsapp = deployedBotId ? (0, import_executor_helpers.selectedBotHasActiveWhatsapp)(deployedBotId) : false;
|
|
442
|
+
const whatsappPanelEnv = botHasWhatsapp ? `BOT_HAS_WHATSAPP=1 ${options.phone ? `WHATSAPP_PANEL_PHONE=${options.phone} ` : ""}` : "";
|
|
443
|
+
const webchatCommand = `${noOpenEnv}${logViewerEnv}${viteDevSessionEnv}${viteDevCredentialsEnv}${whatsappPanelEnv}${viteEnvAppId} TARGET_APP=webchat MODE=${configuration} vite --config ${configPath}`;
|
|
345
444
|
processes.push(
|
|
346
445
|
spawnProcess(webchatCommand, "webchat", colors.webchat, fullProjectRoot)
|
|
347
446
|
);
|
|
348
447
|
if (!options.skipWebviews) {
|
|
349
|
-
const webviewsCommand = `${noOpenEnv}${viteEnvAppId} TARGET_APP=webviews MODE=${configuration} vite --config ${configPath}`;
|
|
448
|
+
const webviewsCommand = `${noOpenEnv}${viteDevSessionEnv}${viteEnvAppId} TARGET_APP=webviews MODE=${configuration} vite --config ${configPath}`;
|
|
350
449
|
processes.push(
|
|
351
450
|
spawnProcess(
|
|
352
451
|
webviewsCommand,
|
|
@@ -364,6 +463,22 @@ ${colors.webviews}[webviews]${colors.reset} - Webviews Vite dev server
|
|
|
364
463
|
`
|
|
365
464
|
${colors.bold}\u{1F6D1} Shutting down all processes...${colors.reset}`
|
|
366
465
|
);
|
|
466
|
+
try {
|
|
467
|
+
const envConfig = ["local", "dev", "dev2", "qa", "prod"].includes(tunnelDeployResult?.targetEnvironment ?? "") ? tunnelDeployResult.targetEnvironment : "local";
|
|
468
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
469
|
+
fullProjectRoot,
|
|
470
|
+
"VITE_DEV_SESSION_URL",
|
|
471
|
+
"",
|
|
472
|
+
envConfig
|
|
473
|
+
);
|
|
474
|
+
(0, import_executor_helpers.writeEnvVarToEnvFile)(
|
|
475
|
+
fullProjectRoot,
|
|
476
|
+
"VITE_DEV_LAMBDA_FUNCTION_NAME",
|
|
477
|
+
"",
|
|
478
|
+
envConfig
|
|
479
|
+
);
|
|
480
|
+
} catch {
|
|
481
|
+
}
|
|
367
482
|
processes.forEach(({ name, color, process: proc }) => {
|
|
368
483
|
console.log(`${color}[${name}]${colors.reset} Stopping...`);
|
|
369
484
|
proc.kill("SIGTERM");
|
|
@@ -405,6 +520,9 @@ ${colors.bold}\u{1F6D1} Shutting down all processes...${colors.reset}`
|
|
|
405
520
|
});
|
|
406
521
|
});
|
|
407
522
|
} catch (error) {
|
|
523
|
+
if ((0, import_executor_helpers.isBotDeployedRestartRequired)(error)) {
|
|
524
|
+
return { success: true };
|
|
525
|
+
}
|
|
408
526
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
409
527
|
console.error(`\u274C Error starting dev server:`, errorMessage);
|
|
410
528
|
return { success: false };
|
|
@@ -34,15 +34,23 @@
|
|
|
34
34
|
"description": "Local port the Lambda listens on (used for cloudflared --url). Default 3001.",
|
|
35
35
|
"default": 3001
|
|
36
36
|
},
|
|
37
|
-
"logViewer": {
|
|
38
|
-
"type": "boolean",
|
|
39
|
-
"description": "When true, start a web-based log viewer (frontail) and open webchat with an embedded logs panel.",
|
|
40
|
-
"default": false
|
|
41
|
-
},
|
|
42
37
|
"logViewerPort": {
|
|
43
38
|
"type": "number",
|
|
44
39
|
"description": "Port for the log viewer web interface",
|
|
45
40
|
"default": 9001
|
|
41
|
+
},
|
|
42
|
+
"provider": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Channel provider to enable local routing for. Currently only 'whatsapp' is supported.",
|
|
45
|
+
"enum": ["whatsapp"]
|
|
46
|
+
},
|
|
47
|
+
"phone": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "Your phone number in E.164 format (e.g. +34612345678). Required when provider=whatsapp. Messages from this number will route to your local Lambda."
|
|
50
|
+
},
|
|
51
|
+
"botName": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Name of the Hubtype bot to use for local development. Skips the interactive bot-selection prompt."
|
|
46
54
|
}
|
|
47
55
|
},
|
|
48
56
|
"required": []
|