@botonic/nx-plugin 2.23.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 (206) hide show
  1. package/CHANGELOG.md +420 -0
  2. package/README.md +279 -0
  3. package/executors.json +55 -0
  4. package/generators.json +61 -0
  5. package/migrations.json +40 -0
  6. package/package.json +54 -0
  7. package/src/cursor-commands/update-bot.md +114 -0
  8. package/src/cursor-commands/update-botonic.md +63 -0
  9. package/src/executors/build-node-app/executor.d.ts +5 -0
  10. package/src/executors/build-node-app/executor.js +65 -0
  11. package/src/executors/build-node-app/schema.d.js +16 -0
  12. package/src/executors/build-node-app/schema.json +25 -0
  13. package/src/executors/delete-bot/executor.d.ts +5 -0
  14. package/src/executors/delete-bot/executor.js +112 -0
  15. package/src/executors/delete-bot/schema.d.js +16 -0
  16. package/src/executors/delete-bot/schema.json +35 -0
  17. package/src/executors/deploy-local-runtime/executor.d.ts +5 -0
  18. package/src/executors/deploy-local-runtime/executor.js +144 -0
  19. package/src/executors/deploy-local-runtime/schema.d.js +16 -0
  20. package/src/executors/deploy-local-runtime/schema.json +34 -0
  21. package/src/executors/deploy-netlify-snapshot/executor.d.ts +8 -0
  22. package/src/executors/deploy-netlify-snapshot/executor.js +79 -0
  23. package/src/executors/deploy-netlify-snapshot/schema.d.js +16 -0
  24. package/src/executors/deploy-netlify-snapshot/schema.json +31 -0
  25. package/src/executors/deploy-to-hubtype/executor.d.ts +5 -0
  26. package/src/executors/deploy-to-hubtype/executor.js +308 -0
  27. package/src/executors/deploy-to-hubtype/schema.d.js +16 -0
  28. package/src/executors/deploy-to-hubtype/schema.json +31 -0
  29. package/src/executors/e2e-webchat/botonic-package-publish.spec.ts +84 -0
  30. package/src/executors/e2e-webchat/executor.d.ts +5 -0
  31. package/src/executors/e2e-webchat/executor.js +134 -0
  32. package/src/executors/e2e-webchat/schema.d.js +16 -0
  33. package/src/executors/e2e-webchat/schema.json +35 -0
  34. package/src/executors/integrate-provider/executor.d.ts +5 -0
  35. package/src/executors/integrate-provider/executor.js +155 -0
  36. package/src/executors/integrate-provider/schema.d.js +16 -0
  37. package/src/executors/integrate-provider/schema.json +30 -0
  38. package/src/executors/login-to-hubtype/executor.d.ts +5 -0
  39. package/src/executors/login-to-hubtype/executor.js +79 -0
  40. package/src/executors/login-to-hubtype/schema.d.js +16 -0
  41. package/src/executors/login-to-hubtype/schema.json +25 -0
  42. package/src/executors/logout-from-hubtype/executor.d.ts +3 -0
  43. package/src/executors/logout-from-hubtype/executor.js +54 -0
  44. package/src/executors/logout-from-hubtype/schema.d.js +16 -0
  45. package/src/executors/logout-from-hubtype/schema.json +9 -0
  46. package/src/executors/run-lambda/executor.d.ts +5 -0
  47. package/src/executors/run-lambda/executor.js +65 -0
  48. package/src/executors/run-lambda/schema.d.js +16 -0
  49. package/src/executors/run-lambda/schema.json +20 -0
  50. package/src/executors/serve-bot/executor.d.ts +5 -0
  51. package/src/executors/serve-bot/executor.js +330 -0
  52. package/src/executors/serve-bot/schema.d.js +16 -0
  53. package/src/executors/serve-bot/schema.json +40 -0
  54. package/src/generators/action/files/__name__.spec.ts.template +15 -0
  55. package/src/generators/action/files/__name__.ts.template +15 -0
  56. package/src/generators/action/generator.d.ts +4 -0
  57. package/src/generators/action/generator.js +112 -0
  58. package/src/generators/action/schema.d.ts +7 -0
  59. package/src/generators/action/schema.js +16 -0
  60. package/src/generators/action/schema.json +43 -0
  61. package/src/generators/bot-app/files/.eslintrc.json.template +18 -0
  62. package/src/generators/bot-app/files/README.md.template +148 -0
  63. package/src/generators/bot-app/files/src/client/custom-messages/index.ts.template +2 -0
  64. package/src/generators/bot-app/files/src/client/webchat/index.html.template +35 -0
  65. package/src/generators/bot-app/files/src/client/webchat/index.tsx.template +107 -0
  66. package/src/generators/bot-app/files/src/client/webchat/styles.css.template +17 -0
  67. package/src/generators/bot-app/files/src/client/webchat/webchat-tokens-overrides.css.template +2 -0
  68. package/src/generators/bot-app/files/src/client/webviews/app.tsx.template +8 -0
  69. package/src/generators/bot-app/files/src/client/webviews/index.html.template +32 -0
  70. package/src/generators/bot-app/files/src/client/webviews/index.tsx.template +18 -0
  71. package/src/generators/bot-app/files/src/server/bot/actions/index.ts.template +2 -0
  72. package/src/generators/bot-app/files/src/server/bot/actions/not-found.ts.template +13 -0
  73. package/src/generators/bot-app/files/src/server/bot/actions/welcome.ts.template +13 -0
  74. package/src/generators/bot-app/files/src/server/bot/index.ts.template +43 -0
  75. package/src/generators/bot-app/files/src/server/bot/plugins/ai-agents/index.ts.template +30 -0
  76. package/src/generators/bot-app/files/src/server/bot/plugins/flow-builder/index.ts.template +28 -0
  77. package/src/generators/bot-app/files/src/server/bot/plugins/index.ts.template +11 -0
  78. package/src/generators/bot-app/files/src/server/bot/routes.ts.template +23 -0
  79. package/src/generators/bot-app/files/src/server/bot/tools/index.ts.template +5 -0
  80. package/src/generators/bot-app/files/src/server/bot/tracking.ts.template +35 -0
  81. package/src/generators/bot-app/files/src/server/bot/types.ts.template +4 -0
  82. package/src/generators/bot-app/files/src/server/bot/utils.ts.template +9 -0
  83. package/src/generators/bot-app/files/src/server/lambda/handler.js.template +24 -0
  84. package/src/generators/bot-app/files/src/server/lambda/package.json +20 -0
  85. package/src/generators/bot-app/files/src/server/lambda/template.yaml.template +20 -0
  86. package/src/generators/bot-app/files/src/shared/constants.ts.template +12 -0
  87. package/src/generators/bot-app/files/vite/base-client.config.ts.template +14 -0
  88. package/src/generators/bot-app/files/vite/base.config.ts.template +20 -0
  89. package/src/generators/bot-app/files/vite/build.config.ts.template +65 -0
  90. package/src/generators/bot-app/files/vite/node.config.ts.template +41 -0
  91. package/src/generators/bot-app/files/vite/plugins/move-html.plugin.ts.template +36 -0
  92. package/src/generators/bot-app/files/vite/webchat.config.ts.template +58 -0
  93. package/src/generators/bot-app/files/vite/webviews.config.ts.template +57 -0
  94. package/src/generators/bot-app/files/vite.config.ts.template +36 -0
  95. package/src/generators/bot-app/generator.d.ts +4 -0
  96. package/src/generators/bot-app/generator.js +294 -0
  97. package/src/generators/bot-app/schema.d.ts +6 -0
  98. package/src/generators/bot-app/schema.js +16 -0
  99. package/src/generators/bot-app/schema.json +36 -0
  100. package/src/generators/bot-app-migrations/migrate-fix-css-code-split/generator.d.ts +5 -0
  101. package/src/generators/bot-app-migrations/migrate-fix-css-code-split/generator.js +92 -0
  102. package/src/generators/bot-app-migrations/migrate-fix-css-code-split/schema.json +15 -0
  103. package/src/generators/bot-app-migrations/migrate-pnpm-compat/generator.d.ts +5 -0
  104. package/src/generators/bot-app-migrations/migrate-pnpm-compat/generator.js +97 -0
  105. package/src/generators/bot-app-migrations/migrate-pnpm-compat/schema.json +15 -0
  106. package/src/generators/bot-app-migrations/migrate-webchat-trigger/generator.d.ts +5 -0
  107. package/src/generators/bot-app-migrations/migrate-webchat-trigger/generator.js +165 -0
  108. package/src/generators/bot-app-migrations/migrate-webchat-trigger/schema.json +15 -0
  109. package/src/generators/custom-message/files/__name__-output.ts.template +21 -0
  110. package/src/generators/custom-message/files/__name__.spec.tsx.template +27 -0
  111. package/src/generators/custom-message/files/__name__.tsx.template +18 -0
  112. package/src/generators/custom-message/generator.d.ts +4 -0
  113. package/src/generators/custom-message/generator.js +235 -0
  114. package/src/generators/custom-message/schema.d.ts +7 -0
  115. package/src/generators/custom-message/schema.js +16 -0
  116. package/src/generators/custom-message/schema.json +44 -0
  117. package/src/generators/preset/files/.cursor/commands/update-bot.md +5 -0
  118. package/src/generators/preset/files/.cursor/commands/update-botonic.md +5 -0
  119. package/src/generators/preset/files/.cursor/scripts/update-bot/discover-bots.sh +67 -0
  120. package/src/generators/preset/files/.cursor/scripts/update-bot/find-migration-guides.sh +70 -0
  121. package/src/generators/preset/files/.cursor/skills/botonic-action/SKILL.md +167 -0
  122. package/src/generators/preset/files/.cursor/skills/botonic-custom-message/SKILL.md +231 -0
  123. package/src/generators/preset/files/.cursor/skills/botonic-webview/SKILL.md +179 -0
  124. package/src/generators/preset/files/.env.prod.template +2 -0
  125. package/src/generators/preset/files/.env.template +2 -0
  126. package/src/generators/preset/files/.npmrc.template +1 -0
  127. package/src/generators/preset/files/README.md.template +174 -0
  128. package/src/generators/preset/files/nx.json +66 -0
  129. package/src/generators/preset/files/package.json +26 -0
  130. package/src/generators/preset/files/tsconfig.base.json +27 -0
  131. package/src/generators/preset/files/tsconfig.base.json.template +27 -0
  132. package/src/generators/preset/files/tsconfig.json +9 -0
  133. package/src/generators/preset/generator.d.ts +4 -0
  134. package/src/generators/preset/generator.js +127 -0
  135. package/src/generators/preset/schema.d.ts +6 -0
  136. package/src/generators/preset/schema.js +16 -0
  137. package/src/generators/preset/schema.json +50 -0
  138. package/src/generators/remove-custom-message/generator.d.ts +4 -0
  139. package/src/generators/remove-custom-message/generator.js +259 -0
  140. package/src/generators/remove-custom-message/schema.d.ts +6 -0
  141. package/src/generators/remove-custom-message/schema.js +16 -0
  142. package/src/generators/remove-custom-message/schema.json +39 -0
  143. package/src/generators/shared/bot-app-utils.d.ts +25 -0
  144. package/src/generators/shared/bot-app-utils.js +209 -0
  145. package/src/generators/webview/files/__name__.spec.tsx.template +20 -0
  146. package/src/generators/webview/files/__name__.tsx.template +19 -0
  147. package/src/generators/webview/generator.d.ts +4 -0
  148. package/src/generators/webview/generator.js +179 -0
  149. package/src/generators/webview/schema.d.ts +5 -0
  150. package/src/generators/webview/schema.js +16 -0
  151. package/src/generators/webview/schema.json +34 -0
  152. package/src/index.d.ts +7 -0
  153. package/src/index.js +56 -0
  154. package/src/lib/api-service.d.ts +110 -0
  155. package/src/lib/api-service.js +591 -0
  156. package/src/lib/bot-config.d.ts +30 -0
  157. package/src/lib/bot-config.js +203 -0
  158. package/src/lib/cloudflared-tunnel.d.ts +29 -0
  159. package/src/lib/cloudflared-tunnel.js +95 -0
  160. package/src/lib/constants.d.ts +13 -0
  161. package/src/lib/constants.js +60 -0
  162. package/src/lib/credentials-handler.d.ts +40 -0
  163. package/src/lib/credentials-handler.js +115 -0
  164. package/src/lib/index.d.ts +10 -0
  165. package/src/lib/index.js +47 -0
  166. package/src/lib/interfaces.d.ts +49 -0
  167. package/src/lib/interfaces.js +16 -0
  168. package/src/lib/util/executor-helpers.d.ts +97 -0
  169. package/src/lib/util/executor-helpers.js +574 -0
  170. package/src/lib/util/file-system.d.ts +8 -0
  171. package/src/lib/util/file-system.js +65 -0
  172. package/src/lib/util/sam-container-cleanup.d.ts +11 -0
  173. package/src/lib/util/sam-container-cleanup.js +55 -0
  174. package/src/lib/util/sam-template.d.ts +9 -0
  175. package/src/lib/util/sam-template.js +71 -0
  176. package/src/lib/util/system.d.ts +1 -0
  177. package/src/lib/util/system.js +30 -0
  178. package/src/migrations/add-botonic-update-bots-skill/add-botonic-update-bots-skill.migration.d.ts +2 -0
  179. package/src/migrations/add-botonic-update-bots-skill/add-botonic-update-bots-skill.migration.js +52 -0
  180. package/src/migrations/add-botonic-update-bots-skill/add-botonic-update-bots-skill.migration.md +23 -0
  181. package/src/migrations/add-botonic-update-bots-skill/files/.cursor/commands/update-bot.md +5 -0
  182. package/src/migrations/add-botonic-update-bots-skill/files/.cursor/commands/update-botonic.md +5 -0
  183. package/src/migrations/add-botonic-update-bots-skill/files/.cursor/scripts/update-bot/discover-bots.sh +67 -0
  184. package/src/migrations/add-botonic-update-bots-skill/files/.cursor/scripts/update-bot/find-migration-guides.sh +70 -0
  185. package/src/migrations/add-botonic-update-bots-skill/schema.json +5 -0
  186. package/src/migrations/add-lilara-registry/add-lilara-registry.migration.d.ts +2 -0
  187. package/src/migrations/add-lilara-registry/add-lilara-registry.migration.js +49 -0
  188. package/src/migrations/add-lilara-registry/schema.json +5 -0
  189. package/src/migrations/fix-css-code-split/fix-css-code-split.migration.md +45 -0
  190. package/src/migrations/remove-codeartifact-registry/remove-codeartifact-registry.migration.d.ts +2 -0
  191. package/src/migrations/remove-codeartifact-registry/remove-codeartifact-registry.migration.js +59 -0
  192. package/src/migrations/remove-codeartifact-registry/schema.json +5 -0
  193. package/src/migrations/sync-pending-bot-migrations/schema.json +5 -0
  194. package/src/migrations/sync-pending-bot-migrations/sync-pending-bot-migrations.migration.d.ts +2 -0
  195. package/src/migrations/sync-pending-bot-migrations/sync-pending-bot-migrations.migration.js +137 -0
  196. package/src/migrations/sync-pending-bot-migrations/sync-pending-bot-migrations.migration.md +19 -0
  197. package/src/migrations/update-cursor-commands-to-stubs/schema.json +5 -0
  198. package/src/migrations/update-cursor-commands-to-stubs/update-cursor-commands-to-stubs.migration.d.ts +2 -0
  199. package/src/migrations/update-cursor-commands-to-stubs/update-cursor-commands-to-stubs.migration.js +61 -0
  200. package/src/migrations/update-pnpm-workspace-scripts/schema.json +4 -0
  201. package/src/migrations/update-pnpm-workspace-scripts/update-pnpm-workspace-scripts.migration.d.ts +2 -0
  202. package/src/migrations/update-pnpm-workspace-scripts/update-pnpm-workspace-scripts.migration.js +47 -0
  203. package/src/migrations/utils/migration-utils.d.ts +109 -0
  204. package/src/migrations/utils/migration-utils.js +448 -0
  205. package/src/plugin.d.ts +15 -0
  206. package/src/plugin.js +246 -0
@@ -0,0 +1,231 @@
1
+ ---
2
+ name: botonic-custom-message
3
+ description: Scaffold and implement a Botonic custom webchat message component using the Nx generator, then guide implementation. Use when the user says "create custom message", "add custom message", "new custom message", "generate custom message", or wants to render a rich or custom UI component in the webchat conversation in a Botonic bot project.
4
+ ---
5
+
6
+ # Botonic Custom Message
7
+
8
+ Scaffold a custom webchat message component and help implement the React component and its output action.
9
+
10
+ ## Workflow
11
+
12
+ ### Step 1 — Gather inputs
13
+
14
+ Collect from context or ask:
15
+
16
+ - **`name`** (required): kebab-case name, e.g. `rating-card`, `product-tile`
17
+ - **`project`** (required): Nx project name of the bot app, e.g. `my-bot`
18
+ - **`directory`** (optional): subdirectory inside `custom-messages/` to group related components, e.g. `cards`
19
+ - **`skipTests`** (optional): `true` to skip spec file generation
20
+ - **`skipAction`** (optional): `true` to skip generating the output action and route
21
+
22
+ ### Step 2 — Run the generator
23
+
24
+ ```bash
25
+ pnpm nx g @botonic/nx-plugin:custom-message --name=<name> --project=<project>
26
+ ```
27
+
28
+ With optional flags:
29
+
30
+ ```bash
31
+ pnpm nx g @botonic/nx-plugin:custom-message rating-card --project my-bot --directory cards
32
+ pnpm nx g @botonic/nx-plugin:custom-message info-banner --project my-bot --skipAction
33
+ ```
34
+
35
+ ### Step 3 — Understand what was generated
36
+
37
+ The generator creates and updates:
38
+
39
+ - `src/client/custom-messages/<name>.tsx` — new React component
40
+ - `src/client/custom-messages/<name>.spec.tsx` — test file (unless `--skipTests`)
41
+ - `src/client/custom-messages/index.ts` — import + registration in `customMessages` map added
42
+ - `src/server/bot/actions/<name>-output.ts` — output action (unless `--skipAction`)
43
+ - `src/server/bot/actions/index.ts` — export added
44
+ - `src/server/bot/routes.ts` — route entry added
45
+ - `src/shared/constants.ts` — `CUSTOM_MESSAGE_TYPES.<CONSTANT>` entry added
46
+
47
+ Read both the component and output action files and show the user the starting points.
48
+
49
+ ### Step 4 — Ask what to implement
50
+
51
+ If not already clear from context, ask: "What data should this message display? What props does the component need?"
52
+
53
+ ### Step 5 — Implement the React component
54
+
55
+ #### Root wrapper: always `Message` with `variant="bot"`
56
+
57
+ Import `Message` from `@botonic/webchat-react` and use it as the root. It handles layout, avatar, timestamp, and — crucially — wraps the children in a `MessageBubble` automatically. Always pass `variant="bot"` explicitly since custom messages are almost always sent by the bot.
58
+
59
+ Never use a plain `<div>` as the root — the message will not render with correct bubble styling, spacing, or theming. Never nest a `MessageBubble` inside `Message` — `Message` already creates one internally; nesting would double-wrap.
60
+
61
+ ```tsx
62
+ import { Message } from '@botonic/webchat-react'
63
+ import React from 'react'
64
+
65
+ import styles from './rating-card.module.css'
66
+
67
+ export const RatingCard: React.FC<RatingCardProps> = (props: any) => {
68
+ return (
69
+ <Message variant='bot' className={styles.bubble}>
70
+ <div className={styles.content}>{/* your content here */}</div>
71
+ </Message>
72
+ )
73
+ }
74
+ ```
75
+
76
+ #### Buttons inside the custom message
77
+
78
+ `ButtonComponent` (exported from `@botonic/webchat-react`) can be rendered inside the `Message`. The webchat automatically injects an `onButtonClick` callback into the component props — use it as the `onClick` handler. The button data (`BotonicButtonContent`) should be built server-side (e.g. with `BotServerMessageFactory.createWebviewButton`) and passed as a prop so the server can embed dynamic values like webview URLs.
79
+
80
+ ```tsx
81
+ import { BotonicButtonContent } from '@botonic/shared'
82
+ import { ButtonComponent, Message } from '@botonic/webchat-react'
83
+
84
+ export interface ProductCardProps {
85
+ title: string
86
+ price: string
87
+ button?: BotonicButtonContent
88
+ onButtonClick?: (button: BotonicButtonContent) => void // injected automatically
89
+ }
90
+
91
+ export const ProductCard: React.FC<ProductCardProps> = (props: any) => {
92
+ const { title, price, button, onButtonClick } = props
93
+ return (
94
+ <Message variant='bot'>
95
+ <div className={styles.content}>
96
+ <div className={styles.title}>{title}</div>
97
+ <div className={styles.price}>{price}</div>
98
+ {button && onButtonClick && <ButtonComponent text={button.title} button={button} onClick={onButtonClick} variant='secondary' />}
99
+ </div>
100
+ </Message>
101
+ )
102
+ }
103
+ ```
104
+
105
+ In the output action, build the button server-side and pass it as a prop:
106
+
107
+ ```typescript
108
+ export async function productCardOutput({ sendMessage, session }: BotContext) {
109
+ const button = BotServerMessageFactory.createWebviewButton({ title: 'View details', webview: WEBVIEWS.PRODUCT }, session)
110
+ await sendMessage(
111
+ BotServerMessageFactory.createCustom({
112
+ name: CUSTOM_MESSAGE_TYPES.PRODUCT_CARD,
113
+ props: { title: 'Widget Pro', price: '€9.99', button },
114
+ })
115
+ )
116
+ return { status: 200, response: 'OK' }
117
+ }
118
+ ```
119
+
120
+ #### Multiple bubbles: send multiple messages from the action
121
+
122
+ `Message` always renders exactly one bubble. If the design requires multiple separate bubbles (e.g. an image bubble followed by a text bubble), send them as separate messages from the output action — do not try to nest multiple `MessageBubble` components inside one `Message`.
123
+
124
+ ```typescript
125
+ // In the output action
126
+ await sendMessage(BotServerMessageFactory.createCustom({ name: CUSTOM_MESSAGE_TYPES.PRODUCT_IMAGE, props: { url } }))
127
+ await sendMessage(BotServerMessageFactory.createCustom({ name: CUSTOM_MESSAGE_TYPES.PRODUCT_DETAIL, props: { title, price } }))
128
+ ```
129
+
130
+ #### Styling: CSS modules only, no inline styles
131
+
132
+ Create `<name>.module.css` next to the component. Never use inline `style={{}}` props.
133
+
134
+ ```css
135
+ /* rating-card.module.css */
136
+ .content {
137
+ display: flex;
138
+ flex-direction: column;
139
+ gap: var(--spacing-xs);
140
+ }
141
+
142
+ .label {
143
+ font-size: var(--font-size-sm);
144
+ font-weight: var(--font-weight-medium);
145
+ }
146
+ ```
147
+
148
+ #### Overriding bubble colours: use component tokens
149
+
150
+ The bubble reads from CSS custom properties. Override them scoped to the component by targeting the class passed to `Message`:
151
+
152
+ ```css
153
+ /* rating-card.module.css */
154
+ .bubble {
155
+ --webchat-message-bubble-bot-bg: #f0f7ff;
156
+ --webchat-message-bubble-bot-text: #1a3c5e;
157
+ --webchat-message-bubble-bot-border-color: #b3d1f0;
158
+ }
159
+ ```
160
+
161
+ Pass the class to `Message`:
162
+
163
+ ```tsx
164
+ <Message className={styles.bubble}>
165
+ ```
166
+
167
+ The token overrides cascade into `MessageBubble` automatically. Available tokens:
168
+
169
+ | Token | Purpose |
170
+ | ------------------------------------------- | ----------------- |
171
+ | `--webchat-message-bubble-bot-bg` | Background colour |
172
+ | `--webchat-message-bubble-bot-text` | Text colour |
173
+ | `--webchat-message-bubble-bot-border-color` | Border colour |
174
+
175
+ Use these same tokens inside the component CSS so colours stay in sync:
176
+
177
+ ```css
178
+ .divider {
179
+ border-top: 1px solid var(--webchat-message-bubble-bot-border-color);
180
+ }
181
+ ```
182
+
183
+ ### Step 6 — Implement the output action
184
+
185
+ Update `<name>-output.ts` to pass the required props in `data`:
186
+
187
+ ```typescript
188
+ export async function ratingCardOutput({ sendMessage }: BotContext) {
189
+ await sendMessage(
190
+ BotServerMessageFactory.createCustom({
191
+ name: CUSTOM_MESSAGE_TYPES.RATING_CARD,
192
+ props: {
193
+ rating: 0,
194
+ label: 'How was your experience?',
195
+ },
196
+ })
197
+ )
198
+ return { status: 200, response: 'OK' }
199
+ }
200
+ ```
201
+
202
+ The `data.props` object must match the component's props interface exactly.
203
+
204
+ ### Step 7 — Trigger from a route
205
+
206
+ The output action is already wired to a route in `routes.ts`. Call it from another action when needed:
207
+
208
+ ```typescript
209
+ import { ratingCardOutput } from './rating-card-output'
210
+
211
+ export async function EndConversation(ctx: BotContext) {
212
+ await ratingCardOutput(ctx)
213
+ return { status: 200, response: 'OK' }
214
+ }
215
+ ```
216
+
217
+ ### Removing a custom message
218
+
219
+ ```bash
220
+ pnpm nx g @botonic/nx-plugin:remove-custom-message <name> --project=<project>
221
+ ```
222
+
223
+ ## Options reference
224
+
225
+ | Option | Default | Notes |
226
+ | ------------ | ------- | ------------------------------------------------- |
227
+ | `name` | — | Required. kebab-case recommended |
228
+ | `project` | — | Required. Nx project name |
229
+ | `directory` | — | Subdirectory inside `custom-messages/` (optional) |
230
+ | `skipTests` | `false` | Omit `<name>.spec.tsx` |
231
+ | `skipAction` | `false` | Omit output action and route |
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: botonic-webview
3
+ description: Scaffold and implement a Botonic webview component using the Nx generator, then guide implementation. Use when the user says "create webview", "add webview", "new webview", "generate webview", or wants to build an in-chat form, panel, or interactive UI component in a Botonic bot project.
4
+ ---
5
+
6
+ # Botonic Webview
7
+
8
+ Scaffold a client-side webview component and help implement its UI.
9
+
10
+ ## Workflow
11
+
12
+ ### Step 1 — Gather inputs
13
+
14
+ Collect from context or ask:
15
+
16
+ - **`name`** (required): kebab-case name, e.g. `booking-form`, `order-detail`
17
+ - **`project`** (required): Nx project name of the bot app, e.g. `my-bot`
18
+ - **`skipTests`** (optional): `true` to skip spec file generation
19
+
20
+ ### Step 2 — Run the generator
21
+
22
+ ```bash
23
+ pnpm nx g @botonic/nx-plugin:webview --name=<name> --project=<project>
24
+ ```
25
+
26
+ ### Step 3 — Understand what was generated
27
+
28
+ The generator creates and updates:
29
+
30
+ - `src/client/webviews/<name>.tsx` — new webview component
31
+ - `src/client/webviews/<name>.spec.tsx` — test file (unless `--skipTests`)
32
+ - `src/client/webviews/index.tsx` — import + `WebviewDefinition` entry added
33
+ - `src/shared/constants.ts` — `WEBVIEWS.<CONSTANT>` entry added
34
+
35
+ Read the generated component file and show the user the starting point.
36
+
37
+ ### Step 4 — Ask what to implement
38
+
39
+ If not already clear from context, ask: "What should this webview display or allow the user to do?"
40
+
41
+ ### Step 5 — Implement the webview
42
+
43
+ Use `WebviewFrame`, `WebviewHeader`, `WebviewBody` from `@botonic/webviews` as the structural shell.
44
+ Access request data and the close handler via `useWebviewRequest`.
45
+
46
+ > **CSS reset and design tokens are loaded automatically.** `WebviewApp` imports `@lilara/foundations/reset.css` and `@lilara/foundations/tokens.css` as side effects — no manual CSS import is needed in the app's `index.tsx`.
47
+
48
+ #### Always use UI components from `@botonic/webchat-react`
49
+
50
+ Never use raw HTML elements (`<input>`, `<button>`, `<select>`, etc.) in webviews. All interactive and display elements must come from `@botonic/webchat-react`, which re-exports the full design system.
51
+
52
+ > **react-aria conventions.** These components are built on react-aria — prop names differ from standard HTML. Check the `.d.ts` types before using any component. Key differences: `Button` uses `onPress` not `onClick`; `Checkbox` and `Switch` use `isSelected` not `checked`, and `onChange` receives a `boolean` not an event.
53
+
54
+ | Component | Use for |
55
+ | ----------------------------------------- | ----------------------------------- |
56
+ | `Button` | Any clickable action |
57
+ | `TextInput` | Single-line text fields |
58
+ | `TextArea` | Multi-line text fields |
59
+ | `Checkbox` | Boolean toggles |
60
+ | `Switch` | On/off toggles |
61
+ | `Select` / `SelectWithSections` | Single-choice dropdowns |
62
+ | `Multiselect` / `MultiselectWithSections` | Multi-choice dropdowns |
63
+ | `Slider` | Numeric range input |
64
+ | `Banner` | Inline alerts and feedback messages |
65
+ | `Tooltip` | Contextual hints |
66
+ | `Badge` / `Tag` | Status indicators |
67
+ | `Spinner` | Loading states |
68
+
69
+ **Static content**
70
+
71
+ ```tsx
72
+ import { useWebviewRequest, WebviewBody, WebviewFrame, WebviewHeader } from '@botonic/webviews'
73
+
74
+ export const OrderDetail = () => {
75
+ const { payload, closeWebview } = useWebviewRequest<{ orderId: string }>()
76
+
77
+ return (
78
+ <WebviewFrame>
79
+ <WebviewHeader title='Order Detail' onClose={closeWebview} />
80
+ <WebviewBody variant='padded'>
81
+ <p>Order ID: {payload?.orderId}</p>
82
+ </WebviewBody>
83
+ </WebviewFrame>
84
+ )
85
+ }
86
+ ```
87
+
88
+ **Form with submission**
89
+
90
+ ```tsx
91
+ import { Button, TextInput } from '@botonic/webchat-react'
92
+ import { useWebviewRequest, WebviewBody, WebviewFrame, WebviewHeader } from '@botonic/webviews'
93
+
94
+ export const BookingForm = () => {
95
+ const { closeWebview } = useWebviewRequest()
96
+ const [name, setName] = useState('')
97
+
98
+ const handleSubmit = (e: React.FormEvent) => {
99
+ e.preventDefault()
100
+ closeWebview({ name })
101
+ }
102
+
103
+ return (
104
+ <WebviewFrame>
105
+ <WebviewHeader title='Booking' />
106
+ <WebviewBody variant='padded'>
107
+ <form onSubmit={handleSubmit}>
108
+ <TextInput value={name} onChange={e => setName(e.target.value)} placeholder='Your name' />
109
+ <Button type='submit'>Confirm</Button>
110
+ </form>
111
+ </WebviewBody>
112
+ </WebviewFrame>
113
+ )
114
+ }
115
+ ```
116
+
117
+ **Fetching data on load**
118
+
119
+ ```tsx
120
+ import { Banner, Spinner } from '@botonic/webchat-react'
121
+ import { useWebviewRequest, WebviewBody, WebviewFrame, WebviewHeader } from '@botonic/webviews'
122
+
123
+ export const OrderDetail = () => {
124
+ const { payload, closeWebview } = useWebviewRequest<{ orderId: string }>()
125
+ const [order, setOrder] = useState<Order>()
126
+ const [error, setError] = useState<string>()
127
+
128
+ useEffect(() => {
129
+ if (!payload?.orderId) return
130
+ let cancelled = false
131
+ fetchOrder(payload.orderId)
132
+ .then(o => {
133
+ if (!cancelled) setOrder(o)
134
+ })
135
+ .catch(err => {
136
+ if (!cancelled) setError(err?.message ?? 'Failed to load order')
137
+ })
138
+ return () => {
139
+ cancelled = true
140
+ }
141
+ }, [payload?.orderId])
142
+
143
+ return (
144
+ <WebviewFrame>
145
+ <WebviewHeader title='Order Detail' onClose={closeWebview} />
146
+ <WebviewBody variant='padded'>
147
+ {error && <Banner variant='error'>{error}</Banner>}
148
+ {!order && !error && <Spinner />}
149
+ {order && <p>{order.statusMessage}</p>}
150
+ </WebviewBody>
151
+ </WebviewFrame>
152
+ )
153
+ }
154
+ ```
155
+
156
+ ### Step 6 — Open it from a bot action
157
+
158
+ After the webview is ready, remind the user to open it from an action using `BotServerMessageFactory.createWebviewButton`:
159
+
160
+ ```typescript
161
+ import { WEBVIEWS } from '../../shared/constants'
162
+
163
+ await sendMessage(
164
+ BotServerMessageFactory.createButtonMessage({
165
+ text: 'Need to book a slot?',
166
+ buttons: [BotServerMessageFactory.createWebviewButton({ title: 'Open form', webview: WEBVIEWS.BOOKING_FORM }, session)],
167
+ })
168
+ )
169
+ ```
170
+
171
+ Use the `botonic-action` skill if the user also needs to create the triggering action.
172
+
173
+ ## Options reference
174
+
175
+ | Option | Default | Notes |
176
+ | ----------- | ------- | -------------------------------- |
177
+ | `name` | — | Required. kebab-case recommended |
178
+ | `project` | — | Required. Nx project name |
179
+ | `skipTests` | `false` | Omit `<name>.spec.tsx` |
@@ -0,0 +1,2 @@
1
+ VITE_HUBTYPE_API_URL=https://api.hubtype.com
2
+ VITE_HUBTYPE_CLIENT_ID=UztB1hd4R3Jx3Cl6nklQuNDsjHMxPQ2hjrmFrdTG
@@ -0,0 +1,2 @@
1
+ # Disable Nx TUI (Terminal User Interface) for cleaner CLI output
2
+ NX_TUI=false
@@ -0,0 +1 @@
1
+ # @botonic/* and @lilara/* use the public npm registry by default.
@@ -0,0 +1,174 @@
1
+ # <%= name %>
2
+
3
+ This workspace was generated using the Botonic Nx Plugin.
4
+
5
+ ## Prerequisites
6
+
7
+ ### 1. Backend Setup
8
+ ```bash
9
+ # Clone and setup hubtype-backend
10
+ git clone https://github.com/metis-ai/hubtype-backend
11
+ cd hubtype-backend
12
+ # Follow setup instructions: https://www.notion.so/Laptop-Setup-macOS-0ae7e69f670d40f8b99bf2fb833b59b4
13
+ ```
14
+
15
+ ### 2. Tunnel Configuration
16
+ ```bash
17
+ # Setup ngrok tunnel for local development
18
+ # Replace {YOUR_TUNNEL_SUBDOMAIN} with your actual subdomain
19
+ # Target URL: https://{YOUR_TUNNEL_SUBDOMAIN}-be-dev.metis.ai/
20
+ ```
21
+
22
+ ### 3. AWS SAM CLI Installation
23
+ ```bash
24
+ # Install AWS SAM CLI for local lambda execution
25
+ # Follow: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html
26
+ ```
27
+
28
+ ### 4. Environment Configuration
29
+ Create/update `hubtype-backend/local.env` with:
30
+
31
+ ```bash
32
+ HUBTYPE_HOST=https://{YOUR_TUNNEL_SUBDOMAIN}-be-dev.metis.ai
33
+ EXTRA_ALLOWED={YOUR_TUNNEL_SUBDOMAIN}-be-dev.metis.ai
34
+ HUBTYPE_FRONTEND_HOST=http://localhost:4200
35
+ CORS_ALLOWED_ORIGINS=http://localhost:4200,http://localhost:4201,http://localhost:4202,http://localhost:3001
36
+ # Note: ports 4201, 4202, 3001 are used by botonic v2 serve mode
37
+ ```
38
+
39
+ ## Getting Started
40
+
41
+ ### Backend Commands
42
+ From within `hubtype-backend` directory:
43
+
44
+ ```bash
45
+ # Local development mode - uses local lambda
46
+ make run-botonic-local
47
+
48
+ # Remote mode - uses deployed lambda
49
+ make run-botonic-remote
50
+ ```
51
+
52
+ ### Tunnel Setup
53
+ Start cloudflare tunnel before running the options below:
54
+
55
+ ```bash
56
+ # Replace {YOUR_TOKEN} with your actual Cloudflare tunnel token
57
+ docker run --rm --network host cloudflare/cloudflared:latest tunnel --no-autoupdate run --token {YOUR_TOKEN} --url http://localhost:8000
58
+ ```
59
+
60
+ ### 1. Initialize Bot Monorepo
61
+ ```bash
62
+ # Navigate to your desired directory
63
+ cd /path/to/your/workspace
64
+
65
+ # Generate new bot monorepo (replace {YOUR_BOTS_MONOREPO_NAME})
66
+ npx nx generate @botonic/nx-plugin:preset {YOUR_BOTS_MONOREPO_NAME}
67
+
68
+ # Alternative: Use Nx UI (Cmd+Shift+P → Nx: Generate (UI))
69
+ ```
70
+
71
+ ### 2. Create Bot Application
72
+ ```bash
73
+ # From within the generated monorepo
74
+ cd {YOUR_BOTS_MONOREPO_NAME}
75
+
76
+ # Generate new bot app (replace {BOT_NAME})
77
+ npx nx g @botonic/nx-plugin:bot-app {BOT_NAME}
78
+
79
+ # Alternative: Use Nx UI (Cmd+Shift+P → Nx: Generate (UI))
80
+ ```
81
+
82
+ ### 3. Local Development Setup
83
+ ```bash
84
+ # Step 1: Start backend in local mode
85
+ cd hubtype-backend
86
+ make run-botonic-local
87
+
88
+ # Step 2: Login to local backend (from bot monorepo)
89
+ cd {YOUR_BOTS_MONOREPO_NAME}
90
+ nx run {BOT_NAME}:login-to-hubtype --configuration=local
91
+
92
+ # Step 3: Deploy local runtime (creates bot in database)
93
+ nx run {BOT_NAME}:deploy-local-runtime --configuration=local
94
+
95
+ # Step 4: Start bot in local mode
96
+ nx serve {BOT_NAME} --mode=local
97
+ ```
98
+
99
+ ### 4. Production Deployment
100
+ ```bash
101
+ # Logout from current environment
102
+ nx run {BOT_NAME}:logout-from-hubtype
103
+
104
+ # Login to target environment (dev, dev2, qa, prod)
105
+ nx run {BOT_NAME}:login-to-hubtype --configuration=dev2
106
+
107
+ # Deploy to target environment
108
+ nx run {BOT_NAME}:deploy-to-hubtype --configuration=dev2
109
+ ```
110
+
111
+
112
+ This workspace contains tools and configurations for building Botonic bot applications.
113
+
114
+ ### Available Commands
115
+
116
+ - `nx g @botonic/nx-plugin:bot-app <name>` - Generate a new bot application
117
+ - `nx build <bot-name>` - Build a bot application
118
+ - `nx serve <bot-name>` - Serve a bot application for development
119
+ - `nx test <bot-name>` - Run tests for a bot application
120
+
121
+ ### AI Commands (Cursor)
122
+
123
+ - `/update-bot` — Update a Botonic bot to a newer version using Nx migrations. Type `/update-bot` in Cursor chat; it discovers bots, lets you pick one, and runs migrations one version at a time with git branch management.
124
+
125
+ ### Project Structure
126
+
127
+ ```
128
+ <%= name %>/
129
+ ├── apps/ # Bot applications
130
+ ├── libs/ # Shared libraries
131
+ ├── tools/ # Build tools and scripts
132
+ ├── nx.json # Nx workspace configuration
133
+ ├── package.json # Dependencies and scripts
134
+ └── README.md # This file
135
+ ```
136
+
137
+ ### Creating Your First Bot
138
+
139
+ To create a new bot application:
140
+
141
+ ```bash
142
+ nx g @botonic/nx-plugin:bot-app my-bot
143
+ ```
144
+
145
+ This will create a new bot application in the `apps/my-bot` directory with:
146
+ - Basic bot structure
147
+ - Routes and actions
148
+ - Configuration files
149
+ - Build and development scripts
150
+
151
+ ### Development
152
+
153
+ To start developing your bot:
154
+
155
+ ```bash
156
+ # Serve the bot for development
157
+ nx serve my-bot
158
+
159
+ # Build the bot for production
160
+ nx build my-bot
161
+
162
+ # Run tests
163
+ nx test my-bot
164
+ ```
165
+
166
+ ### Learn More
167
+
168
+ - [Botonic Documentation](https://botonic.io/docs/)
169
+ - [Nx Documentation](https://nx.dev/)
170
+ - [Hubtype Documentation](https://docs.hubtype.com/)
171
+
172
+ ## Support
173
+
174
+ For support and questions, please visit our [GitHub repository](https://github.com/hubtype/botonic).
@@ -0,0 +1,66 @@
1
+ {
2
+ "$schema": "./node_modules/nx/schemas/nx-schema.json",
3
+ "defaultBase": "main",
4
+ "namedInputs": {
5
+ "default": ["{projectRoot}/**/*", "sharedGlobals"],
6
+ "production": [
7
+ "default",
8
+ "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
9
+ "!{projectRoot}/tsconfig.spec.json",
10
+ "!{projectRoot}/jest.config.[jt]s",
11
+ "!{projectRoot}/src/test-setup.[jt]s",
12
+ "!{projectRoot}/test-setup.[jt]s",
13
+ "!{projectRoot}/.eslintrc.json",
14
+ "!{projectRoot}/eslint.config.js"
15
+ ],
16
+ "sharedGlobals": ["{workspaceRoot}/.github/workflows/ci.yml"]
17
+ },
18
+ "generators": {
19
+ "@nx/react": {
20
+ "application": {
21
+ "babel": true,
22
+ "style": "css",
23
+ "linter": "eslint",
24
+ "bundler": "vite",
25
+ "projectNameAndRootFormat": "as-provided"
26
+ },
27
+ "component": {
28
+ "style": "css"
29
+ },
30
+ "library": {
31
+ "style": "css",
32
+ "linter": "eslint",
33
+ "unitTestRunner": "vitest",
34
+ "projectNameAndRootFormat": "as-provided"
35
+ }
36
+ }
37
+ },
38
+ "targetDefaults": {
39
+ "build": {
40
+ "cache": true,
41
+ "dependsOn": ["^build"],
42
+ "inputs": ["production", "^production"]
43
+ },
44
+ "test": {
45
+ "cache": true,
46
+ "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"]
47
+ },
48
+ "lint": {
49
+ "cache": true,
50
+ "inputs": [
51
+ "default",
52
+ "{workspaceRoot}/.eslintrc.json",
53
+ "{workspaceRoot}/.eslintignore",
54
+ "{workspaceRoot}/eslint.config.js"
55
+ ]
56
+ },
57
+ "typecheck": {
58
+ "cache": true,
59
+ "inputs": ["default", "^production"]
60
+ }
61
+ },
62
+ "plugins": ["@botonic/nx-plugin"],
63
+ "tui": {
64
+ "autoExit": 0
65
+ }
66
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "<%= name %>",
3
+ "version": "1.0.0",
4
+ "description": "A Botonic workspace using NPM workspaces and TypeScript project references",
5
+ "private": true,
6
+ "workspaces": [
7
+ "packages/*",
8
+ "apps/*"
9
+ ],
10
+ "scripts": {
11
+ "build": "nx run-many --target=build",
12
+ "test": "nx run-many --target=test",
13
+ "lint": "nx run-many --target=lint",
14
+ "typecheck": "nx run-many --target=typecheck"
15
+ },
16
+ "devDependencies": {
17
+ "@nx/js": "21.0.3",
18
+ "@swc-node/register": "~1.9.1",
19
+ "@swc/core": "~1.5.7",
20
+ "@swc/helpers": "~0.5.11",
21
+ "nx": "21.0.3",
22
+ "prettier": "^2.6.2",
23
+ "tslib": "^2.3.0",
24
+ "typescript": "~5.8.2"
25
+ }
26
+ }