@promptbook/cli 0.104.0-1 → 0.104.0-10

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 (199) 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/messages/MessagesClient.tsx +294 -0
  15. package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
  16. package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
  17. package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
  18. package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
  19. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
  20. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
  21. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
  22. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
  23. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
  24. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
  25. package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
  26. package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
  27. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
  28. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
  30. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
  31. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
  32. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
  33. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
  34. package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
  35. package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
  36. package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
  37. package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
  38. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
  39. package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
  40. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
  41. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
  42. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
  43. package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
  44. package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
  45. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
  46. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
  47. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
  48. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
  49. package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
  50. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
  51. package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
  52. package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
  53. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
  54. package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
  55. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
  56. package/apps/agents-server/src/app/api/agents/route.ts +28 -3
  57. package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
  58. package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
  59. package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
  60. package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
  61. package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
  62. package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
  63. package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
  64. package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
  65. package/apps/agents-server/src/app/api/messages/route.ts +102 -0
  66. package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
  67. package/apps/agents-server/src/app/api/upload/route.ts +128 -45
  68. package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
  69. package/apps/agents-server/src/app/docs/page.tsx +12 -12
  70. package/apps/agents-server/src/app/globals.css +140 -33
  71. package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
  72. package/apps/agents-server/src/app/layout.tsx +27 -22
  73. package/apps/agents-server/src/app/page.tsx +54 -6
  74. package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
  75. package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
  76. package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
  77. package/apps/agents-server/src/app/security.txt/route.ts +1 -1
  78. package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
  79. package/apps/agents-server/src/app/swagger/page.tsx +14 -0
  80. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +41 -116
  81. package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
  82. package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
  83. package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
  84. package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
  85. package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
  86. package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
  87. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
  88. package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
  89. package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
  90. package/apps/agents-server/src/components/Header/Header.tsx +114 -40
  91. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +145 -23
  92. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
  93. package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
  94. package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
  95. package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
  96. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
  97. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
  98. package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
  99. package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
  100. package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
  101. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
  102. package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
  103. package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
  104. package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
  105. package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
  106. package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
  107. package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
  108. package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
  109. package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
  110. package/apps/agents-server/src/database/migrate.ts +34 -1
  111. package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
  112. package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
  113. package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
  114. package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
  115. package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
  116. package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
  117. package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
  118. package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
  119. package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
  120. package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
  121. package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
  122. package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
  123. package/apps/agents-server/src/database/schema.ts +231 -4
  124. package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
  125. package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
  126. package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
  127. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
  128. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
  129. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
  130. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
  131. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
  132. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
  133. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
  134. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
  135. package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
  136. package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
  137. package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
  138. package/apps/agents-server/src/message-providers/index.ts +13 -0
  139. package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
  140. package/apps/agents-server/src/middleware.ts +19 -23
  141. package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
  142. package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
  143. package/apps/agents-server/src/utils/auth.ts +117 -17
  144. package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
  145. package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
  146. package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
  147. package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
  148. package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
  149. package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
  150. package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
  151. package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
  152. package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
  153. package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
  154. package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
  155. package/esm/index.es.js +2890 -2737
  156. package/esm/index.es.js.map +1 -1
  157. package/esm/typings/servers.d.ts +8 -0
  158. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  159. package/esm/typings/src/_packages/types.index.d.ts +10 -2
  160. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
  161. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
  162. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
  163. package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
  164. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
  165. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  166. package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
  167. package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  168. package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
  169. package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
  170. package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
  171. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +21 -11
  172. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +80 -14
  173. package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
  174. package/esm/typings/src/commitments/index.d.ts +2 -1
  175. package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
  176. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
  177. package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
  178. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
  179. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
  180. package/esm/typings/src/types/Message.d.ts +49 -0
  181. package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
  182. package/esm/typings/src/types/typeAliases.d.ts +23 -1
  183. package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
  184. package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
  185. package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
  186. package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
  187. package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
  188. package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
  189. package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
  190. package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
  191. package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
  192. package/esm/typings/src/version.d.ts +1 -1
  193. package/package.json +1 -1
  194. package/umd/index.umd.js +4018 -3865
  195. package/umd/index.umd.js.map +1 -1
  196. package/apps/agents-server/package-lock.json +0 -27
  197. package/apps/agents-server/public/fonts/download-font.js +0 -22
  198. package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
  199. package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
@@ -0,0 +1,200 @@
1
+ 'use server';
2
+
3
+ import { saturate } from '@promptbook-local/color';
4
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
5
+ import Link from 'next/link';
6
+ import { Color } from '../../../../../../../src/utils/color/Color';
7
+ import { getAgentName, getAgentProfile } from '../_utils';
8
+
9
+ /**
10
+ * Available image types for agents with their descriptions and sizes
11
+ */
12
+ const AGENT_IMAGES = [
13
+ {
14
+ name: 'default-avatar.png',
15
+ title: 'Default Avatar',
16
+ description: 'AI-generated avatar image based on the agent profile. Vertical orientation (1024x1792).',
17
+ size: '1024×1792',
18
+ },
19
+ {
20
+ name: 'icon-256.png',
21
+ title: 'Icon (256×256)',
22
+ description: 'Small circular icon suitable for profile pictures and thumbnails.',
23
+ size: '256×256',
24
+ },
25
+ {
26
+ name: 'screenshot-fullhd.png',
27
+ title: 'Screenshot Full HD',
28
+ description: 'Landscape screenshot showing the agent with name. Suitable for desktop previews.',
29
+ size: '1920×1080',
30
+ },
31
+ {
32
+ name: 'screenshot-phone.png',
33
+ title: 'Screenshot Phone',
34
+ description: 'Portrait screenshot optimized for mobile devices.',
35
+ size: '1080×1920',
36
+ },
37
+ ] as const;
38
+
39
+ export default async function AgentImagesPage({ params }: { params: Promise<{ agentName: string }> }) {
40
+ const agentName = await getAgentName(params);
41
+ const agentProfile = await getAgentProfile(agentName);
42
+
43
+ const brandColor = Color.fromSafe(agentProfile.meta.color || PROMPTBOOK_COLOR);
44
+ const brandColorHex = brandColor.then(saturate(-0.5)).toHex();
45
+
46
+ const fullname = (agentProfile.meta.fullname || agentProfile.agentName || 'Agent') as string;
47
+
48
+ return (
49
+ <div
50
+ style={{
51
+ minHeight: '100vh',
52
+ backgroundColor: '#f5f5f5',
53
+ padding: '2rem',
54
+ }}
55
+ >
56
+ <div
57
+ style={{
58
+ maxWidth: '1200px',
59
+ margin: '0 auto',
60
+ }}
61
+ >
62
+ <header
63
+ style={{
64
+ marginBottom: '2rem',
65
+ padding: '1.5rem',
66
+ backgroundColor: brandColorHex,
67
+ borderRadius: '12px',
68
+ color: 'white',
69
+ }}
70
+ >
71
+ <h1 style={{ margin: 0, fontSize: '2rem' }}>
72
+ Images for <strong>{fullname}</strong>
73
+ </h1>
74
+ <p style={{ margin: '0.5rem 0 0', opacity: 0.9 }}>
75
+ All available image assets for agent{' '}
76
+ <code
77
+ style={{
78
+ backgroundColor: 'rgba(255,255,255,0.2)',
79
+ padding: '2px 6px',
80
+ borderRadius: '4px',
81
+ }}
82
+ >
83
+ {agentName}
84
+ </code>
85
+ </p>
86
+ </header>
87
+
88
+ <div
89
+ style={{
90
+ display: 'grid',
91
+ gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
92
+ gap: '1.5rem',
93
+ }}
94
+ >
95
+ {AGENT_IMAGES.map((image) => {
96
+ const imageUrl = `/agents/${encodeURIComponent(agentName)}/images/${image.name}`;
97
+ return (
98
+ <div
99
+ key={image.name}
100
+ style={{
101
+ backgroundColor: 'white',
102
+ borderRadius: '12px',
103
+ overflow: 'hidden',
104
+ boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
105
+ }}
106
+ >
107
+ <div
108
+ style={{
109
+ aspectRatio: '16/9',
110
+ backgroundColor: '#e0e0e0',
111
+ display: 'flex',
112
+ alignItems: 'center',
113
+ justifyContent: 'center',
114
+ overflow: 'hidden',
115
+ }}
116
+ >
117
+ {/* eslint-disable-next-line @next/next/no-img-element */}
118
+ <img
119
+ src={imageUrl}
120
+ alt={image.title}
121
+ style={{
122
+ maxWidth: '100%',
123
+ maxHeight: '100%',
124
+ objectFit: 'contain',
125
+ }}
126
+ />
127
+ </div>
128
+ <div style={{ padding: '1rem' }}>
129
+ <h2 style={{ margin: '0 0 0.5rem', fontSize: '1.25rem' }}>{image.title}</h2>
130
+ <p style={{ margin: '0 0 0.5rem', color: '#666', fontSize: '0.9rem' }}>
131
+ {image.description}
132
+ </p>
133
+ <div
134
+ style={{
135
+ display: 'flex',
136
+ justifyContent: 'space-between',
137
+ alignItems: 'center',
138
+ marginTop: '1rem',
139
+ }}
140
+ >
141
+ <span
142
+ style={{
143
+ backgroundColor: '#f0f0f0',
144
+ padding: '4px 8px',
145
+ borderRadius: '4px',
146
+ fontSize: '0.85rem',
147
+ color: '#555',
148
+ }}
149
+ >
150
+ {image.size}
151
+ </span>
152
+ <Link
153
+ href={imageUrl}
154
+ target="_blank"
155
+ style={{
156
+ backgroundColor: brandColorHex,
157
+ color: 'white',
158
+ padding: '8px 16px',
159
+ borderRadius: '6px',
160
+ textDecoration: 'none',
161
+ fontSize: '0.9rem',
162
+ }}
163
+ >
164
+ Open Image
165
+ </Link>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ );
170
+ })}
171
+ </div>
172
+
173
+ <footer
174
+ style={{
175
+ marginTop: '2rem',
176
+ padding: '1rem',
177
+ backgroundColor: 'white',
178
+ borderRadius: '12px',
179
+ textAlign: 'center',
180
+ color: '#666',
181
+ }}
182
+ >
183
+ <p style={{ margin: 0 }}>
184
+ <Link
185
+ href={`/agents/${encodeURIComponent(agentName)}`}
186
+ style={{ color: brandColorHex, textDecoration: 'none' }}
187
+ >
188
+ ← Back to {fullname}
189
+ </Link>
190
+ </p>
191
+ </footer>
192
+ </div>
193
+ </div>
194
+ );
195
+ }
196
+
197
+ /**
198
+ * TODO: [🦚] Add download button functionality
199
+ * TODO: [🦚] Add image regeneration option for default-avatar
200
+ */
@@ -1,3 +1,4 @@
1
+ import { $provideServer } from '@/src/tools/$provideServer';
1
2
  import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
3
  import { serializeError } from '@promptbook-local/utils';
3
4
  import { ImageResponse } from 'next/og';
@@ -21,6 +22,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
21
22
  const agentProfile = await getAgentProfile(agentName);
22
23
  const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
23
24
  const backgroundColor = agentColor.then(grayscale(0.5));
25
+ const { publicUrl } = await $provideServer();
24
26
 
25
27
  return new ImageResponse(
26
28
  (
@@ -48,10 +50,9 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
48
50
  <img
49
51
  style={{
50
52
  width: '80%',
51
- backgroundColor: agentColor.toHex(),
52
- borderRadius: '50%',
53
+ // backgroundColor: agentColor.toHex(),
53
54
  }}
54
- src={agentProfile.meta.image!}
55
+ src={`${publicUrl.href}agents/${agentProfile.permanentId || agentName}/images/icon-256.png`}
55
56
  alt="Agent Icon"
56
57
  />
57
58
  </div>
@@ -1,3 +1,4 @@
1
+ import { $provideServer } from '@/src/tools/$provideServer';
1
2
  import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
3
  import { serializeError } from '@promptbook-local/utils';
3
4
  import { ImageResponse } from 'next/og';
@@ -21,6 +22,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
21
22
  const agentProfile = await getAgentProfile(agentName);
22
23
  const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
23
24
  const backgroundColor = agentColor.then(grayscale(0.5));
25
+ const { publicUrl } = await $provideServer();
24
26
 
25
27
  return new ImageResponse(
26
28
  (
@@ -48,10 +50,9 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
48
50
  <img
49
51
  style={{
50
52
  width: '80%',
51
- backgroundColor: agentColor.toHex(),
52
- borderRadius: '50%',
53
+ // backgroundColor: agentColor.toHex(),
53
54
  }}
54
- src={agentProfile.meta.image!}
55
+ src={`${publicUrl.href}agents/${agentProfile.permanentId || agentName}/images/icon-256.png`}
55
56
  alt="Agent Icon"
56
57
  />
57
58
  </div>
@@ -4,7 +4,7 @@ import { $getTableName } from '@/src/database/$getTableName';
4
4
  import { $provideSupabase } from '@/src/database/$provideSupabase';
5
5
  import { $provideServer } from '@/src/tools/$provideServer';
6
6
  import { isUserAdmin } from '@/src/utils/isUserAdmin';
7
- import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
7
+ import { generatePlaceholderAgentProfileImageUrl, PROMPTBOOK_COLOR } from '@promptbook-local/core';
8
8
  import { ArrowLeftIcon, BoxIcon, CodeIcon, GlobeIcon, ServerIcon, TerminalIcon } from 'lucide-react';
9
9
  import { headers } from 'next/headers';
10
10
  import Link from 'next/link';
@@ -25,6 +25,7 @@ export const generateMetadata = generateAgentMetadata;
25
25
 
26
26
  export default async function AgentIntegrationPage({ params }: { params: Promise<{ agentName: string }> }) {
27
27
  $sideEffect(headers());
28
+
28
29
  const agentName = await getAgentName(params);
29
30
  const isAdmin = await isUserAdmin();
30
31
 
@@ -170,7 +171,7 @@ export default async function AgentIntegrationPage({ params }: { params: Promise
170
171
  }
171
172
  `);
172
173
 
173
- const agentLinks = getAgentLinks(agentName);
174
+ const agentLinks = getAgentLinks(agentProfile.permanentId || agentName);
174
175
  const chatLink = agentLinks.find((l) => l.title === 'Chat with Agent')!;
175
176
  const websiteIntegrationLink = agentLinks.find((l) => l.title === 'Website Integration')!;
176
177
 
@@ -187,7 +188,13 @@ export default async function AgentIntegrationPage({ params }: { params: Promise
187
188
  {agentProfile.meta.image && (
188
189
  // eslint-disable-next-line @next/next/no-img-element
189
190
  <img
190
- src={agentProfile.meta.image as string}
191
+ src={
192
+ agentProfile.meta.image ||
193
+ generatePlaceholderAgentProfileImageUrl(
194
+ agentProfile.permanentId || agentName,
195
+ publicUrl,
196
+ )
197
+ }
191
198
  alt={agentProfile.meta.fullname || agentName}
192
199
  className="w-16 h-16 rounded-full object-cover border-2"
193
200
  style={{ borderColor: primaryColor }}
@@ -1,7 +1,7 @@
1
1
  'use server';
2
2
 
3
3
  import { $provideServer } from '@/src/tools/$provideServer';
4
- import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
4
+ import { generatePlaceholderAgentProfileImageUrl, PROMPTBOOK_COLOR } from '@promptbook-local/core';
5
5
  import { ArrowLeftIcon, CodeIcon, HomeIcon, LinkIcon, ShareIcon } from 'lucide-react';
6
6
  import { headers } from 'next/headers';
7
7
  import Link from 'next/link';
@@ -9,15 +9,16 @@ import { notFound } from 'next/navigation';
9
9
  import { Color } from '../../../../../../../src/utils/color/Color';
10
10
  import { withAlpha } from '../../../../../../../src/utils/color/operators/withAlpha';
11
11
  import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
12
+ import { getAgentName, getAgentProfile } from '../_utils';
12
13
  import { getAgentExternalLinks, getAgentLinks } from '../agentLinks';
13
14
  import { CopyField } from '../CopyField';
14
- import { getAgentName, getAgentProfile } from '../_utils';
15
15
  import { generateAgentMetadata } from '../generateAgentMetadata';
16
16
 
17
17
  export const generateMetadata = generateAgentMetadata;
18
18
 
19
19
  export default async function AgentLinksPage({ params }: { params: Promise<{ agentName: string }> }) {
20
20
  $sideEffect(headers());
21
+
21
22
  const agentName = await getAgentName(params);
22
23
 
23
24
  let agentProfile;
@@ -56,7 +57,13 @@ export default async function AgentLinksPage({ params }: { params: Promise<{ age
56
57
  {agentProfile.meta.image && (
57
58
  // eslint-disable-next-line @next/next/no-img-element
58
59
  <img
59
- src={agentProfile.meta.image as string}
60
+ src={
61
+ agentProfile.meta.image ||
62
+ generatePlaceholderAgentProfileImageUrl(
63
+ agentProfile.permanentId || agentName,
64
+ publicUrl,
65
+ )
66
+ }
60
67
  alt={agentProfile.meta.fullname || agentName}
61
68
  className="w-16 h-16 rounded-full object-cover border-2"
62
69
  style={{ borderColor: primaryColor }}
@@ -129,7 +136,7 @@ export default async function AgentLinksPage({ params }: { params: Promise<{ age
129
136
  Agent Resources
130
137
  </h2>
131
138
  <div className="grid md:grid-cols-2 gap-4">
132
- {getAgentLinks(agentName)
139
+ {getAgentLinks(agentProfile.permanentId || agentName)
133
140
  .filter((link) =>
134
141
  ['Chat with Agent', 'History & Feedback', 'Integration'].includes(link.title),
135
142
  )
@@ -1,4 +1,5 @@
1
- import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
1
+ import { $provideServer } from '@/src/tools/$provideServer';
2
+ import { generatePlaceholderAgentProfileImageUrl, PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
3
  import { serializeError } from '@promptbook-local/utils';
3
4
  import { ImageResponse } from 'next/og';
4
5
  import { assertsError } from '../../../../../../src/errors/assertsError';
@@ -24,6 +25,7 @@ export default async function Image({ params }: { params: Promise<{ agentName: s
24
25
  const agentProfile = await getAgentProfile(agentName);
25
26
  const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
26
27
  const backgroundColor = agentColor.then(grayscale(0.5));
28
+ const { publicUrl } = await $provideServer();
27
29
 
28
30
  return new ImageResponse(
29
31
  (
@@ -53,8 +55,15 @@ export default async function Image({ params }: { params: Promise<{ agentName: s
53
55
  width: '80%',
54
56
  backgroundColor: agentColor.toHex(),
55
57
  borderRadius: '50%',
58
+ aspectRatio: '1 / 1',
56
59
  }}
57
- src={agentProfile.meta.image!}
60
+ src={
61
+ agentProfile.meta.image ||
62
+ generatePlaceholderAgentProfileImageUrl(
63
+ agentProfile.permanentId || agentName,
64
+ publicUrl,
65
+ )
66
+ }
58
67
  alt="Agent Icon"
59
68
  />
60
69
  </div>
@@ -3,10 +3,11 @@
3
3
  import { $provideServer } from '@/src/tools/$provideServer';
4
4
  import { isUserAdmin } from '@/src/utils/isUserAdmin';
5
5
  import { saturate } from '@promptbook-local/color';
6
- import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
6
+ import { generatePlaceholderAgentProfileImageUrl, NotFoundError, PROMPTBOOK_COLOR } from '@promptbook-local/core';
7
7
  import { notFound } from 'next/navigation';
8
8
  import { Color } from '../../../../../../src/utils/color/Color';
9
- import { getAgentName, getAgentProfile } from './_utils';
9
+ import { DeletedAgentBanner } from '../../../components/DeletedAgentBanner';
10
+ import { getAgentName, getAgentProfile, isAgentDeleted } from './_utils';
10
11
  import { getAgentLinks } from './agentLinks';
11
12
  import { AgentProfileChat } from './AgentProfileChat';
12
13
  import { AgentProfileWrapper } from './AgentProfileWrapper';
@@ -26,24 +27,24 @@ export default async function AgentPage({
26
27
  const isAdmin = await isUserAdmin();
27
28
  const { headless: headlessParam } = await searchParams;
28
29
  const isHeadless = headlessParam !== undefined;
30
+ const { publicUrl } = await $provideServer();
29
31
 
30
32
  let agentProfile;
31
33
  try {
32
34
  agentProfile = await getAgentProfile(agentName);
33
35
  } catch (error) {
34
36
  if (
35
- error instanceof Error &&
36
- // Note: This is a bit hacky, but valid way to check for specific error message
37
- (error.message.includes('Cannot coerce the result to a single JSON object') ||
38
- error.message.includes('JSON object requested, multiple (or no) results returned'))
37
+ error instanceof NotFoundError ||
38
+ (error instanceof Error &&
39
+ // Note: This is a bit hacky, but valid way to check for specific error message
40
+ (error.message.includes('Cannot coerce the result to a single JSON object') ||
41
+ error.message.includes('JSON object requested, multiple (or no) results returned')))
39
42
  ) {
40
43
  notFound();
41
44
  }
42
45
  throw error;
43
46
  }
44
47
 
45
- const { publicUrl } = await $provideServer();
46
-
47
48
  // Build agent page URL for QR and copy
48
49
  const agentUrl = `${publicUrl.href}${encodeURIComponent(agentName)}`;
49
50
  // <- TODO: [🐱‍🚀] Better
@@ -54,6 +55,7 @@ export default async function AgentPage({
54
55
  const brandColorHex = brandColor.then(saturate(-0.5)).toHex();
55
56
 
56
57
  const fullname = (agentProfile.meta.fullname || agentProfile.agentName || 'Agent') as string;
58
+ const isDeleted = await isAgentDeleted(agentName);
57
59
 
58
60
  return (
59
61
  <>
@@ -61,6 +63,7 @@ export default async function AgentPage({
61
63
  <AgentProfileWrapper
62
64
  agent={agentProfile}
63
65
  agentUrl={agentUrl}
66
+ publicUrl={publicUrl}
64
67
  agentEmail={agentEmail}
65
68
  agentName={agentName}
66
69
  brandColorHex={brandColorHex}
@@ -68,7 +71,7 @@ export default async function AgentPage({
68
71
  isHeadless={isHeadless}
69
72
  actions={
70
73
  <>
71
- {getAgentLinks(agentName)
74
+ {getAgentLinks(agentProfile.permanentId || agentName)
72
75
  .filter((link) => ['Edit Book', 'Integration', 'All Links'].includes(link.title))
73
76
  .map((link) => (
74
77
  <a
@@ -86,12 +89,17 @@ export default async function AgentPage({
86
89
  </>
87
90
  }
88
91
  >
92
+ {isDeleted && <DeletedAgentBanner />}
89
93
  <AgentProfileChat
90
94
  agentUrl={agentUrl}
91
95
  agentName={agentName}
92
96
  fullname={fullname}
93
97
  brandColorHex={brandColorHex}
94
- avatarSrc={agentProfile.meta.image!}
98
+ avatarSrc={
99
+ agentProfile.meta.image ||
100
+ generatePlaceholderAgentProfileImageUrl(agentProfile.permanentId || agentName, publicUrl)
101
+ }
102
+ isDeleted={isDeleted}
95
103
  />
96
104
  </AgentProfileWrapper>
97
105
  </>
@@ -0,0 +1,100 @@
1
+ 'use server';
2
+
3
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
4
+ import { $provideServer } from '@/src/tools/$provideServer';
5
+ import { generatePlaceholderAgentProfileImageUrl } from '@promptbook-local/core';
6
+ import { ArrowLeftIcon, FileTextIcon } from 'lucide-react';
7
+ import { headers } from 'next/headers';
8
+ import Link from 'next/link';
9
+ import { notFound } from 'next/navigation';
10
+ import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
11
+ import { getAgentName, getAgentProfile } from '../_utils';
12
+ import { generateAgentMetadata } from '../generateAgentMetadata';
13
+
14
+ export const generateMetadata = generateAgentMetadata;
15
+
16
+ export default async function AgentSystemMessagePage({ params }: { params: Promise<{ agentName: string }> }) {
17
+ $sideEffect(headers());
18
+
19
+ const { publicUrl } = await $provideServer();
20
+
21
+ const agentName = await getAgentName(params);
22
+
23
+ let agentProfile;
24
+ let agentSource;
25
+ try {
26
+ agentProfile = await getAgentProfile(agentName);
27
+ const collection = await $provideAgentCollectionForServer();
28
+ agentSource = await collection.getAgentSource(agentName);
29
+ } catch (error) {
30
+ if (
31
+ error instanceof Error &&
32
+ (error.message.includes('Cannot coerce the result to a single JSON object') ||
33
+ error.message.includes('JSON object requested, multiple (or no) results returned'))
34
+ ) {
35
+ notFound();
36
+ }
37
+ throw error;
38
+ }
39
+
40
+ // For now, we'll display the agent source as the system message
41
+ // TODO: [🧠] This might need to be the actual generated system message from the Agent class
42
+ const systemMessage = agentSource || 'No system message available';
43
+
44
+ return (
45
+ <div className="min-h-screen p-6 md:p-12 flex flex-col items-center bg-gray-50">
46
+ <div className="w-full max-w-4xl bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
47
+ {/* Header */}
48
+ <div className="p-6 border-b border-gray-200 flex items-center gap-4">
49
+ {agentProfile.meta.image && (
50
+ // eslint-disable-next-line @next/next/no-img-element
51
+ <img
52
+ src={
53
+ agentProfile.meta.image ||
54
+ generatePlaceholderAgentProfileImageUrl(
55
+ agentProfile.permanentId || agentName,
56
+ publicUrl,
57
+ )
58
+ }
59
+ alt={agentProfile.meta.fullname || agentName}
60
+ className="w-16 h-16 rounded-full object-cover border-2 border-gray-200"
61
+ />
62
+ )}
63
+ <div className="flex-1">
64
+ <h1 className="text-2xl font-bold text-gray-900">{agentProfile.meta.fullname || agentName}</h1>
65
+ <p className="text-gray-500 flex items-center gap-2">
66
+ <FileTextIcon className="w-4 h-4" />
67
+ System Message
68
+ </p>
69
+ </div>
70
+ <Link
71
+ href={`/agents/${encodeURIComponent(agentName)}`}
72
+ className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors"
73
+ title="Back to Agent"
74
+ >
75
+ <ArrowLeftIcon className="w-6 h-6" />
76
+ </Link>
77
+ </div>
78
+
79
+ <div className="p-6">
80
+ <div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
81
+ <h2 className="text-lg font-semibold text-gray-900 mb-3">Generated System Message</h2>
82
+ <pre className="text-sm text-gray-700 whitespace-pre-wrap font-mono bg-white p-4 rounded border border-gray-200 overflow-x-auto">
83
+ {systemMessage}
84
+ </pre>
85
+ </div>
86
+
87
+ <div className="mt-6 bg-blue-50 rounded-lg p-4 border border-blue-200">
88
+ <h3 className="text-md font-semibold text-blue-900 mb-2">Model Requirements</h3>
89
+ <div className="text-sm text-blue-800">
90
+ <p>
91
+ <strong>Model Variant:</strong> CHAT
92
+ </p>
93
+ {/* TODO: [🧠] Add more model requirements if available */}
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ );
100
+ }
@@ -0,0 +1,12 @@
1
+ import { getMetadata } from '../../../database/getMetadata';
2
+ import { NextResponse } from 'next/server';
3
+
4
+ export async function GET() {
5
+ try {
6
+ const adminEmail = await getMetadata('ADMIN_EMAIL');
7
+ return NextResponse.json({ adminEmail });
8
+ } catch (error) {
9
+ console.error('Failed to get admin email:', error);
10
+ return NextResponse.json({ adminEmail: 'support@ptbk.io' }, { status: 500 });
11
+ }
12
+ }
@@ -1,16 +1,15 @@
1
- // POST /api/agents/[agentName]/clone
2
1
  import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
3
- import { AgentBasicInformation } from '../../../../../../../../src/book-2.0/agent-source/AgentBasicInformation';
4
- import { string_book } from '../../../../../../../../src/book-2.0/agent-source/string_book';
5
2
  import { TODO_any } from '@promptbook-local/types';
6
3
  import { NextResponse } from 'next/server';
4
+ import { string_book } from '../../../../../../../../src/book-2.0/agent-source/string_book';
7
5
 
8
6
  export async function POST(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
9
7
  const { agentName } = await params;
10
8
  const collection = await $provideAgentCollectionForServer();
11
9
 
12
10
  try {
13
- const source = await collection.getAgentSource(agentName);
11
+ const agentId = await collection.getAgentPermanentId(agentName);
12
+ const source = await collection.getAgentSource(agentId);
14
13
 
15
14
  // Generate new name
16
15
  // TODO: [🧠] Better naming strategy, maybe check for collisions
@@ -20,15 +19,15 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
20
19
  // eslint-disable-next-line no-constant-condition
21
20
  while (true) {
22
21
  try {
23
- await collection.getAgentSource(newAgentName);
24
- // If success, it means it exists, so we try next one
25
- counter++;
26
- newAgentName = `${agentName} (Copy ${counter})`;
27
- } catch (error) {
28
- // If error, it likely means it does not exist (NotFoundError), so we can use it
29
- // TODO: [🧠] Check if it is really NotFoundError
30
- break;
31
- }
22
+ await collection.getAgentPermanentId(newAgentName);
23
+ // If success, it means it exists, so we try next one
24
+ counter++;
25
+ newAgentName = `${agentName} (Copy ${counter})`;
26
+ } catch (error) {
27
+ // If error, it likely means it does not exist (NotFoundError), so we can use it
28
+ // TODO: [🧠] Check if it is really NotFoundError
29
+ break;
30
+ }
32
31
  }
33
32
 
34
33
  const lines = source.split('\n');
@@ -36,7 +35,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
36
35
  const newSource = lines.join('\n') as string_book;
37
36
 
38
37
  const newAgent = await collection.createAgent(newSource);
39
-
38
+
40
39
  return NextResponse.json(newAgent);
41
40
  } catch (error) {
42
41
  return NextResponse.json(
@@ -0,0 +1,20 @@
1
+ // POST /api/agents/[agentName]/restore - restore deleted agent
2
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
3
+ import { TODO_any } from '@promptbook-local/types';
4
+ import { NextResponse } from 'next/server';
5
+
6
+ export async function POST(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
7
+ const { agentName } = await params;
8
+ const collection = await $provideAgentCollectionForServer();
9
+
10
+ try {
11
+ const agentId = await collection.getAgentPermanentId(agentName);
12
+ await collection.restoreAgent(agentId);
13
+ return NextResponse.json({ success: true });
14
+ } catch (error) {
15
+ return NextResponse.json(
16
+ { success: false, error: (error as TODO_any)?.message || 'Failed to restore agent' },
17
+ { status: 500 },
18
+ );
19
+ }
20
+ }