@botonic/nx-plugin 2.25.0 → 2.27.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +4 -4
  3. package/generators.json +0 -26
  4. package/migrations.json +1 -38
  5. package/package.json +1 -1
  6. package/src/executors/e2e-webchat/botonic-package-publish.spec.ts +3 -3
  7. package/src/executors/serve-bot/executor.js +98 -16
  8. package/src/executors/serve-bot/schema.json +10 -0
  9. package/src/generators/action/files/__name__.spec.ts.template +4 -4
  10. package/src/generators/action/files/__name__.ts.template +5 -5
  11. package/src/generators/action/generator.js +1 -1
  12. package/src/generators/bot-app/files/.eslintrc.json.template +30 -1
  13. package/src/generators/bot-app/files/src/client/webchat/index.tsx.template +1 -6
  14. package/src/generators/bot-app/files/src/server/bot/actions/not-found.ts.template +7 -6
  15. package/src/generators/bot-app/files/src/server/bot/actions/welcome.ts.template +7 -6
  16. package/src/generators/bot-app/files/src/server/bot/index.ts.template +9 -11
  17. package/src/generators/bot-app/files/src/server/bot/plugins/ai-agents/index.ts.template +4 -4
  18. package/src/generators/bot-app/files/src/server/bot/plugins/flow-builder/index.ts.template +5 -5
  19. package/src/generators/bot-app/files/src/server/bot/routes.ts.template +5 -5
  20. package/src/generators/bot-app/files/src/server/bot/tracking.ts.template +4 -4
  21. package/src/generators/bot-app/files/src/server/lambda/handler.js.template +1 -6
  22. package/src/generators/bot-app/files/vite/plugins/dev-log-viewer-html.plugin.ts.template +65 -0
  23. package/src/generators/bot-app/files/vite/webchat.config.ts.template +14 -1
  24. package/src/generators/bot-app/generator.js +6 -2
  25. package/src/generators/bot-app/lilara-version.json +1 -1
  26. package/src/generators/bot-app/schema.d.ts +1 -0
  27. package/src/generators/bot-app/schema.json +4 -0
  28. package/src/generators/custom-message/files/__name__-output.ts.template +12 -10
  29. package/src/generators/custom-message/generator.js +1 -1
  30. package/src/{cursor-commands → generators/preset/files/.claude/commands}/update-bot.md +7 -7
  31. package/src/{cursor-commands → generators/preset/files/.claude/commands}/update-botonic.md +5 -3
  32. package/src/{migrations/add-botonic-update-bots-skill/files/.cursor → generators/preset/files/.claude}/scripts/update-bot/discover-bots.sh +1 -1
  33. package/src/generators/preset/files/{.cursor → .claude}/skills/botonic-action/SKILL.md +21 -21
  34. package/src/generators/preset/files/{.cursor → .claude}/skills/botonic-custom-message/SKILL.md +11 -12
  35. package/src/generators/preset/files/{.cursor → .claude}/skills/botonic-webview/SKILL.md +8 -8
  36. package/src/generators/preset/files/.cursor/commands/update-bot.md +1 -3
  37. package/src/generators/preset/files/.cursor/commands/update-botonic.md +1 -3
  38. package/src/lib/util/executor-helpers.d.ts +0 -1
  39. package/src/lib/util/executor-helpers.js +1 -8
  40. package/src/generators/bot-app-migrations/migrate-fix-css-code-split/generator.d.ts +0 -5
  41. package/src/generators/bot-app-migrations/migrate-fix-css-code-split/generator.js +0 -92
  42. package/src/generators/bot-app-migrations/migrate-fix-css-code-split/schema.json +0 -15
  43. package/src/generators/bot-app-migrations/migrate-pnpm-compat/generator.d.ts +0 -5
  44. package/src/generators/bot-app-migrations/migrate-pnpm-compat/generator.js +0 -97
  45. package/src/generators/bot-app-migrations/migrate-pnpm-compat/schema.json +0 -15
  46. package/src/generators/bot-app-migrations/migrate-webchat-trigger/generator.d.ts +0 -5
  47. package/src/generators/bot-app-migrations/migrate-webchat-trigger/generator.js +0 -165
  48. package/src/generators/bot-app-migrations/migrate-webchat-trigger/schema.json +0 -15
  49. package/src/generators/preset/files/.cursor/scripts/update-bot/discover-bots.sh +0 -67
  50. package/src/migrations/add-botonic-update-bots-skill/add-botonic-update-bots-skill.migration.d.ts +0 -2
  51. package/src/migrations/add-botonic-update-bots-skill/add-botonic-update-bots-skill.migration.js +0 -52
  52. package/src/migrations/add-botonic-update-bots-skill/add-botonic-update-bots-skill.migration.md +0 -23
  53. package/src/migrations/add-botonic-update-bots-skill/files/.cursor/commands/update-bot.md +0 -5
  54. package/src/migrations/add-botonic-update-bots-skill/files/.cursor/commands/update-botonic.md +0 -5
  55. package/src/migrations/add-botonic-update-bots-skill/files/.cursor/scripts/update-bot/find-migration-guides.sh +0 -70
  56. package/src/migrations/add-botonic-update-bots-skill/schema.json +0 -5
  57. package/src/migrations/add-lilara-registry/add-lilara-registry.migration.d.ts +0 -2
  58. package/src/migrations/add-lilara-registry/add-lilara-registry.migration.js +0 -49
  59. package/src/migrations/add-lilara-registry/schema.json +0 -5
  60. package/src/migrations/fix-css-code-split/fix-css-code-split.migration.md +0 -45
  61. package/src/migrations/remove-codeartifact-registry/remove-codeartifact-registry.migration.d.ts +0 -2
  62. package/src/migrations/remove-codeartifact-registry/remove-codeartifact-registry.migration.js +0 -59
  63. package/src/migrations/remove-codeartifact-registry/schema.json +0 -5
  64. package/src/migrations/sync-pending-bot-migrations/schema.json +0 -5
  65. package/src/migrations/sync-pending-bot-migrations/sync-pending-bot-migrations.migration.d.ts +0 -2
  66. package/src/migrations/sync-pending-bot-migrations/sync-pending-bot-migrations.migration.js +0 -137
  67. package/src/migrations/sync-pending-bot-migrations/sync-pending-bot-migrations.migration.md +0 -19
  68. package/src/migrations/update-cursor-commands-to-stubs/schema.json +0 -5
  69. package/src/migrations/update-cursor-commands-to-stubs/update-cursor-commands-to-stubs.migration.d.ts +0 -2
  70. package/src/migrations/update-cursor-commands-to-stubs/update-cursor-commands-to-stubs.migration.js +0 -61
  71. package/src/migrations/update-pnpm-workspace-scripts/schema.json +0 -4
  72. package/src/migrations/update-pnpm-workspace-scripts/update-pnpm-workspace-scripts.migration.d.ts +0 -2
  73. package/src/migrations/update-pnpm-workspace-scripts/update-pnpm-workspace-scripts.migration.js +0 -47
  74. /package/src/generators/preset/files/{.cursor → .claude}/scripts/update-bot/find-migration-guides.sh +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 2.27.0 (2026-04-29)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **webchat-react:** add design system gateway for @lilara/ui-web-react (BLT-2334) ([#859](https://github.com/metis-ai/hubtype-product/pull/859))
6
+ - Hero Header transitions ([#860](https://github.com/metis-ai/hubtype-product/pull/860))
7
+ - **botonic:** session and Hubtype API adaptations (non-webviews) ([#810](https://github.com/metis-ai/hubtype-product/pull/810))
8
+
9
+ ### 🩹 Fixes
10
+
11
+ - **nx-plugin:** stabilize vitest execution ([#798](https://github.com/metis-ai/hubtype-product/pull/798))
12
+ - **nx-plugin:** inject lilara version into bot-app generator ([#795](https://github.com/metis-ai/hubtype-product/pull/795))
13
+
14
+ ### ❤️ Thank You
15
+
16
+ - David Hidalgo @Davidhidalgo
17
+ - Marc Rabat @vanbasten17
18
+
19
+ ## 2.26.0 (2026-03-31)
20
+
21
+ ### 🚀 Features
22
+
23
+ - **webchat-react:** chat input action bar and contextual send/mic #BLT-2290 ([#788](https://github.com/metis-ai/hubtype-product/pull/788))
24
+ - **botonic:** allow Cloudflare tunnel hostnames in Vite webchat ([#780](https://github.com/metis-ai/hubtype-product/pull/780))
25
+
26
+ ### ❤️ Thank You
27
+
28
+ - David Hidalgo @Davidhidalgo
29
+
1
30
  ## 2.25.0 (2026-03-27)
2
31
 
3
32
  This was a version bump only for @botonic/nx-plugin to align it with other projects, there were no code changes.
package/README.md CHANGED
@@ -37,7 +37,7 @@ nx g @botonic/nx-plugin:bot-app my-bot
37
37
 
38
38
  #### What it creates:
39
39
 
40
- - **Modern Actions**: Async functions using `BotContext` instead of React class components
40
+ - **Modern Actions**: Async functions using `BotonicContext` instead of React class components
41
41
  - **React 18 Webchat**: Uses `@botonic/webchat` with `createRoot` and `StrictMode`
42
42
  - **Current Dependencies**: Uses modern Botonic packages and avoids legacy packages
43
43
  - **Custom Messages Structure**: Empty structure ready for your custom components
@@ -173,11 +173,11 @@ export class Welcome extends React.Component {
173
173
  **After (modern async function):**
174
174
 
175
175
  ```typescript
176
- import { BotContext } from '@botonic/core'
176
+ import { BotonicContext } from '@botonic/core'
177
177
  import { MessageAction } from '@botonic/shared'
178
178
 
179
- export async function Welcome({ sendMessage }: BotContext) {
180
- await sendMessage([
179
+ export async function Welcome({ sendMessages }: BotonicContext) {
180
+ await sendMessages([
181
181
  {
182
182
  type: 'text',
183
183
  data: { text: "Hello! I'm your bot 🤖\nWelcome to Botonic!" },
package/generators.json CHANGED
@@ -30,32 +30,6 @@
30
30
  "factory": "./src/generators/webview/generator",
31
31
  "schema": "./src/generators/webview/schema.json",
32
32
  "description": "Create a webview component for Botonic"
33
- },
34
- "migrate-webchat-trigger": {
35
- "factory": "./src/generators/bot-app-migrations/migrate-webchat-trigger/generator",
36
- "schema": "./src/generators/bot-app-migrations/migrate-webchat-trigger/schema.json",
37
- "description": "Migrate a bot app to use @botonic/webchat-react/trigger lazy-loading entry point",
38
- "hidden": true,
39
- "x-bot-app-migration": true,
40
- "x-introduced-in": "2.18.0",
41
- "x-migration-guide": "src/generators/bot-app-migrations/migrate-webchat-trigger/migrate-webchat-trigger.md"
42
- },
43
- "migrate-fix-css-code-split": {
44
- "factory": "./src/generators/bot-app-migrations/migrate-fix-css-code-split/generator",
45
- "schema": "./src/generators/bot-app-migrations/migrate-fix-css-code-split/schema.json",
46
- "description": "Add cssCodeSplit: false to vite webchat and webviews configs",
47
- "hidden": true,
48
- "x-bot-app-migration": true,
49
- "x-introduced-in": "2.18.0"
50
- },
51
- "migrate-pnpm-compat": {
52
- "factory": "./src/generators/bot-app-migrations/migrate-pnpm-compat/generator",
53
- "schema": "./src/generators/bot-app-migrations/migrate-pnpm-compat/schema.json",
54
- "description": "Add **/node_modules/** to eslint ignorePatterns and update README npm refs to pnpm",
55
- "hidden": true,
56
- "x-bot-app-migration": true,
57
- "x-introduced-in": "2.18.0",
58
- "x-migration-guide": "src/generators/bot-app-migrations/migrate-pnpm-compat/migrate-pnpm-compat.md"
59
33
  }
60
34
  }
61
35
  }
package/migrations.json CHANGED
@@ -1,40 +1,3 @@
1
1
  {
2
- "generators": {
3
- "add-botonic-update-bots-skill": {
4
- "version": "2.17.0",
5
- "description": "Add /update-botonic and /update-bot Cursor commands for workspace and per-bot version migrations",
6
- "cli": "nx",
7
- "implementation": "./src/migrations/add-botonic-update-bots-skill/add-botonic-update-bots-skill.migration"
8
- },
9
- "sync-pending-bot-migrations": {
10
- "version": "2.17.0",
11
- "description": "Write .botonic/pending-bot-migrations.json with bot-app generators pending for each bot",
12
- "cli": "nx",
13
- "implementation": "./src/migrations/sync-pending-bot-migrations/sync-pending-bot-migrations.migration"
14
- },
15
- "update-pnpm-workspace-scripts": {
16
- "version": "2.18.0",
17
- "description": "Update find-migration-guides.sh to reference pnpm instead of npm",
18
- "cli": "nx",
19
- "implementation": "./src/migrations/update-pnpm-workspace-scripts/update-pnpm-workspace-scripts.migration"
20
- },
21
- "update-cursor-commands-to-stubs": {
22
- "version": "2.18.0",
23
- "description": "Replace full-content .cursor/commands/*.md with stable stubs that reference the canonical source in node_modules",
24
- "cli": "nx",
25
- "implementation": "./src/migrations/update-cursor-commands-to-stubs/update-cursor-commands-to-stubs.migration"
26
- },
27
- "add-lilara-registry": {
28
- "version": "2.19.0",
29
- "description": "(Legacy) Add @hubtype-lilara:registry for CodeArtifact; run remove-codeartifact-registry (2.23+) for public npm",
30
- "cli": "nx",
31
- "implementation": "./src/migrations/add-lilara-registry/add-lilara-registry.migration"
32
- },
33
- "remove-codeartifact-registry": {
34
- "version": "2.23.0",
35
- "description": "Remove CodeArtifact @botonic / @hubtype-lilara registry and auth lines from .npmrc (switch to public npm)",
36
- "cli": "nx",
37
- "implementation": "./src/migrations/remove-codeartifact-registry/remove-codeartifact-registry.migration"
38
- }
39
- }
2
+ "generators": {}
40
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botonic/nx-plugin",
3
- "version": "2.25.0",
3
+ "version": "2.27.0",
4
4
  "description": "Nx plugin for creating Botonic bot applications",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -70,12 +70,12 @@ test.describe('Botonic Publish E2E', () => {
70
70
  await ensureWebchatOpen(page)
71
71
 
72
72
  const input = page.locator('textarea').first()
73
- const sendButton = page.locator('button[class*="send-button"]').first()
74
-
75
73
  await expect(input).toBeVisible({ timeout: 5000 })
76
- await expect(sendButton).toBeVisible()
77
74
 
78
75
  await input.fill('test message')
76
+
77
+ const sendButton = page.getByRole('button', { name: 'Send message' })
78
+ await expect(sendButton).toBeVisible()
79
79
  await sendButton.click()
80
80
 
81
81
  const messages = page.locator('[class*="message-module"]')
@@ -51,6 +51,77 @@ const colors = {
51
51
  dim: "\x1B[2m",
52
52
  bold: "\x1B[1m"
53
53
  };
54
+ let logFilePath = null;
55
+ function initLogFile(projectRoot) {
56
+ const logsDir = (0, import_path.join)(projectRoot, ".botonic", "logs");
57
+ (0, import_fs.mkdirSync)(logsDir, { recursive: true });
58
+ const logPath = (0, import_path.join)(logsDir, "serve-bot.log");
59
+ (0, import_fs.writeFileSync)(logPath, "");
60
+ return logPath;
61
+ }
62
+ function writeToLogFile(line) {
63
+ if (logFilePath) {
64
+ try {
65
+ (0, import_fs.appendFileSync)(logFilePath, `${line}
66
+ `);
67
+ } catch {
68
+ }
69
+ }
70
+ }
71
+ function checkFrontailInstalled(cwd) {
72
+ try {
73
+ (0, import_child_process.execSync)("npx --no frontail --help", { stdio: "pipe", cwd });
74
+ return true;
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+ function startFrontail(logPath, port, cwd) {
80
+ if (!checkFrontailInstalled(cwd)) {
81
+ console.warn(`${colors.bold}\u26A0\uFE0F frontail is not installed.${colors.reset}`);
82
+ console.warn(
83
+ ` Run: ${colors.dim}pnpm install${colors.reset} to install dependencies
84
+ `
85
+ );
86
+ return null;
87
+ }
88
+ const frontailProcess = (0, import_child_process.spawn)(
89
+ "npx",
90
+ [
91
+ "--no",
92
+ "frontail",
93
+ "--port",
94
+ port.toString(),
95
+ "--theme",
96
+ "dark",
97
+ "--lines",
98
+ "500",
99
+ "--ui-hide-topbar",
100
+ logPath
101
+ ],
102
+ {
103
+ cwd,
104
+ stdio: ["ignore", "pipe", "pipe"],
105
+ detached: false
106
+ }
107
+ );
108
+ frontailProcess.on("error", (error) => {
109
+ console.error(
110
+ `${colors.dim}[log-viewer]${colors.reset} Error: ${error.message}`
111
+ );
112
+ });
113
+ return frontailProcess;
114
+ }
115
+ function tryReadWebchatDevPort(projectRoot) {
116
+ try {
117
+ const configPath = (0, import_path.join)(projectRoot, "vite", "build.config.ts");
118
+ const content = (0, import_fs.readFileSync)(configPath, "utf8");
119
+ const match = content.match(/webchat:\s*\{\s*port:\s*(\d+)/);
120
+ if (match) return parseInt(match[1], 10);
121
+ } catch {
122
+ }
123
+ return 4201;
124
+ }
54
125
  function getAppIdFromEnvFile(projectRoot) {
55
126
  const envFilePath = (0, import_path.join)(projectRoot, ".env.local");
56
127
  if (!(0, import_fs.existsSync)(envFilePath)) {
@@ -77,9 +148,9 @@ function prefixOutput(stream, prefix, color) {
77
148
  const rl = readline.createInterface({ input: stream });
78
149
  rl.on("line", (line) => {
79
150
  const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
80
- console.log(
81
- `${colors.dim}${timestamp}${colors.reset} ${color}[${prefix}]${colors.reset} ${line}`
82
- );
151
+ const formattedLine = `${colors.dim}${timestamp}${colors.reset} ${color}[${prefix}]${colors.reset} ${line}`;
152
+ console.log(formattedLine);
153
+ writeToLogFile(`${timestamp} [${prefix}] ${line}`);
83
154
  });
84
155
  }
85
156
  function spawnProcess(command, name, color, cwd) {
@@ -134,11 +205,21 @@ async function serveBotExecutor(options, context) {
134
205
  `${colors.dim}[lambda] No previous Lambda container to clean up.${colors.reset}`
135
206
  );
136
207
  }
208
+ const logViewerEnabled = options.logViewer === true;
209
+ logFilePath = null;
210
+ const logViewerPort = options.logViewerPort ?? 9001;
211
+ const webchatDevPort = tryReadWebchatDevPort(fullProjectRoot);
212
+ let frontailProcess = null;
213
+ if (logViewerEnabled) {
214
+ logFilePath = initLogFile(fullProjectRoot);
215
+ frontailProcess = startFrontail(logFilePath, logViewerPort, context.root);
216
+ }
217
+ const logViewerInfo = logViewerEnabled ? `${colors.dim}Webchat:${colors.reset} ${colors.bold}http://localhost:${webchatDevPort}/?logs=${logViewerPort}${colors.reset} (with logs panel)
218
+ ${colors.dim}Log viewer:${colors.reset} http://localhost:${logViewerPort}` : "";
137
219
  const useTunnel = true;
138
220
  const tunnelPort = options.tunnelPort ?? 3001;
139
221
  let effectiveLambdaEndpoint = options.lambdaEndpoint;
140
222
  let startLambda = !options.skipLambda && !effectiveLambdaEndpoint;
141
- let tunnelBotId;
142
223
  let tunnelDeployResult;
143
224
  if (useTunnel) {
144
225
  await (0, import_executor_helpers.ensureHubtypeLoginBeforeTunnel)(context, fullProjectRoot, {
@@ -183,7 +264,6 @@ async function serveBotExecutor(options, context) {
183
264
  tunnelUrl,
184
265
  { env: options.env, configuration }
185
266
  );
186
- tunnelBotId = deployResult.botId;
187
267
  tunnelDeployResult = {
188
268
  targetEnvironment: deployResult.targetEnvironment,
189
269
  environmentVariables: deployResult.environmentVariables
@@ -194,6 +274,9 @@ async function serveBotExecutor(options, context) {
194
274
  } catch (tunnelError) {
195
275
  const msg = tunnelError instanceof Error ? tunnelError.message : String(tunnelError);
196
276
  console.error(`\u274C Tunnel flow failed: ${msg}`);
277
+ if (frontailProcess) {
278
+ frontailProcess.kill("SIGTERM");
279
+ }
197
280
  return { success: false };
198
281
  }
199
282
  let effectiveAppId = appId;
@@ -206,14 +289,6 @@ async function serveBotExecutor(options, context) {
206
289
  }
207
290
  if (configuration !== "local" && effectiveAppId) {
208
291
  await (0, import_executor_helpers.writeAppIdToEnvFile)(fullProjectRoot, effectiveAppId, configuration);
209
- if (tunnelBotId) {
210
- (0, import_executor_helpers.writeEnvVarToEnvFile)(
211
- fullProjectRoot,
212
- "VITE_HUBTYPE_BOT_ID",
213
- tunnelBotId,
214
- configuration
215
- );
216
- }
217
292
  }
218
293
  const useExternalLambda = Boolean(effectiveLambdaEndpoint);
219
294
  if (!useTunnel) {
@@ -227,6 +302,7 @@ ${colors.bold}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
227
302
  ${colors.dim}Project:${colors.reset} ${context.projectName}
228
303
  ${colors.dim}Configuration:${colors.reset} ${configuration}
229
304
  ${colors.dim}App ID:${colors.reset} ${effectiveAppId}
305
+ ${logViewerInfo}
230
306
  ${useExternalLambda ? `${colors.dim}Lambda (external):${colors.reset} ${effectiveLambdaEndpoint}
231
307
  ` : ""}
232
308
  ${colors.bot}[bot]${colors.reset} - Node/Lambda build watcher
@@ -264,13 +340,13 @@ ${colors.webviews}[webviews]${colors.reset} - Webviews Vite dev server
264
340
  }
265
341
  const noOpenEnv = options.open === false ? "VITE_SERVE_OPEN=false " : "";
266
342
  const viteEnvAppId = `VITE_HUBTYPE_APP_ID=${effectiveAppId}`;
267
- const viteEnvBotId = tunnelBotId != null ? ` VITE_HUBTYPE_BOT_ID=${tunnelBotId}` : "";
268
- const webchatCommand = `${noOpenEnv}${viteEnvAppId}${viteEnvBotId} TARGET_APP=webchat MODE=${configuration} vite --config ${configPath}`;
343
+ const logViewerEnv = logViewerEnabled ? `LOG_VIEWER_PORT=${logViewerPort} ` : "";
344
+ const webchatCommand = `${noOpenEnv}${logViewerEnv}${viteEnvAppId} TARGET_APP=webchat MODE=${configuration} vite --config ${configPath}`;
269
345
  processes.push(
270
346
  spawnProcess(webchatCommand, "webchat", colors.webchat, fullProjectRoot)
271
347
  );
272
348
  if (!options.skipWebviews) {
273
- const webviewsCommand = `${noOpenEnv}${viteEnvAppId}${viteEnvBotId} TARGET_APP=webviews MODE=${configuration} vite --config ${configPath}`;
349
+ const webviewsCommand = `${noOpenEnv}${viteEnvAppId} TARGET_APP=webviews MODE=${configuration} vite --config ${configPath}`;
274
350
  processes.push(
275
351
  spawnProcess(
276
352
  webviewsCommand,
@@ -292,6 +368,12 @@ ${colors.bold}\u{1F6D1} Shutting down all processes...${colors.reset}`
292
368
  console.log(`${color}[${name}]${colors.reset} Stopping...`);
293
369
  proc.kill("SIGTERM");
294
370
  });
371
+ if (frontailProcess) {
372
+ console.log(
373
+ `${colors.dim}[log-viewer]${colors.reset} Stopping log viewer...`
374
+ );
375
+ frontailProcess.kill("SIGTERM");
376
+ }
295
377
  };
296
378
  process.on("SIGINT", cleanup);
297
379
  process.on("SIGTERM", cleanup);
@@ -34,6 +34,16 @@
34
34
  "type": "number",
35
35
  "description": "Local port the Lambda listens on (used for cloudflared --url). Default 3001.",
36
36
  "default": 3001
37
+ },
38
+ "logViewer": {
39
+ "type": "boolean",
40
+ "description": "When true, start a web-based log viewer (frontail) and open webchat with an embedded logs panel.",
41
+ "default": false
42
+ },
43
+ "logViewerPort": {
44
+ "type": "number",
45
+ "description": "Port for the log viewer web interface",
46
+ "default": 9001
37
47
  }
38
48
  },
39
49
  "required": []
@@ -3,13 +3,13 @@ import { describe, expect, it, vi } from 'vitest'
3
3
  import { <%= className %> } from './<%= fileName %>'
4
4
 
5
5
  describe('<%= className %>', () => {
6
- it('should call sendMessage and return OK', async () => {
7
- const sendMessage = vi.fn().mockResolvedValue(undefined)
8
- const mockContext = { sendMessage } as any
6
+ it('should call sendMessages and return OK', async () => {
7
+ const sendMessages = vi.fn().mockResolvedValue(undefined)
8
+ const mockContext = { sendMessages } as any
9
9
 
10
10
  const result = await <%= className %>(mockContext)
11
11
 
12
- expect(sendMessage).toHaveBeenCalledOnce()
12
+ expect(sendMessages).toHaveBeenCalledOnce()
13
13
  expect(result).toEqual({ status: 200, response: 'OK' })
14
14
  })
15
15
  })
@@ -1,12 +1,12 @@
1
- import { BotContext, BotServerMessageFactory } from '@botonic/shared'
1
+ import { BotonicContext, BotServerMessageFactory } from '@botonic/shared'
2
2
 
3
- export async function <%= className %>({ sendMessage }: BotContext) {
3
+ export async function <%= className %>({ sendMessages }: BotonicContext) {
4
4
  // TODO: Implement your action logic here
5
- await sendMessage(
5
+ await sendMessages([
6
6
  BotServerMessageFactory.createText({
7
7
  text: 'Hello from <%= className %>!',
8
- })
9
- )
8
+ }),
9
+ ])
10
10
 
11
11
  return {
12
12
  status: 200,
@@ -80,7 +80,7 @@ function updateRoutes(tree, options) {
80
80
  (0, import_bot_app_utils.insertRouteBeforeFlowBuilder)(tree, options.routesPath, [
81
81
  ` {`,
82
82
  ` text: '${options.resolvedRouteMatcher}',`,
83
- ` action: async () => await ${options.className}(botContext),`,
83
+ ` action: async () => await ${options.className}(botonicContext),`,
84
84
  ` },`
85
85
  ]);
86
86
  }
@@ -4,7 +4,36 @@
4
4
  "overrides": [
5
5
  {
6
6
  "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
- "rules": {}
7
+ "rules": {
8
+ "no-restricted-imports": [
9
+ "warn",
10
+ {
11
+ "paths": [
12
+ {
13
+ "name": "@lilara/ui-web-react",
14
+ "message": "Import UI components from '@botonic/webchat-react' instead of '@lilara/ui-web-react'."
15
+ },
16
+ {
17
+ "name": "@lilara/ui-web",
18
+ "message": "Import UI components from '@botonic/webchat-react' instead of '@lilara/ui-web'."
19
+ }
20
+ ],
21
+ "patterns": [
22
+ {
23
+ "group": [
24
+ "@lilara/ui-web-react/*",
25
+ "!@lilara/ui-web-react/styles.css"
26
+ ],
27
+ "message": "Import UI components from '@botonic/webchat-react' instead of '@lilara/ui-web-react'."
28
+ },
29
+ {
30
+ "group": ["@lilara/ui-web/*"],
31
+ "message": "Import UI components from '@botonic/webchat-react' instead of '@lilara/ui-web'."
32
+ }
33
+ ]
34
+ }
35
+ ]
36
+ }
8
37
  },
9
38
  {
10
39
  "files": ["*.ts", "*.tsx"],
@@ -41,12 +41,7 @@ function App({
41
41
  options: WebchatOptions
42
42
  }) {
43
43
  // Default avatar SVG
44
- const defaultAvatarSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="17" height="18" viewBox="0 0 17 18" fill="none">
45
- <circle cx="8.5" cy="9" r="8.5" fill="#24252C"/>
46
- <path d="M5.9647 5.03898C5.9647 4.61514 5.80479 4.45386 5.36004 4.45386C4.90405 4.45386 4.74414 4.61639 4.74414 5.03898C4.74414 5.46281 4.90405 5.6241 5.36004 5.6241C5.80479 5.6241 5.9647 5.46281 5.9647 5.03898Z" fill="white"/>
47
- <path d="M11.1872 4.45386C10.7312 4.45386 10.5713 4.61639 10.5713 5.03898C10.5713 5.46281 10.7312 5.6241 11.1872 5.6241C11.6307 5.6241 11.7919 5.46156 11.7919 5.03898C11.7906 4.61514 11.6307 4.45386 11.1872 4.45386Z" fill="white"/>
48
- <path d="M10.99 6.47729H10.7063C10.4352 6.47729 10.3365 6.60233 10.2503 6.99995L9.32574 10.8498C9.15332 11.4975 8.95591 11.6726 8.40117 11.6726H8.13005C7.5878 11.6726 7.3779 11.4988 7.20548 10.8498L6.28092 6.99995C6.19471 6.60108 6.1085 6.47729 5.83737 6.47729H5.55376C5.30762 6.47729 5.19643 6.6636 5.29513 7.04996L6.37962 11.2112C6.56453 11.9339 6.93436 12.2953 7.56406 12.4315C7.72399 12.4691 7.8102 12.5566 7.8102 12.7304V14.5884C7.8102 14.7997 7.94638 14.9373 8.15504 14.9373H8.38993C8.58733 14.9373 8.73476 14.7997 8.73476 14.5884V12.7316C8.73476 12.5566 8.82097 12.4703 8.9809 12.4328C9.59686 12.2953 9.96793 11.9339 10.1641 11.2124L11.2486 7.05121C11.3473 6.66485 11.2361 6.47729 10.99 6.47729Z" fill="white"/>
49
- </svg>`
44
+ const defaultAvatarSvg = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="16" rx="6" fill="#24252C"/><rect x="0.2" y="0.2" width="15.6" height="15.6" rx="5.8" stroke="white" stroke-opacity="0.2" stroke-width="0.4"/><path d="M7.94153 11.5381C8.09581 11.5381 8.20905 11.5664 8.28235 11.6396C8.35546 11.7129 8.38095 11.8235 8.38098 11.9688C8.38098 12.1147 8.35354 12.2258 8.27942 12.2988C8.20522 12.3717 8.09189 12.3994 7.94153 12.3994C7.78327 12.3994 7.66826 12.3719 7.59387 12.2988C7.51926 12.2255 7.49329 12.1143 7.49329 11.9688C7.49332 11.8227 7.52127 11.7117 7.5968 11.6387C7.67209 11.5659 7.78719 11.5381 7.94153 11.5381Z" fill="white" stroke="white" stroke-width="0.0694444"/><path d="M5.7464 3.29785C5.90068 3.29785 6.01392 3.32621 6.08722 3.39941C6.16033 3.47263 6.18582 3.58322 6.18585 3.72852C6.18585 3.87448 6.15839 3.98553 6.08429 4.05859C6.01009 4.13148 5.89676 4.15918 5.7464 4.15918C5.58813 4.15917 5.47313 4.13167 5.39874 4.05859C5.32414 3.98529 5.29816 3.87409 5.29816 3.72852C5.29819 3.58251 5.32614 3.47149 5.40167 3.39844C5.47696 3.32567 5.59206 3.29786 5.7464 3.29785Z" fill="white" stroke="white" stroke-width="0.0694444"/><path d="M10.0797 3.29785C10.234 3.29785 10.3472 3.32621 10.4205 3.39941C10.4936 3.47263 10.5191 3.58322 10.5192 3.72852C10.5192 3.87449 10.4917 3.98554 10.4176 4.05859C10.3434 4.13148 10.2301 4.15918 10.0797 4.15918C9.92145 4.15917 9.80644 4.13166 9.73206 4.05859C9.65744 3.9853 9.63147 3.87409 9.63147 3.72852C9.6315 3.5825 9.65945 3.47149 9.73499 3.39844C9.81027 3.32568 9.92538 3.29786 10.0797 3.29785Z" fill="white" stroke="white" stroke-width="0.0694444"/><path d="M6.29785 4.92285C6.39622 4.92286 6.46906 4.94612 6.52344 5.01172C6.57475 5.07369 6.60632 5.17037 6.63574 5.30469H6.63477L7.25879 7.89648L7.30371 8.04395C7.35035 8.17596 7.40297 8.26354 7.46875 8.32129C7.55517 8.39706 7.67153 8.42676 7.84961 8.42676H8.03418C8.21668 8.42676 8.33272 8.39686 8.41797 8.32129C8.50461 8.24437 8.56623 8.11445 8.625 7.89648L9.24805 5.30469L9.29785 5.12988C9.31646 5.08141 9.33846 5.04164 9.36523 5.01074C9.42164 4.94591 9.49655 4.92285 9.59473 4.92285H9.78809C9.88168 4.92285 9.95707 4.9593 9.99609 5.03809C10.0332 5.11348 10.0335 5.22116 9.99902 5.35449L9.26562 8.1582V8.15918C9.19723 8.40664 9.09972 8.59529 8.96387 8.73438C8.86165 8.83893 8.73882 8.91483 8.59277 8.96582L8.43848 9.00879C8.38813 9.02043 8.35392 9.03858 8.33203 9.06348C8.31053 9.08814 8.29688 9.12375 8.29688 9.17676V10.4229C8.29688 10.5029 8.2682 10.571 8.21875 10.6191C8.16946 10.6671 8.1018 10.6933 8.02637 10.6934H7.86621C7.78698 10.6934 7.71857 10.6677 7.66992 10.6191C7.62126 10.5705 7.5957 10.5021 7.5957 10.4229V9.17676C7.5957 9.12375 7.58205 9.08814 7.56055 9.06348C7.53862 9.03845 7.50373 9.02045 7.45312 9.00879V9.00781C7.23793 8.9604 7.06298 8.87439 6.92578 8.73535C6.82281 8.63087 6.74253 8.49757 6.68164 8.33301L6.62695 8.1582L5.89355 5.35449C5.8591 5.22116 5.85838 5.11348 5.89551 5.03809C5.93444 4.95924 6.01001 4.92295 6.10352 4.92285H6.29785Z" fill="white" stroke="white" stroke-width="0.0694444"/></svg>`
50
45
 
51
46
  const avatarConfig = {
52
47
  bot: {
@@ -1,10 +1,11 @@
1
- import { BotContext, BotServerMessageFactory } from '@botonic/shared'
1
+ import { BotonicContext, BotServerMessageFactory } from '@botonic/shared'
2
2
 
3
- export async function NotFound({ sendMessage }: BotContext) {
4
- const message = BotServerMessageFactory.createText({
5
- text: 'Sorry, I didn\'t understand that.\n\nTry saying "hello" to get started!',
6
- })
7
- await sendMessage(message)
3
+ export async function NotFound({ sendMessages }: BotonicContext) {
4
+ await sendMessages([
5
+ BotServerMessageFactory.createText({
6
+ text: 'Sorry, I didn\'t understand that.\n\nTry saying "hello" to get started!',
7
+ }),
8
+ ])
8
9
 
9
10
  return {
10
11
  status: 200,
@@ -1,10 +1,11 @@
1
- import { BotContext, BotServerMessageFactory } from '@botonic/shared'
1
+ import { BotonicContext, BotServerMessageFactory } from '@botonic/shared'
2
2
 
3
- export async function Welcome({ sendMessage }: BotContext) {
4
- const message = BotServerMessageFactory.createText({
5
- text: `Hello! I'm <%= className %> 🤖\n\nWelcome to your new Botonic bot! You can start building your conversation flow by editing the routes and actions.`,
6
- })
7
- await sendMessage(message)
3
+ export async function Welcome({ sendMessages }: BotonicContext) {
4
+ await sendMessages([
5
+ BotServerMessageFactory.createText({
6
+ text: `Hello! I'm <%= className %> 🤖\n\nWelcome to your new Botonic bot! You can start building your conversation flow by editing the routes and actions.`,
7
+ }),
8
+ ])
8
9
 
9
10
  return {
10
11
  status: 200,
@@ -1,15 +1,16 @@
1
1
  // eslint-disable-next-line @nx/enforce-module-boundaries
2
2
  import { CoreBot } from '@botonic/core'
3
- import { BotContext, BotServerMessageFactory } from '@botonic/shared'
3
+ import { BotonicContext, BotServerMessageFactory } from '@botonic/shared'
4
4
 
5
5
  import { plugins } from './plugins'
6
6
  import { routes } from './routes'
7
7
 
8
- const DefaultFallbackAction = async ({ sendMessage }: BotContext) => {
9
- const message = BotServerMessageFactory.createText({
10
- text: "I don't understand you",
11
- })
12
- await sendMessage(message)
8
+ const DefaultFallbackAction = async ({ sendMessages }: BotonicContext) => {
9
+ await sendMessages([
10
+ BotServerMessageFactory.createText({
11
+ text: "I don't understand you",
12
+ }),
13
+ ])
13
14
 
14
15
  return {
15
16
  status: 200,
@@ -17,10 +18,10 @@ const DefaultFallbackAction = async ({ sendMessage }: BotContext) => {
17
18
  }
18
19
  }
19
20
 
20
- const defaultRoutes = (botContext: BotContext) => [
21
+ const defaultRoutes = (botonicContext: BotonicContext) => [
21
22
  {
22
23
  path: '404',
23
- action: async () => await DefaultFallbackAction(botContext),
24
+ action: async () => await DefaultFallbackAction(botonicContext),
24
25
  },
25
26
  ]
26
27
 
@@ -35,9 +36,6 @@ export class NodeApp {
35
36
  this.bot = new CoreBot(config)
36
37
  }
37
38
 
38
- input(args: any) {
39
- return this.bot.input(args)
40
- }
41
39
  }
42
40
 
43
41
  export const app = new NodeApp({ routes, plugins })
@@ -1,5 +1,5 @@
1
1
  import { AiAgentArgs, BotonicPluginAiAgents } from '@botonic/plugin-ai-agents'
2
- import { BotContext } from '@botonic/shared'
2
+ import { BotonicContext } from '@botonic/shared'
3
3
 
4
4
  import { customTools } from '../../tools'
5
5
  import { BotPlugins } from '../../types'
@@ -16,15 +16,15 @@ export const aiAgentsPlugin = hasAiAgentsConfig
16
16
  : null
17
17
 
18
18
  export async function getAiAgentResponse(
19
- botContext: BotContext<BotPlugins>,
19
+ botonicContext: BotonicContext<BotPlugins>,
20
20
  aiAgentArgs: AiAgentArgs
21
21
  ) {
22
- const aiAgentPlugin = botContext.plugins.aiAgents
22
+ const aiAgentPlugin = botonicContext.plugins.aiAgents
23
23
  if (!aiAgentPlugin) {
24
24
  throw new Error(
25
25
  'AI Agents plugin is not configured. Please set AZURE_OPENAI_API_KEY or OPENAI_API_KEY environment variable.'
26
26
  )
27
27
  }
28
- const response = await aiAgentPlugin.getInference(botContext, aiAgentArgs)
28
+ const response = await aiAgentPlugin.getInference(botonicContext, aiAgentArgs)
29
29
  return response
30
30
  }
@@ -4,7 +4,7 @@ import {
4
4
  BotonicPluginFlowBuilderOptions,
5
5
  FlowBuilderJSONVersion,
6
6
  } from '@botonic/plugin-flow-builder'
7
- import { BotContext } from '@botonic/shared'
7
+ import { BotonicContext } from '@botonic/shared'
8
8
 
9
9
  import { trackEventToHubtypeAnalytics } from '../../tracking'
10
10
  import { isLambdaLocal } from '../../utils'
@@ -14,14 +14,14 @@ export const flowBuilderConfig: BotonicPluginFlowBuilderOptions = {
14
14
  flowVersion: isLambdaLocal()
15
15
  ? FlowBuilderJSONVersion.DRAFT
16
16
  : FlowBuilderJSONVersion.LATEST,
17
- trackEvent: async (botContext: BotContext, eventAction, args) => {
18
- await trackEventToHubtypeAnalytics(botContext, eventAction, args)
17
+ trackEvent: async (botonicContext: BotonicContext, eventAction, args) => {
18
+ await trackEventToHubtypeAnalytics(botonicContext, eventAction, args)
19
19
  },
20
20
  getAiAgentResponse: async (
21
- botContext: BotContext,
21
+ botonicContext: BotonicContext,
22
22
  aiAgentArgs: AiAgentArgs
23
23
  ) => {
24
- return getAiAgentResponse(botContext, aiAgentArgs)
24
+ return getAiAgentResponse(botonicContext, aiAgentArgs)
25
25
  },
26
26
  }
27
27
 
@@ -1,23 +1,23 @@
1
1
  import { FlowBuilderAction } from '@botonic/plugin-flow-builder'
2
- import { BotContext } from '@botonic/shared'
2
+ import { BotonicContext } from '@botonic/shared'
3
3
 
4
4
  import { Welcome, NotFound } from './actions'
5
5
 
6
- export const routes = (botContext: BotContext) => {
6
+ export const routes = (botonicContext: BotonicContext) => {
7
7
  return [
8
8
  {
9
9
  text: /^(hi|hello|start|hola)$/i,
10
- action: async () => await Welcome(botContext),
10
+ action: async () => await Welcome(botonicContext),
11
11
  },
12
12
  {
13
13
  path: 'flow-builder',
14
14
  text: /.*/,
15
15
  payload: /.*/,
16
- action: async () => await FlowBuilderAction.botonicInit(botContext),
16
+ action: async () => await FlowBuilderAction.botonicInit(botonicContext),
17
17
  },
18
18
  {
19
19
  path: 'not-found',
20
- action: async () => await NotFound(botContext),
20
+ action: async () => await NotFound(botonicContext),
21
21
  },
22
22
  ]
23
23
  }