@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,235 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var generator_exports = {};
20
+ __export(generator_exports, {
21
+ default: () => generator_default
22
+ });
23
+ module.exports = __toCommonJS(generator_exports);
24
+ var import_devkit = require("@nx/devkit");
25
+ var import_bot_app_utils = require("../shared/bot-app-utils");
26
+ const MODULE_DIR = __dirname;
27
+ function normalizeOptions(tree, options) {
28
+ const { projectRoot, sourceRoot } = (0, import_bot_app_utils.validateBotAppProject)(
29
+ tree,
30
+ options.project
31
+ );
32
+ const paths = (0, import_bot_app_utils.resolveStandardPaths)(sourceRoot);
33
+ const name = (0, import_devkit.names)(options.name);
34
+ const fileName = name.fileName;
35
+ const className = name.className;
36
+ const propertyName = name.propertyName;
37
+ const constantName = name.constantName.toUpperCase();
38
+ const customMessagesPath = options.directory ? `${sourceRoot}/client/custom-messages/${options.directory}` : `${sourceRoot}/client/custom-messages`;
39
+ return {
40
+ ...options,
41
+ projectName: options.project,
42
+ projectRoot,
43
+ fileName,
44
+ className,
45
+ propertyName,
46
+ constantName,
47
+ customMessagesPath,
48
+ constantsPath: paths.constantsPath,
49
+ actionsPath: paths.actionsPath,
50
+ routesPath: paths.routesPath
51
+ };
52
+ }
53
+ function addFilesToProject(tree, options) {
54
+ const name = (0, import_devkit.names)(options.name);
55
+ const templateOptions = {
56
+ ...options,
57
+ ...name,
58
+ name: name.fileName
59
+ // __name__ in filenames must be kebab-case
60
+ };
61
+ (0, import_devkit.generateFiles)(
62
+ tree,
63
+ (0, import_devkit.joinPathFragments)(MODULE_DIR, "files"),
64
+ options.customMessagesPath,
65
+ templateOptions
66
+ );
67
+ if (options.skipTests) {
68
+ const testFilePath = `${options.customMessagesPath}/${options.fileName}.spec.tsx`;
69
+ if (tree.exists(testFilePath)) {
70
+ tree.delete(testFilePath);
71
+ }
72
+ }
73
+ if (options.skipAction) {
74
+ const actionPath = `${options.customMessagesPath}/${options.fileName}-output.ts`;
75
+ if (tree.exists(actionPath)) {
76
+ tree.delete(actionPath);
77
+ }
78
+ } else {
79
+ const sourceActionPath = `${options.customMessagesPath}/${options.fileName}-output.ts`;
80
+ const targetActionPath = `${options.actionsPath}/${options.fileName}.ts`;
81
+ if (tree.exists(sourceActionPath)) {
82
+ const content = tree.read(sourceActionPath, "utf-8");
83
+ tree.write(targetActionPath, content);
84
+ tree.delete(sourceActionPath);
85
+ }
86
+ }
87
+ }
88
+ function updateCustomMessagesIndex(tree, options) {
89
+ const indexPath = `${options.customMessagesPath}/index.ts`;
90
+ if (!tree.exists(indexPath)) {
91
+ tree.write(
92
+ indexPath,
93
+ `import { CUSTOM_MESSAGE_TYPES } from '../../shared/constants'
94
+
95
+ export const customMessages = {
96
+ [CUSTOM_MESSAGE_TYPES.${options.constantName}]: ${options.className},
97
+ }
98
+ `
99
+ );
100
+ return;
101
+ }
102
+ const content = tree.read(indexPath, "utf-8");
103
+ const lines = content.split("\n");
104
+ const importStatement = `import { ${options.className} } from './${options.directory ? options.directory + "/" : ""}${options.fileName}'`;
105
+ let importInsertIndex = 0;
106
+ for (let i = 0; i < lines.length; i++) {
107
+ if (lines[i].startsWith("import")) {
108
+ importInsertIndex = i + 1;
109
+ } else if (lines[i].trim() === "" && importInsertIndex > 0) {
110
+ break;
111
+ }
112
+ }
113
+ lines.splice(importInsertIndex, 0, importStatement);
114
+ const exportLineIndex = lines.findIndex(
115
+ (line) => line.includes("export const customMessages")
116
+ );
117
+ if (exportLineIndex !== -1) {
118
+ let braceIndex = exportLineIndex;
119
+ while (braceIndex < lines.length && !lines[braceIndex].includes("{")) {
120
+ braceIndex++;
121
+ }
122
+ if (braceIndex < lines.length) {
123
+ const braceLine = lines[braceIndex];
124
+ if (braceLine.trim() === "{" || braceLine.includes("{}")) {
125
+ const newEntry = ` [CUSTOM_MESSAGE_TYPES.${options.constantName}]: ${options.className},`;
126
+ if (braceLine.includes("{}")) {
127
+ lines[braceIndex] = braceLine.replace("{}", `{
128
+ ${newEntry}
129
+ }`);
130
+ } else {
131
+ lines.splice(braceIndex + 1, 0, newEntry);
132
+ }
133
+ } else {
134
+ let closingBraceIndex = braceIndex;
135
+ let braceCount = 0;
136
+ for (let i = braceIndex; i < lines.length; i++) {
137
+ for (const char of lines[i]) {
138
+ if (char === "{") braceCount++;
139
+ if (char === "}") braceCount--;
140
+ }
141
+ if (braceCount === 0) {
142
+ closingBraceIndex = i;
143
+ break;
144
+ }
145
+ }
146
+ const newEntry = ` [CUSTOM_MESSAGE_TYPES.${options.constantName}]: ${options.className},`;
147
+ lines.splice(closingBraceIndex, 0, newEntry);
148
+ }
149
+ }
150
+ }
151
+ tree.write(indexPath, lines.join("\n"));
152
+ }
153
+ function updateConstantsFile(tree, options) {
154
+ if (!tree.exists(options.constantsPath)) {
155
+ tree.write(
156
+ options.constantsPath,
157
+ `export const CUSTOM_MESSAGE_TYPES = {
158
+ ${options.constantName}: '${options.fileName}',
159
+ } as const
160
+
161
+ export const WEBVIEWS = {
162
+ // Add your webviews here
163
+ } as const
164
+
165
+ export const PAYLOADS = {
166
+ // Add your payloads here
167
+ } as const
168
+ `
169
+ );
170
+ return;
171
+ }
172
+ (0, import_bot_app_utils.addConstEntry)(
173
+ tree,
174
+ options.constantsPath,
175
+ "CUSTOM_MESSAGE_TYPES",
176
+ options.constantName,
177
+ options.fileName
178
+ );
179
+ }
180
+ function updateActionsIndex(tree, options) {
181
+ if (options.skipAction) {
182
+ return;
183
+ }
184
+ (0, import_bot_app_utils.addExportToIndex)(
185
+ tree,
186
+ `${options.actionsPath}/index.ts`,
187
+ `export * from './${options.fileName}'`
188
+ );
189
+ }
190
+ function updateRoutes(tree, options) {
191
+ if (options.skipAction) {
192
+ return;
193
+ }
194
+ const actionImport = `${options.propertyName}Output`;
195
+ (0, import_bot_app_utils.addActionImportToRoutes)(tree, options.routesPath, actionImport);
196
+ (0, import_bot_app_utils.insertRouteBeforeFlowBuilder)(tree, options.routesPath, [
197
+ ` {`,
198
+ ` text: '${options.fileName}',`,
199
+ ` action: async () => await ${actionImport}(botContext),`,
200
+ ` },`
201
+ ]);
202
+ }
203
+ async function generator_default(tree, options) {
204
+ console.log(`Creating custom message: ${options.name}`);
205
+ const normalizedOptions = normalizeOptions(tree, options);
206
+ console.log(`Project: ${normalizedOptions.projectName}`);
207
+ console.log(`Location: ${normalizedOptions.customMessagesPath}`);
208
+ addFilesToProject(tree, normalizedOptions);
209
+ updateConstantsFile(tree, normalizedOptions);
210
+ updateCustomMessagesIndex(tree, normalizedOptions);
211
+ updateActionsIndex(tree, normalizedOptions);
212
+ updateRoutes(tree, normalizedOptions);
213
+ await (0, import_devkit.formatFiles)(tree);
214
+ console.log(
215
+ `
216
+ Successfully created custom message: ${normalizedOptions.className}`
217
+ );
218
+ console.log(`Next steps:`);
219
+ console.log(
220
+ ` 1. Customize the component in: ${normalizedOptions.customMessagesPath}/${normalizedOptions.fileName}.tsx`
221
+ );
222
+ if (!options.skipAction) {
223
+ console.log(
224
+ ` 2. Update the action in: ${normalizedOptions.actionsPath}/${normalizedOptions.fileName}.ts`
225
+ );
226
+ console.log(
227
+ ` 3. Test by typing '${normalizedOptions.fileName}' in the webchat`
228
+ );
229
+ }
230
+ if (!options.skipTests) {
231
+ console.log(
232
+ ` 4. Update the tests in: ${normalizedOptions.customMessagesPath}/${normalizedOptions.fileName}.spec.tsx`
233
+ );
234
+ }
235
+ }
@@ -0,0 +1,7 @@
1
+ export interface CustomMessageGeneratorSchema {
2
+ name: string;
3
+ project: string;
4
+ directory?: string;
5
+ skipTests?: boolean;
6
+ skipAction?: boolean;
7
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var schema_exports = {};
16
+ module.exports = __toCommonJS(schema_exports);
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "cli": "nx",
4
+ "$id": "CustomMessage",
5
+ "title": "Create a custom message component for Botonic",
6
+ "description": "Generate a custom message component with automatic registration in the bot application.",
7
+ "type": "object",
8
+ "properties": {
9
+ "name": {
10
+ "type": "string",
11
+ "description": "The name of the custom message component.",
12
+ "$default": {
13
+ "$source": "argv",
14
+ "index": 0
15
+ },
16
+ "x-prompt": "What name would you like to use for the custom message?",
17
+ "pattern": "^[a-zA-Z].*$"
18
+ },
19
+ "project": {
20
+ "type": "string",
21
+ "description": "The name of the botonic project to add the custom message to.",
22
+ "x-prompt": "Which botonic project should this custom message be added to?",
23
+ "$default": {
24
+ "$source": "projectName"
25
+ }
26
+ },
27
+ "directory": {
28
+ "type": "string",
29
+ "description": "The directory within custom-messages to create the component (optional).",
30
+ "alias": "dir"
31
+ },
32
+ "skipTests": {
33
+ "type": "boolean",
34
+ "description": "Skip generating test files.",
35
+ "default": false
36
+ },
37
+ "skipAction": {
38
+ "type": "boolean",
39
+ "description": "Skip generating an example action that uses this custom message.",
40
+ "default": false
41
+ }
42
+ },
43
+ "required": ["name", "project"]
44
+ }
@@ -0,0 +1,5 @@
1
+ # Update Botonic Bot
2
+
3
+ Applies pending bot-app migrations from `.botonic/pending-bot-migrations.json` to a single bot.
4
+
5
+ Follow the full workflow in: `node_modules/@botonic/nx-plugin/src/cursor-commands/update-bot.md`
@@ -0,0 +1,5 @@
1
+ # Update Botonic Workspace
2
+
3
+ Updates the Botonic plugin to the latest version and queues pending bot-app migrations.
4
+
5
+ Follow the full workflow in: `node_modules/@botonic/nx-plugin/src/cursor-commands/update-botonic.md`
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ # Discovers botonic bot apps and their current versions.
3
+ # Uses nx show projects with tag:botonic:bot-app filter.
4
+ # Run from workspace root. Output: JSON array of { name, path, version }.
5
+ # Usage: bash .cursor/scripts/update-bot/discover-bots.sh
6
+
7
+ set -euo pipefail
8
+
9
+ # Ensure we're in workspace root (contains apps/ or nx.json)
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ GIT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
12
+ REL_ROOT="$(cd "$SCRIPT_DIR/../../../" && pwd)"
13
+ if [[ -n "$GIT_ROOT" ]] && [[ -f "$GIT_ROOT/nx.json" ]]; then
14
+ ROOT="$GIT_ROOT"
15
+ elif [[ -d "$REL_ROOT/apps" ]] || [[ -f "$REL_ROOT/nx.json" ]]; then
16
+ ROOT="$REL_ROOT"
17
+ else
18
+ ROOT="${GIT_ROOT:-.}"
19
+ fi
20
+ cd "$ROOT"
21
+
22
+ # List bot apps via nx (tag:botonic:bot-app)
23
+ # Nx may print plugin messages to stdout; use tail -1 to get JSON only
24
+ names=$(npx nx show projects --projects "tag:botonic:bot-app" --json 2>/dev/null | tail -1 || echo "[]")
25
+ if [[ "$names" == "[]" ]] || [[ -z "$names" ]]; then
26
+ echo "[]"
27
+ exit 0
28
+ fi
29
+
30
+ bots='[]'
31
+ for name in $(echo "$names" | node -e "
32
+ try {
33
+ const arr = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8'));
34
+ (Array.isArray(arr) ? arr : []).forEach(n => console.log(n));
35
+ } catch {}
36
+ "); do
37
+ [[ -z "$name" ]] && continue
38
+
39
+ path="$name"
40
+ if root=$(npx nx show project "$name" --json 2>/dev/null | tail -1 | node -e "try { const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); process.stdout.write(d.root||''); } catch {}"); then
41
+ [[ -n "$root" ]] && path="$root"
42
+ else
43
+ path="apps/$name"
44
+ fi
45
+
46
+ version="unknown"
47
+ pkg="$ROOT/$path/package.json"
48
+ if [[ -f "$pkg" ]]; then
49
+ version=$(node -e "
50
+ try {
51
+ const p = require('$pkg');
52
+ const v = p.dependencies?.['@botonic/core']
53
+ || p.dependencies?.['@botonic/webchat-react']
54
+ || 'unknown';
55
+ console.log(String(v).replace(/^[\^~]/, ''));
56
+ } catch { console.log('unknown'); }
57
+ " 2>/dev/null || echo "unknown")
58
+ fi
59
+
60
+ bots=$(echo "$bots" | node -e "
61
+ const b = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8'));
62
+ b.push({ name: process.argv[1], path: process.argv[2], version: process.argv[3] });
63
+ console.log(JSON.stringify(b, null, 2));
64
+ " "$name" "$path" "$version")
65
+ done
66
+
67
+ echo "$bots"
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+ # Finds <name>.migration.md files for migrations between two versions.
3
+ # Reads node_modules/@botonic/nx-plugin/migrations.json (published, real semver).
4
+ # Usage: find-migration-guides.sh <from-version> <to-version>
5
+ # Output: JSON array of { version, migrations: [{ name, migrationGuide }] } grouped by unique version
6
+
7
+ set -euo pipefail
8
+
9
+ FROM_VERSION="${1:?Usage: find-migration-guides.sh <from> <to>}"
10
+ TO_VERSION="${2:?Usage: find-migration-guides.sh <from> <to>}"
11
+
12
+ # Run from workspace root
13
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ GIT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
15
+ REL_ROOT="$(cd "$SCRIPT_DIR/../../../" && pwd)"
16
+ if [[ -n "$GIT_ROOT" ]] && [[ -f "$GIT_ROOT/nx.json" ]]; then
17
+ ROOT="$GIT_ROOT"
18
+ elif [[ -f "$REL_ROOT/nx.json" ]] || [[ -f "$REL_ROOT/node_modules/@botonic/nx-plugin/migrations.json" ]]; then
19
+ ROOT="$REL_ROOT"
20
+ else
21
+ ROOT="${GIT_ROOT:-$REL_ROOT}"
22
+ fi
23
+ cd "$ROOT"
24
+
25
+ MIGRATIONS_JSON="node_modules/@botonic/nx-plugin/migrations.json"
26
+ MIGRATIONS_DIR="node_modules/@botonic/nx-plugin/src/migrations"
27
+
28
+ if [ ! -f "$MIGRATIONS_JSON" ]; then
29
+ echo '{"error": "migrations.json not found. Run pnpm install first."}' >&2
30
+ exit 1
31
+ fi
32
+
33
+ node -e "
34
+ const fs = require('fs');
35
+ const path = require('path');
36
+ let semver;
37
+ try { semver = require('semver'); } catch { semver = null; }
38
+
39
+ const mj = JSON.parse(fs.readFileSync('$MIGRATIONS_JSON', 'utf8'));
40
+ const generators = mj.generators || {};
41
+ const from = '$FROM_VERSION';
42
+ const to = '$TO_VERSION';
43
+ const migrationsDir = path.resolve('$MIGRATIONS_DIR');
44
+
45
+ const valid = (v) => semver ? semver.valid(v) : /^\\d+\\.\\d+\\.\\d+$/.test(v);
46
+ const gt = (a, b) => semver ? semver.gt(a, b) : a !== b;
47
+ const lte = (a, b) => semver ? semver.lte(a, b) : true;
48
+ const compare = (a, b) => semver ? semver.compare(a, b) : 0;
49
+
50
+ const entries = Object.entries(generators)
51
+ .filter(([, cfg]) => valid(cfg.version) && gt(cfg.version, from) && lte(cfg.version, to))
52
+ .sort(([, a], [, b]) => compare(a.version, b.version));
53
+
54
+ const byVersion = new Map();
55
+ for (const [name, cfg] of entries) {
56
+ const guideFile = path.join(migrationsDir, name, \`\${name}.migration.md\`);
57
+ const migrationGuide = fs.existsSync(guideFile) ? guideFile : null;
58
+ if (!byVersion.has(cfg.version)) {
59
+ byVersion.set(cfg.version, []);
60
+ }
61
+ byVersion.get(cfg.version).push({ name, migrationGuide });
62
+ }
63
+
64
+ const results = Array.from(byVersion.entries()).map(([version, migrations]) => ({
65
+ version,
66
+ migrations
67
+ }));
68
+
69
+ console.log(JSON.stringify(results, null, 2));
70
+ "
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: botonic-action
3
+ description: Scaffold and implement a Botonic bot action using the Nx generator, then guide implementation. Use when the user says "create action", "add action", "new action", "generate action", or wants to handle a specific message, intent, or conversation route in a Botonic bot project.
4
+ ---
5
+
6
+ # Botonic Action
7
+
8
+ Scaffold a server-side action, wire its route, and help implement its logic.
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. `order-status`, `greet-user`
17
+ - **`project`** (required): Nx project name of the bot app, e.g. `my-bot`
18
+ - **`routeMatcher`** (required unless skipping route): the text pattern that triggers this action. Ask the user explicitly — e.g. _"What message or phrase should trigger this action?"_. Defaults to the kebab-case name if omitted.
19
+ - **`skipRoute`** (optional): `true` only if the action should not be wired into `routes.ts` (e.g. internal helper, called by another action)
20
+ - **`skipTests`** (optional): `true` to skip spec file generation
21
+
22
+ ### Step 2 — Run the generator
23
+
24
+ ```bash
25
+ pnpm nx g @botonic/nx-plugin:action --name=<name> --project=<project>
26
+ ```
27
+
28
+ With route matcher:
29
+
30
+ ```bash
31
+ pnpm nx g @botonic/nx-plugin:action order-status --project my-bot --routeMatcher "order status"
32
+ ```
33
+
34
+ Without route wiring (internal helpers):
35
+
36
+ ```bash
37
+ pnpm nx g @botonic/nx-plugin:action send-confirmation --project my-bot --skipRoute
38
+ ```
39
+
40
+ ### Step 3 — Review the generated route
41
+
42
+ Open `src/server/bot/routes.ts` and show the user the entry that was inserted:
43
+
44
+ ```typescript
45
+ {
46
+ text: 'order-status',
47
+ action: async () => await OrderStatus(botContext),
48
+ },
49
+ ```
50
+
51
+ Confirm with the user:
52
+
53
+ - Is the `text` matcher correct? Does it match what the bot will receive?
54
+ - Should it be a regex? e.g. `/^order.*/i`
55
+ - Should it match on `payload` or `type` instead of `text`?
56
+
57
+ If the matcher needs changing, edit `routes.ts` directly:
58
+
59
+ ```typescript
60
+ // Regex matcher
61
+ { text: /^order.*/i, action: async () => await OrderStatus(botContext) },
62
+
63
+ // Payload matcher
64
+ { payload: 'ORDER_STATUS', action: async () => await OrderStatus(botContext) },
65
+
66
+ // Message type matcher
67
+ { type: 'postback', payload: 'ORDER_STATUS', action: async () => await OrderStatus(botContext) },
68
+ ```
69
+
70
+ ### Step 4 — Ask what to implement
71
+
72
+ If not already clear from context, ask: "What should this action do?"
73
+
74
+ ### Step 5 — Implement the action
75
+
76
+ #### Building blocks: `sendMessage` and `BotServerMessageFactory`
77
+
78
+ Every action receives `BotContext` as its argument. The two key pieces for sending responses are:
79
+
80
+ - **`sendMessage`** — async function from `BotContext`. Call it once per message you want to send. You can call it multiple times to send a sequence of messages.
81
+ - **`BotServerMessageFactory`** — imported from `@botonic/shared`. Use its factory methods to build every message; never construct raw message objects by hand.
82
+
83
+ ```typescript
84
+ import { BotContext, BotServerMessageFactory } from '@botonic/shared'
85
+
86
+ export async function MyAction({ sendMessage }: BotContext) {
87
+ await sendMessage(BotServerMessageFactory.createText({ text: 'Hello!' }))
88
+ return { status: 200, response: 'OK' }
89
+ }
90
+ ```
91
+
92
+ #### Expected return value
93
+
94
+ Every action **must** return `{ status: number, response: string }`. Use `200 / 'OK'` for success and a meaningful status code + message for errors.
95
+
96
+ ```typescript
97
+ return { status: 200, response: 'OK' } // success
98
+ return { status: 400, response: 'Bad Request' } // validation error
99
+ ```
100
+
101
+ #### Factory methods
102
+
103
+ **Text message**
104
+
105
+ ```typescript
106
+ await sendMessage(BotServerMessageFactory.createText({ text: 'Your order is on the way!' }))
107
+ ```
108
+
109
+ **Button message** — build each button with the appropriate factory method; never pass raw button objects
110
+
111
+ ```typescript
112
+ await sendMessage(
113
+ BotServerMessageFactory.createButtonMessage({
114
+ text: 'Need help with your order?',
115
+ buttons: [BotServerMessageFactory.createPostbackButton({ title: 'Order status', payload: 'ORDER_STATUS' }), BotServerMessageFactory.createUrlButton({ title: 'Visit website', url: 'https://example.com' }), BotServerMessageFactory.createWebviewButton({ title: 'Open form', webview: WEBVIEWS.ORDER_FORM }, session)],
116
+ })
117
+ )
118
+ ```
119
+
120
+ **Custom message**
121
+
122
+ ```typescript
123
+ await sendMessage(
124
+ BotServerMessageFactory.createCustom({
125
+ name: CUSTOM_MESSAGE_TYPES.ORDER_CARD,
126
+ props: { orderId, status },
127
+ })
128
+ )
129
+ ```
130
+
131
+ **Conditional logic with early return**
132
+
133
+ ```typescript
134
+ export async function OrderStatus({ sendMessage, request }: BotContext) {
135
+ const { payload } = request
136
+ if (!payload?.orderId) {
137
+ await sendMessage(BotServerMessageFactory.createText({ text: 'Please provide an order ID.' }))
138
+ return { status: 400, response: 'Bad Request' }
139
+ }
140
+ // fetch order and respond...
141
+ return { status: 200, response: 'OK' }
142
+ }
143
+ ```
144
+
145
+ **Calling an external API**
146
+
147
+ Keep HTTP calls in a dedicated service file; import and call it from the action:
148
+
149
+ ```typescript
150
+ import { orderService } from '../../services/order-service'
151
+
152
+ export async function OrderStatus({ sendMessage, request }: BotContext) {
153
+ const order = await orderService.getOrder(request.payload?.orderId)
154
+ await sendMessage(BotServerMessageFactory.createText({ text: order.statusMessage }))
155
+ return { status: 200, response: 'OK' }
156
+ }
157
+ ```
158
+
159
+ ## Options reference
160
+
161
+ | Option | Default | Notes |
162
+ | -------------- | --------------- | -------------------------------- |
163
+ | `name` | — | Required. kebab-case recommended |
164
+ | `project` | — | Required. Nx project name |
165
+ | `routeMatcher` | kebab-case name | The `text` field in `routes.ts` |
166
+ | `skipRoute` | `false` | Omit route entry in `routes.ts` |
167
+ | `skipTests` | `false` | Omit `<name>.spec.ts` |