@promptbook/cli 0.104.0-1 → 0.104.0-11

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 (229) hide show
  1. package/apps/agents-server/config.ts +1 -3
  2. package/apps/agents-server/next.config.ts +2 -2
  3. package/apps/agents-server/package.json +7 -3
  4. package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
  5. package/apps/agents-server/public/swagger.json +115 -0
  6. package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
  7. package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
  8. package/apps/agents-server/src/app/AddAgentButton.tsx +47 -21
  9. package/apps/agents-server/src/app/actions.ts +22 -5
  10. package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +211 -0
  11. package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
  12. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
  13. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
  14. package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +263 -0
  15. package/apps/agents-server/src/app/admin/files/actions.ts +61 -0
  16. package/apps/agents-server/src/app/admin/files/page.tsx +13 -0
  17. package/apps/agents-server/src/app/admin/image-generator-test/ImageGeneratorTestClient.tsx +169 -0
  18. package/apps/agents-server/src/app/admin/image-generator-test/page.tsx +13 -0
  19. package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +256 -0
  20. package/apps/agents-server/src/app/admin/images/actions.ts +60 -0
  21. package/apps/agents-server/src/app/admin/images/page.tsx +13 -0
  22. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +294 -0
  23. package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
  24. package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
  25. package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
  26. package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
  27. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
  28. package/apps/agents-server/src/app/admin/search-engine-test/SearchEngineTestClient.tsx +109 -0
  29. package/apps/agents-server/src/app/admin/search-engine-test/actions.ts +17 -0
  30. package/apps/agents-server/src/app/admin/search-engine-test/page.tsx +13 -0
  31. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
  32. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
  33. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
  34. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
  35. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
  36. package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
  37. package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
  38. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
  39. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
  40. package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
  41. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
  42. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
  43. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
  44. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
  45. package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
  46. package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
  47. package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
  48. package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
  49. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
  50. package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
  51. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
  52. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
  53. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
  54. package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
  55. package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
  56. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
  57. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
  58. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
  59. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
  60. package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
  61. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
  62. package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
  63. package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
  64. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
  65. package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
  66. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
  67. package/apps/agents-server/src/app/api/agents/route.ts +28 -3
  68. package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
  69. package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
  70. package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
  71. package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
  72. package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
  73. package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
  74. package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
  75. package/apps/agents-server/src/app/api/images/[filename]/route.ts +128 -0
  76. package/apps/agents-server/src/app/api/messages/route.ts +102 -0
  77. package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
  78. package/apps/agents-server/src/app/api/upload/route.ts +128 -45
  79. package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
  80. package/apps/agents-server/src/app/docs/page.tsx +12 -12
  81. package/apps/agents-server/src/app/globals.css +140 -33
  82. package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
  83. package/apps/agents-server/src/app/layout.tsx +27 -22
  84. package/apps/agents-server/src/app/page.tsx +54 -6
  85. package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
  86. package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
  87. package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
  88. package/apps/agents-server/src/app/security.txt/route.ts +1 -1
  89. package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
  90. package/apps/agents-server/src/app/swagger/page.tsx +14 -0
  91. package/apps/agents-server/src/components/AgentProfile/AgentCapabilityChips.tsx +38 -0
  92. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +44 -116
  93. package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
  94. package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
  95. package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
  96. package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
  97. package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
  98. package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
  99. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
  100. package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
  101. package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
  102. package/apps/agents-server/src/components/Header/Header.tsx +130 -40
  103. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +150 -23
  104. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
  105. package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
  106. package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
  107. package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
  108. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
  109. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
  110. package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
  111. package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
  112. package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
  113. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
  114. package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
  115. package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
  116. package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
  117. package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
  118. package/apps/agents-server/src/database/$getTableName.ts +1 -0
  119. package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
  120. package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
  121. package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
  122. package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
  123. package/apps/agents-server/src/database/migrate.ts +34 -1
  124. package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
  125. package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
  126. package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
  127. package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
  128. package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
  129. package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
  130. package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
  131. package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
  132. package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
  133. package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
  134. package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
  135. package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
  136. package/apps/agents-server/src/database/migrations/2025-12-0830-image-purpose.sql +5 -0
  137. package/apps/agents-server/src/database/migrations/2025-12-0890-file-agent-id.sql +5 -0
  138. package/apps/agents-server/src/database/schema.ts +244 -4
  139. package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
  140. package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
  141. package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
  142. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
  143. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
  144. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
  145. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
  146. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
  147. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
  148. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
  149. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
  150. package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
  151. package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
  152. package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
  153. package/apps/agents-server/src/message-providers/index.ts +13 -0
  154. package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
  155. package/apps/agents-server/src/middleware.ts +19 -23
  156. package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
  157. package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
  158. package/apps/agents-server/src/utils/auth.ts +117 -17
  159. package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
  160. package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
  161. package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
  162. package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
  163. package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
  164. package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
  165. package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
  166. package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
  167. package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
  168. package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
  169. package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
  170. package/esm/index.es.js +1534 -1330
  171. package/esm/index.es.js.map +1 -1
  172. package/esm/typings/servers.d.ts +8 -0
  173. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  174. package/esm/typings/src/_packages/types.index.d.ts +16 -2
  175. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +29 -1
  176. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
  177. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
  178. package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
  179. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
  180. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  181. package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
  182. package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  183. package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +9 -13
  184. package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +3 -3
  185. package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +1 -1
  186. package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
  187. package/esm/typings/src/book-components/icons/AboutIcon.d.ts +1 -1
  188. package/esm/typings/src/book-components/icons/AttachmentIcon.d.ts +1 -1
  189. package/esm/typings/src/book-components/icons/CameraIcon.d.ts +1 -1
  190. package/esm/typings/src/book-components/icons/DownloadIcon.d.ts +1 -1
  191. package/esm/typings/src/book-components/icons/MenuIcon.d.ts +1 -1
  192. package/esm/typings/src/book-components/icons/SaveIcon.d.ts +1 -1
  193. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -12
  194. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +27 -15
  195. package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
  196. package/esm/typings/src/commitments/index.d.ts +2 -1
  197. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/countUsage.d.ts +1 -1
  198. package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
  199. package/esm/typings/src/llm-providers/agent/Agent.d.ts +6 -1
  200. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
  201. package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
  202. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
  203. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
  204. package/esm/typings/src/remote-server/ui/ServerApp.d.ts +1 -1
  205. package/esm/typings/src/search-engines/SearchEngine.d.ts +9 -0
  206. package/esm/typings/src/search-engines/SearchResult.d.ts +18 -0
  207. package/esm/typings/src/search-engines/bing/BingSearchEngine.d.ts +15 -0
  208. package/esm/typings/src/search-engines/dummy/DummySearchEngine.d.ts +15 -0
  209. package/esm/typings/src/types/Message.d.ts +49 -0
  210. package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
  211. package/esm/typings/src/types/typeAliases.d.ts +23 -1
  212. package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
  213. package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
  214. package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
  215. package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
  216. package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
  217. package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
  218. package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
  219. package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
  220. package/esm/typings/src/utils/random/$randomAgentPersona.d.ts +3 -2
  221. package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
  222. package/esm/typings/src/version.d.ts +1 -1
  223. package/package.json +1 -1
  224. package/umd/index.umd.js +1542 -1338
  225. package/umd/index.umd.js.map +1 -1
  226. package/apps/agents-server/package-lock.json +0 -27
  227. package/apps/agents-server/public/fonts/download-font.js +0 -22
  228. package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
  229. package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
@@ -0,0 +1,117 @@
1
+ import { describe, expect, it } from '@jest/globals';
2
+ import { parseEmailAddresses } from './parseEmailAddresses';
3
+
4
+ describe('how parseEmailAddresses works', () => {
5
+ it('should work with single email', () => {
6
+ expect(parseEmailAddresses('pavol@webgpt.cz')).toEqual([
7
+ {
8
+ fullName: null,
9
+ baseEmail: 'pavol@webgpt.cz',
10
+ fullEmail: 'pavol@webgpt.cz',
11
+ plus: [],
12
+ },
13
+ ]);
14
+ });
15
+
16
+ it('should work with simple emails', () => {
17
+ expect(parseEmailAddresses('pavol@webgpt.cz, jirka@webgpt.cz, tomas@webgpt.cz')).toEqual([
18
+ {
19
+ fullName: null,
20
+ baseEmail: 'pavol@webgpt.cz',
21
+ fullEmail: 'pavol@webgpt.cz',
22
+ plus: [],
23
+ },
24
+ {
25
+ fullName: null,
26
+ baseEmail: 'jirka@webgpt.cz',
27
+ fullEmail: 'jirka@webgpt.cz',
28
+ plus: [],
29
+ },
30
+ {
31
+ fullName: null,
32
+ baseEmail: 'tomas@webgpt.cz',
33
+ fullEmail: 'tomas@webgpt.cz',
34
+ plus: [],
35
+ },
36
+ ]);
37
+ });
38
+
39
+ it('should work with fullname', () => {
40
+ expect(
41
+ parseEmailAddresses(
42
+ 'Pavol Hejný <pavol@webgpt.cz>, Jirka <jirka@webgpt.cz>, "Tomáš Studeník" <tomas@webgpt.cz>',
43
+ ),
44
+ ).toEqual([
45
+ {
46
+ fullName: 'Pavol Hejný',
47
+ baseEmail: 'pavol@webgpt.cz',
48
+ fullEmail: 'pavol@webgpt.cz',
49
+ plus: [],
50
+ },
51
+ {
52
+ fullName: 'Jirka',
53
+ baseEmail: 'jirka@webgpt.cz',
54
+ fullEmail: 'jirka@webgpt.cz',
55
+ plus: [],
56
+ },
57
+ {
58
+ fullName: 'Tomáš Studeník',
59
+ baseEmail: 'tomas@webgpt.cz',
60
+ fullEmail: 'tomas@webgpt.cz',
61
+ plus: [],
62
+ },
63
+ ]);
64
+ });
65
+
66
+ it('not confused by comma', () => {
67
+ expect(parseEmailAddresses(', pavol@webgpt.cz, ')).toEqual([
68
+ {
69
+ fullName: null,
70
+ fullEmail: 'pavol@webgpt.cz',
71
+ baseEmail: 'pavol@webgpt.cz',
72
+ plus: [],
73
+ },
74
+ ]);
75
+ });
76
+
77
+ it('works on real-life example', () => {
78
+ expect(
79
+ parseEmailAddresses(
80
+ '"bob" <bob@bot.webgpt.cz>, "pavolto" <pavol+to@ptbk.io>, "Pavol" <pavol@collboard.com>',
81
+ ),
82
+ ).toEqual([
83
+ {
84
+ fullName: 'bob',
85
+ fullEmail: 'bob@bot.webgpt.cz',
86
+ baseEmail: 'bob@bot.webgpt.cz',
87
+ plus: [],
88
+ },
89
+ {
90
+ fullName: 'pavolto',
91
+ fullEmail: 'pavol+to@ptbk.io',
92
+ baseEmail: 'pavol@ptbk.io',
93
+ plus: ['to'],
94
+ },
95
+ {
96
+ fullName: 'Pavol',
97
+ fullEmail: 'pavol@collboard.com',
98
+ baseEmail: 'pavol@collboard.com',
99
+ plus: [],
100
+ },
101
+ ]);
102
+ });
103
+
104
+ it('throws on invalid email adresses', () => {
105
+ expect(() => parseEmailAddresses('Pavol, Hejný')).toThrowError(/Invalid email address/);
106
+ expect(() => parseEmailAddresses('Pavol Hejný <>')).toThrowError(/Invalid email address/);
107
+ expect(() => parseEmailAddresses('Pavol Hejný, <@webgpt.cz>')).toThrowError(/Invalid email address/);
108
+ expect(() => parseEmailAddresses('Pavol Hejný <webgpt.cz>')).toThrowError(/Invalid email address/);
109
+ expect(() => parseEmailAddresses('Pavol Hejný <pavol@>')).toThrowError(/Invalid email address/);
110
+ expect(() => parseEmailAddresses('Pavol Hejný <a@b>,')).toThrowError(/Invalid email address/);
111
+ });
112
+ });
113
+
114
+
115
+ /**
116
+ * TODO: [🐫] This test fails because of aliased imports `import type { string_emails } from '@promptbook-local/types';`, fix it
117
+ */
@@ -0,0 +1,19 @@
1
+ import type { string_emails } from '@promptbook-local/types';
2
+ import { spaceTrim } from 'spacetrim';
3
+ import type { EmailAddress } from '../Email';
4
+ import { parseEmailAddress } from './parseEmailAddress';
5
+
6
+ /**
7
+ * Parses the email addresses into its components
8
+ */
9
+ export function parseEmailAddresses(value: string_emails): Array<EmailAddress> {
10
+ const emailAddresses = value
11
+ .split(',')
12
+ .map((email) => spaceTrim(email))
13
+ .filter((email) => email !== '')
14
+ .map((email) => parseEmailAddress(email));
15
+
16
+ // console.log('parseEmailAddresses', value, '->', emailAddresses);
17
+
18
+ return emailAddresses;
19
+ }
@@ -0,0 +1,119 @@
1
+ import { describe, expect, it } from '@jest/globals';
2
+ import { stringifyEmailAddress } from './stringifyEmailAddress';
3
+
4
+ describe('how stringifyEmailAddress works', () => {
5
+ it('should work with simple email', () => {
6
+ expect(
7
+ stringifyEmailAddress({
8
+ fullName: null,
9
+ baseEmail: 'pavol@webgpt.cz',
10
+ fullEmail: 'pavol@webgpt.cz',
11
+ plus: [],
12
+ }),
13
+ ).toBe('pavol@webgpt.cz');
14
+ expect(
15
+ stringifyEmailAddress({
16
+ fullName: null,
17
+ baseEmail: 'jirka@webgpt.cz',
18
+ fullEmail: 'jirka@webgpt.cz',
19
+ plus: [],
20
+ }),
21
+ ).toBe('jirka@webgpt.cz');
22
+ expect(
23
+ stringifyEmailAddress({
24
+ fullName: null,
25
+ baseEmail: 'tomas@webgpt.cz',
26
+ fullEmail: 'tomas@webgpt.cz',
27
+ plus: [],
28
+ }),
29
+ ).toBe('tomas@webgpt.cz');
30
+ });
31
+
32
+ it('should work with fullname', () => {
33
+ expect(
34
+ stringifyEmailAddress({
35
+ fullName: 'Pavol Hejný',
36
+ baseEmail: 'pavol@webgpt.cz',
37
+ fullEmail: 'pavol@webgpt.cz',
38
+ plus: [],
39
+ }),
40
+ ).toBe('"Pavol Hejný" <pavol@webgpt.cz>');
41
+ expect(
42
+ stringifyEmailAddress({
43
+ fullName: 'Jirka',
44
+ baseEmail: 'jirka@webgpt.cz',
45
+ fullEmail: 'jirka@webgpt.cz',
46
+ plus: [],
47
+ }),
48
+ ).toBe('"Jirka" <jirka@webgpt.cz>');
49
+ expect(
50
+ stringifyEmailAddress({
51
+ fullName: 'Tomáš Studeník',
52
+ baseEmail: 'tomas@webgpt.cz',
53
+ fullEmail: 'tomas@webgpt.cz',
54
+ plus: [],
55
+ }),
56
+ ).toBe('"Tomáš Studeník" <tomas@webgpt.cz>');
57
+ });
58
+
59
+ it('should work with plus', () => {
60
+ expect(
61
+ stringifyEmailAddress({
62
+ fullName: null,
63
+ baseEmail: 'pavol@webgpt.cz',
64
+ fullEmail: 'pavol+test@webgpt.cz',
65
+ plus: ['test'],
66
+ }),
67
+ ).toBe('pavol+test@webgpt.cz');
68
+ expect(
69
+ stringifyEmailAddress({
70
+ fullName: null,
71
+ baseEmail: 'jirka@webgpt.cz',
72
+ fullEmail: 'jirka+test@webgpt.cz',
73
+ plus: ['test'],
74
+ }),
75
+ ).toBe('jirka+test@webgpt.cz');
76
+ expect(
77
+ stringifyEmailAddress({
78
+ fullName: null,
79
+ baseEmail: 'tomas@webgpt.cz',
80
+ fullEmail: 'tomas+test+ainautes@webgpt.cz',
81
+ plus: ['test', 'ainautes'],
82
+ }),
83
+ ).toBe('tomas+test+ainautes@webgpt.cz');
84
+ });
85
+
86
+ it('should work with both fullname and plus', () => {
87
+ expect(
88
+ stringifyEmailAddress({
89
+ fullName: 'Pavol Hejný',
90
+ baseEmail: 'pavol@webgpt.cz',
91
+ fullEmail: 'pavol+test@webgpt.cz',
92
+ plus: ['test'],
93
+ }),
94
+ ).toBe('"Pavol Hejný" <pavol+test@webgpt.cz>');
95
+ expect(
96
+ stringifyEmailAddress({
97
+ fullName: 'Jirka',
98
+ baseEmail: 'jirka@webgpt.cz',
99
+ fullEmail: 'jirka+test@webgpt.cz',
100
+ plus: ['test'],
101
+ }),
102
+ ).toBe('"Jirka" <jirka+test@webgpt.cz>');
103
+ expect(
104
+ stringifyEmailAddress({
105
+ fullName: 'Tomáš Studeník',
106
+ baseEmail: 'tomas@webgpt.cz',
107
+ fullEmail: 'tomas+test+ainautes@webgpt.cz',
108
+ plus: ['test', 'ainautes'],
109
+ }),
110
+ ).toBe('"Tomáš Studeník" <tomas+test+ainautes@webgpt.cz>');
111
+ });
112
+
113
+ // TODO: [🎾] Implement and test here escaping
114
+ });
115
+
116
+
117
+ /**
118
+ * TODO: [🐫] This test fails because of aliased imports `import type { string_emails } from '@promptbook-local/types';`, fix it
119
+ */
@@ -0,0 +1,19 @@
1
+ import type { string_email } from '@promptbook-local/types';
2
+ import type { EmailAddress } from '../Email';
3
+
4
+ /**
5
+ * Makes string email from EmailAddress
6
+ */
7
+ export function stringifyEmailAddress(emailAddress: EmailAddress): string_email {
8
+ const { fullEmail, fullName } = emailAddress;
9
+
10
+ if (fullName !== null) {
11
+ return `"${fullName}" <${fullEmail}>`;
12
+ }
13
+
14
+ return fullEmail;
15
+ }
16
+
17
+ /**
18
+ * TODO: [🎾] Implement and test here escaping
19
+ */
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it } from '@jest/globals';
2
+ import { stringifyEmailAddresses } from './stringifyEmailAddresses';
3
+
4
+ describe('how stringifyEmailAddresses works', () => {
5
+ it('should work with single email', () => {
6
+ expect(
7
+ stringifyEmailAddresses([
8
+ {
9
+ fullName: null,
10
+ baseEmail: 'pavol@webgpt.cz',
11
+ fullEmail: 'pavol@webgpt.cz',
12
+ plus: [],
13
+ },
14
+ ]),
15
+ ).toEqual('pavol@webgpt.cz');
16
+ });
17
+
18
+ it('should work with simple emails', () => {
19
+ expect(
20
+ stringifyEmailAddresses([
21
+ {
22
+ fullName: null,
23
+ baseEmail: 'pavol@webgpt.cz',
24
+ fullEmail: 'pavol@webgpt.cz',
25
+ plus: [],
26
+ },
27
+ {
28
+ fullName: null,
29
+ baseEmail: 'jirka@webgpt.cz',
30
+ fullEmail: 'jirka@webgpt.cz',
31
+ plus: [],
32
+ },
33
+ {
34
+ fullName: null,
35
+ baseEmail: 'tomas@webgpt.cz',
36
+ fullEmail: 'tomas@webgpt.cz',
37
+ plus: [],
38
+ },
39
+ ]),
40
+ ).toEqual('pavol@webgpt.cz, jirka@webgpt.cz, tomas@webgpt.cz');
41
+ });
42
+
43
+ it('should work with fullname', () => {
44
+ expect(
45
+ stringifyEmailAddresses([
46
+ {
47
+ fullName: 'Pavol Hejný',
48
+ baseEmail: 'pavol@webgpt.cz',
49
+ fullEmail: 'pavol@webgpt.cz',
50
+ plus: [],
51
+ },
52
+ {
53
+ fullName: 'Jiří Jahn',
54
+ baseEmail: 'jirka@webgpt.cz',
55
+ fullEmail: 'jirka@webgpt.cz',
56
+ plus: [],
57
+ },
58
+ {
59
+ fullName: 'Tomáš Studeník',
60
+ baseEmail: 'tomas@webgpt.cz',
61
+ fullEmail: 'tomas@webgpt.cz',
62
+ plus: [],
63
+ },
64
+ ]),
65
+ ).toEqual('"Pavol Hejný" <pavol@webgpt.cz>, "Jiří Jahn" <jirka@webgpt.cz>, "Tomáš Studeník" <tomas@webgpt.cz>');
66
+ });
67
+
68
+ // TODO: [🎾] Implement and test here escaping
69
+ });
70
+
71
+
72
+ /**
73
+ * TODO: [🐫] This test fails because of aliased imports `import type { string_emails } from '@promptbook-local/types';`, fix it
74
+ */
@@ -0,0 +1,14 @@
1
+ import type { string_emails } from '@promptbook-local/types';
2
+ import type { EmailAddress } from '../Email';
3
+ import { stringifyEmailAddress } from './stringifyEmailAddress';
4
+
5
+ /**
6
+ * Makes string email from multiple EmailAddress
7
+ */
8
+ export function stringifyEmailAddresses(emailAddresses: Array<EmailAddress>): string_emails {
9
+ return emailAddresses.map((emailAddress) => stringifyEmailAddress(emailAddress)).join(', ');
10
+ }
11
+
12
+ /**
13
+ * TODO: [🎾] Implement and test here escaping
14
+ */
@@ -0,0 +1,44 @@
1
+ import { removeMarkdownFormatting } from '@promptbook-local/markdown-utils';
2
+ import type { really_any } from '@promptbook-local/types';
3
+ import sendgridEmailClient from '@sendgrid/mail';
4
+ import { marked } from 'marked';
5
+ import { MessageProvider } from '../../interfaces/MessageProvider';
6
+ import { OutboundEmail } from '../_common/Email';
7
+ import { parseEmailAddress } from '../_common/utils/parseEmailAddress';
8
+
9
+ export class SendgridMessageProvider implements MessageProvider {
10
+ constructor(private readonly apiKey: string) {
11
+ sendgridEmailClient.setApiKey(this.apiKey);
12
+ }
13
+
14
+ public async send(message: OutboundEmail): Promise<really_any> {
15
+ const sender = message.sender;
16
+ const recipients = (Array.isArray(message.recipients) ? message.recipients : [message.recipients]).filter(
17
+ Boolean,
18
+ ) as really_any[];
19
+
20
+ const text = removeMarkdownFormatting(message.content);
21
+ const html = await marked.parse(message.content);
22
+
23
+ const { fullEmail, fullName } = parseEmailAddress(sender);
24
+
25
+ const response = await sendgridEmailClient.send({
26
+ from: {
27
+ email: fullEmail,
28
+ name: fullName || undefined,
29
+ },
30
+ to: recipients.map((r) => {
31
+ const { fullEmail, fullName } = parseEmailAddress(r.email || r.baseEmail || r);
32
+ return {
33
+ email: fullEmail,
34
+ name: r.name || fullName || undefined,
35
+ };
36
+ }),
37
+ subject: message.metadata?.subject || 'No Subject',
38
+ text,
39
+ html,
40
+ });
41
+
42
+ return response;
43
+ }
44
+ }
@@ -0,0 +1,49 @@
1
+ import { string_markdown } from '@promptbook-local/types';
2
+ import { simpleParser } from 'mailparser';
3
+ import TurndownService from 'turndown';
4
+ import { extractBodyContentFromHtml } from '../../../utils/content/extractBodyContentFromHtml';
5
+ import type { InboundEmail } from '../_common/Email';
6
+ import { parseEmailAddress } from '../_common/utils/parseEmailAddress';
7
+ import { parseEmailAddresses } from '../_common/utils/parseEmailAddresses';
8
+
9
+ /**
10
+ * Function parseInboundSendgridEmail will parse raw inbound email from Sendgrid and return Email object
11
+ */
12
+ export async function parseInboundSendgridEmail(rawEmail: string): Promise<InboundEmail> {
13
+ const parsedEmail = await simpleParser(rawEmail);
14
+
15
+ const toArray = !Array.isArray(parsedEmail.to)
16
+ ? parsedEmail.to === undefined
17
+ ? []
18
+ : [parsedEmail.to]
19
+ : parsedEmail.to;
20
+ const to = toArray.flatMap((_) => parseEmailAddresses(_.text));
21
+
22
+ const ccArray = !Array.isArray(parsedEmail.cc)
23
+ ? parsedEmail.cc === undefined
24
+ ? []
25
+ : [parsedEmail.cc]
26
+ : parsedEmail.cc;
27
+ const cc = ccArray.flatMap((_) => parseEmailAddresses(_.text));
28
+
29
+ const turndownService = new TurndownService();
30
+
31
+ const content = (parsedEmail.html
32
+ ? turndownService.turndown(extractBodyContentFromHtml(parsedEmail.html))
33
+ : parsedEmail.text || '') as string_markdown;
34
+
35
+ const email: InboundEmail = {
36
+ channel: 'EMAIL',
37
+ direction: 'INBOUND',
38
+ sender: parseEmailAddress(parsedEmail.from?.text || '').fullEmail,
39
+ recipients: to.map((_) => _.fullEmail),
40
+ cc,
41
+ subject: parsedEmail.subject || '',
42
+ content,
43
+ attachments: [
44
+ /* <- TODO: [📯] Parse attachments */
45
+ ],
46
+ };
47
+
48
+ return email;
49
+ }
@@ -0,0 +1,51 @@
1
+ import { removeMarkdownFormatting } from '@promptbook-local/markdown-utils';
2
+ import type { really_any } from '@promptbook-local/types';
3
+ import { marked } from 'marked';
4
+ // @ts-expect-error: Zeptomail types are not resolving correctly
5
+ import { SendMailClient } from 'zeptomail';
6
+ import { MessageProvider } from '../../interfaces/MessageProvider';
7
+ import { OutboundEmail } from '../_common/Email';
8
+
9
+ export class ZeptomailMessageProvider implements MessageProvider {
10
+ constructor(private readonly apiKey: string) {}
11
+
12
+ public async send(message: OutboundEmail): Promise<really_any> {
13
+ try {
14
+ const client = new SendMailClient({ url: 'api.zeptomail.com/', token: this.apiKey });
15
+
16
+ const sender = message.sender as really_any;
17
+ const recipients = (Array.isArray(message.recipients) ? message.recipients : [message.recipients]).filter(
18
+ Boolean,
19
+ ) as really_any[];
20
+
21
+ const textbody = removeMarkdownFormatting(message.content);
22
+ const htmlbody = await marked.parse(message.content);
23
+
24
+ const response = await client.sendMail({
25
+ from: {
26
+ address: sender.email || sender.baseEmail || sender,
27
+ name: sender.name || sender.fullName || undefined,
28
+ },
29
+ to: recipients.map((r) => ({
30
+ email_address: {
31
+ address: r.email || r.baseEmail || r,
32
+ name: r.name || r.fullName || undefined,
33
+ },
34
+ })),
35
+ subject: message.metadata?.subject || 'No Subject',
36
+ textbody,
37
+ htmlbody,
38
+ track_clicks: true,
39
+ track_opens: true,
40
+ });
41
+
42
+ return response;
43
+ } catch (raw: really_any) {
44
+ if (!('error' in raw)) {
45
+ throw raw;
46
+ }
47
+
48
+ throw new Error(raw.error.message, raw.error.details);
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,13 @@
1
+ import { SendgridMessageProvider } from './email/sendgrid/SendgridMessageProvider';
2
+ import { ZeptomailMessageProvider } from './email/zeptomail/ZeptomailMessageProvider';
3
+ import { MessageProvider } from './interfaces/MessageProvider';
4
+
5
+ export const EMAIL_PROVIDERS: Record<string, MessageProvider> = {};
6
+
7
+ if (process.env.ZEPTOMAIL_API_KEY) {
8
+ EMAIL_PROVIDERS['ZEPTOMAIL'] = new ZeptomailMessageProvider(process.env.ZEPTOMAIL_API_KEY);
9
+ }
10
+
11
+ if (process.env.SENDGRID_API_KEY) {
12
+ EMAIL_PROVIDERS['SENDGRID'] = new SendgridMessageProvider(process.env.SENDGRID_API_KEY);
13
+ }
@@ -0,0 +1,11 @@
1
+ import type { Message, really_any, string_email } from '@promptbook-local/types';
2
+
3
+ export type MessageProvider = {
4
+ /**
5
+ * Sends a message through the provider
6
+ *
7
+ * @param message The message to send
8
+ * @returns Raw response from the provider
9
+ */
10
+ send(message: Message<string_email>): Promise<really_any>;
11
+ };
@@ -1,13 +1,15 @@
1
1
  import { TODO_any } from '@promptbook-local/types';
2
2
  import { createClient } from '@supabase/supabase-js';
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { SERVERS, SUPABASE_TABLE_PREFIX } from '../config';
4
+ import { SERVERS } from '../config';
5
+ import { $getTableName } from './database/$getTableName';
6
+ import { RESERVED_PATHS } from './generated/reservedPaths';
5
7
  import { isIpAllowed } from './utils/isIpAllowed';
6
8
 
7
- // Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies
9
+ // Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies !!!!
8
10
  function normalizeTo_PascalCase(text: string): string {
9
11
  return text
10
- .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
12
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => {
11
13
  return word.toUpperCase();
12
14
  })
13
15
  .replace(/\s+/g, '');
@@ -33,8 +35,12 @@ export async function middleware(req: NextRequest) {
33
35
  const host = req.headers.get('host');
34
36
 
35
37
  if (host) {
38
+ /*
39
+ Note: [🐔] This code was commented out because results of it are unused
40
+
36
41
  let tablePrefix = SUPABASE_TABLE_PREFIX;
37
42
 
43
+
38
44
  if (SERVERS && SERVERS.length > 0) {
39
45
  // Logic mirrored from src/tools/$provideServer.ts
40
46
  if (SERVERS.some((server) => server === host)) {
@@ -44,6 +50,7 @@ export async function middleware(req: NextRequest) {
44
50
  tablePrefix = `server_${serverName}_`;
45
51
  }
46
52
  }
53
+ */
47
54
 
48
55
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
49
56
  const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
@@ -58,7 +65,7 @@ export async function middleware(req: NextRequest) {
58
65
  });
59
66
 
60
67
  const { data } = await supabase
61
- .from(`${tablePrefix}Metadata`)
68
+ .from(await $getTableName(`Metadata`))
62
69
  .select('value')
63
70
  .eq('key', 'RESTRICT_IP')
64
71
  .single();
@@ -82,6 +89,9 @@ export async function middleware(req: NextRequest) {
82
89
  const token = authHeader.split(' ')[1];
83
90
 
84
91
  if (token.startsWith('ptbk_')) {
92
+ /*
93
+ Note: [🐔] This code was commented out because results of it are unused
94
+
85
95
  const host = req.headers.get('host');
86
96
  let tablePrefix = SUPABASE_TABLE_PREFIX;
87
97
 
@@ -93,6 +103,7 @@ export async function middleware(req: NextRequest) {
93
103
  tablePrefix = `server_${serverName}_`;
94
104
  }
95
105
  }
106
+ */
96
107
 
97
108
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
98
109
  const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
@@ -107,7 +118,7 @@ export async function middleware(req: NextRequest) {
107
118
  });
108
119
 
109
120
  const { data } = await supabase
110
- .from(`${tablePrefix}ApiTokens`)
121
+ .from(await $getTableName(`ApiTokens`))
111
122
  .select('id')
112
123
  .eq('token', token)
113
124
  .eq('isRevoked', false)
@@ -186,22 +197,7 @@ export async function middleware(req: NextRequest) {
186
197
 
187
198
  if (
188
199
  potentialAgentName &&
189
- ![
190
- 'agents',
191
- 'api',
192
- 'admin',
193
- 'docs',
194
- 'test',
195
- 'embed',
196
- '_next',
197
- 'manifest.webmanifest',
198
- 'sw.js',
199
- 'favicon.ico',
200
- 'sitemap.xml',
201
- 'robots.txt',
202
- 'security.txt',
203
- 'humans.txt',
204
- ].includes(potentialAgentName) &&
200
+ !RESERVED_PATHS.includes(potentialAgentName) &&
205
201
  !potentialAgentName.startsWith('.') &&
206
202
  // Note: Other static files are excluded by the matcher configuration below
207
203
  true
@@ -243,7 +239,7 @@ export async function middleware(req: NextRequest) {
243
239
  let serverName = serverHost;
244
240
  serverName = serverName.replace(/\.ptbk\.io$/, '');
245
241
  serverName = normalizeTo_PascalCase(serverName);
246
- const prefix = `server_${serverName}_`;
242
+ // const prefix = `server_${serverName}_`;
247
243
 
248
244
  // Search for agent with matching META LINK
249
245
  // agentProfile->links is an array of strings
@@ -256,7 +252,7 @@ export async function middleware(req: NextRequest) {
256
252
 
257
253
  try {
258
254
  const { data } = await supabase
259
- .from(`${prefix}Agent`)
255
+ .from(await $getTableName(`Agent`))
260
256
  .select('agentName')
261
257
  .or(orFilter)
262
258
  .limit(1)