@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
@@ -3,16 +3,25 @@ import { AgentCard } from './AgentCard';
3
3
  import { Section } from './Section';
4
4
 
5
5
  type ExternalAgentsSectionProps = {
6
- agentsByServer: AgentsByServer[];
6
+ /**
7
+ * @@@
8
+ */
9
+ readonly agentsByServer: AgentsByServer[];
10
+
11
+ /**
12
+ * Base URL of the agents server
13
+ */
14
+ readonly publicUrl: URL;
7
15
  };
8
16
 
9
- export function ExternalAgentsSection({ agentsByServer }: ExternalAgentsSectionProps) {
17
+ export function ExternalAgentsSection(props: ExternalAgentsSectionProps) {
18
+ const { agentsByServer, publicUrl } = props;
10
19
  return (
11
20
  <>
12
21
  {agentsByServer.map(({ serverUrl, agents }) => (
13
22
  <Section key={serverUrl} title={`Agents from ${new URL(serverUrl).hostname} (${agents.length})`}>
14
23
  {agents.map((agent) => (
15
- <AgentCard key={agent.url} agent={agent} href={agent.url} />
24
+ <AgentCard key={agent.url} agent={agent} href={agent.url} publicUrl={publicUrl} />
16
25
  ))}
17
26
  </Section>
18
27
  ))}
@@ -15,7 +15,15 @@ type ServerState = {
15
15
  error?: string;
16
16
  };
17
17
 
18
- export function ExternalAgentsSectionClient() {
18
+ type ExternalAgentsSectionClientProps = {
19
+ /**
20
+ * Base URL of the agents server
21
+ */
22
+ readonly publicUrl: URL;
23
+ };
24
+
25
+ export function ExternalAgentsSectionClient(props: ExternalAgentsSectionClientProps) {
26
+ const { publicUrl } = props;
19
27
  const [servers, setServers] = useState<Record<string, ServerState>>({});
20
28
  const [initialLoading, setInitialLoading] = useState(true);
21
29
 
@@ -26,24 +34,23 @@ export function ExternalAgentsSectionClient() {
26
34
  try {
27
35
  const response = await fetch('/api/federated-agents');
28
36
  if (!response.ok) throw new Error('Failed to fetch federated servers');
29
-
37
+
30
38
  const data: FederatedServersResponse = await response.json();
31
-
39
+
32
40
  if (isCancelled) return;
33
41
 
34
42
  const initialServerState: Record<string, ServerState> = {};
35
- data.federatedServers.forEach(serverUrl => {
43
+ data.federatedServers.forEach((serverUrl) => {
36
44
  initialServerState[serverUrl] = { status: 'loading', agents: [] };
37
45
  });
38
-
46
+
39
47
  setServers(initialServerState);
40
48
  setInitialLoading(false);
41
49
 
42
50
  // Fetch agents for each server independently
43
- data.federatedServers.forEach(serverUrl => {
51
+ data.federatedServers.forEach((serverUrl) => {
44
52
  fetchAgentsForServer(serverUrl);
45
53
  });
46
-
47
54
  } catch (error) {
48
55
  console.error('Failed to load federated servers list', error);
49
56
  if (!isCancelled) setInitialLoading(false);
@@ -80,7 +87,9 @@ export function ExternalAgentsSectionClient() {
80
87
  const response = await fetch(proxyUrl);
81
88
 
82
89
  if (!response.ok) {
83
- throw new Error(`Failed to fetch agents from ${serverUrl} via proxy (Status: ${response.status})`);
90
+ throw new Error(
91
+ `Failed to fetch agents from ${serverUrl} via proxy (Status: ${response.status})`,
92
+ );
84
93
  }
85
94
 
86
95
  const data = await response.json();
@@ -134,7 +143,7 @@ export function ExternalAgentsSectionClient() {
134
143
 
135
144
  return (
136
145
  <>
137
- {serverUrls.map(serverUrl => {
146
+ {serverUrls.map((serverUrl) => {
138
147
  const state = servers[serverUrl];
139
148
  const hostname = (() => {
140
149
  try {
@@ -169,7 +178,7 @@ export function ExternalAgentsSectionClient() {
169
178
  return (
170
179
  <Section key={serverUrl} title={`Agents from ${hostname} (${state.agents.length})`}>
171
180
  {state.agents.map((agent) => (
172
- <AgentCard key={agent.url} agent={agent} href={agent.url} />
181
+ <AgentCard key={agent.url} agent={agent} href={agent.url} publicUrl={publicUrl} />
173
182
  ))}
174
183
  </Section>
175
184
  );
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { usePathname, useSearchParams } from 'next/navigation';
4
4
  import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
5
+ import { MenuHoistingProvider } from '../../../../../src/book-components/_common/MenuHoisting/MenuHoistingContext';
5
6
  import type { UserInfo } from '../../utils/getCurrentUser';
6
7
  import { Footer, type FooterLink } from '../Footer/Footer';
7
8
  import { Header } from '../Header/Header';
@@ -41,7 +42,7 @@ export function LayoutWrapper({
41
42
  }
42
43
 
43
44
  return (
44
- <>
45
+ <MenuHoistingProvider>
45
46
  <Header
46
47
  isAdmin={isAdmin}
47
48
  currentUser={currentUser}
@@ -52,6 +53,6 @@ export function LayoutWrapper({
52
53
  />
53
54
  <main className={`pt-[60px]`}>{children}</main>
54
55
  {isFooterShown && !isFooterHiddenOnPage && <Footer extraLinks={footerLinks} />}
55
- </>
56
+ </MenuHoistingProvider>
56
57
  );
57
58
  }
@@ -1,8 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import { loginAction } from '@/src/app/actions';
4
+ import { ForgottenPasswordDialog } from '../ForgottenPasswordDialog/ForgottenPasswordDialog';
5
+ import { RegisterUserDialog } from '../RegisterUserDialog/RegisterUserDialog';
4
6
  import { Loader2, Lock, User } from 'lucide-react';
5
- import { useState } from 'react';
7
+ import { useEffect, useState } from 'react';
6
8
 
7
9
  type LoginFormProps = {
8
10
  onSuccess?: () => void;
@@ -13,6 +15,24 @@ export function LoginForm(props: LoginFormProps) {
13
15
  const { onSuccess, className } = props;
14
16
  const [isLoading, setIsLoading] = useState(false);
15
17
  const [error, setError] = useState<string | null>(null);
18
+ const [adminEmail, setAdminEmail] = useState<string>('support@ptbk.io');
19
+ const [isForgottenPasswordOpen, setIsForgottenPasswordOpen] = useState(false);
20
+ const [isRegisterUserOpen, setIsRegisterUserOpen] = useState(false);
21
+
22
+ useEffect(() => {
23
+ // Fetch admin email on component mount
24
+ fetch('/api/admin-email')
25
+ .then(response => response.json())
26
+ .then(data => {
27
+ if (data.adminEmail) {
28
+ setAdminEmail(data.adminEmail);
29
+ }
30
+ })
31
+ .catch(error => {
32
+ console.error('Failed to fetch admin email:', error);
33
+ // Keep default value
34
+ });
35
+ }, []);
16
36
 
17
37
  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
18
38
  event.preventDefault();
@@ -104,6 +124,35 @@ export function LoginForm(props: LoginFormProps) {
104
124
  'Log in'
105
125
  )}
106
126
  </button>
127
+
128
+ <div className="flex justify-between text-sm">
129
+ <button
130
+ type="button"
131
+ onClick={() => setIsForgottenPasswordOpen(true)}
132
+ className="text-promptbook-blue hover:text-promptbook-blue-dark underline focus:outline-none focus:ring-2 focus:ring-promptbook-blue focus:ring-offset-2 rounded-sm"
133
+ >
134
+ Forgotten password?
135
+ </button>
136
+ <button
137
+ type="button"
138
+ onClick={() => setIsRegisterUserOpen(true)}
139
+ className="text-promptbook-blue hover:text-promptbook-blue-dark underline focus:outline-none focus:ring-2 focus:ring-promptbook-blue focus:ring-offset-2 rounded-sm"
140
+ >
141
+ Register new user
142
+ </button>
143
+ </div>
144
+
145
+ <ForgottenPasswordDialog
146
+ isOpen={isForgottenPasswordOpen}
147
+ onClose={() => setIsForgottenPasswordOpen(false)}
148
+ adminEmail={adminEmail}
149
+ />
150
+
151
+ <RegisterUserDialog
152
+ isOpen={isRegisterUserOpen}
153
+ onClose={() => setIsRegisterUserOpen(false)}
154
+ adminEmail={adminEmail}
155
+ />
107
156
  </form>
108
157
  );
109
158
  }
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+
3
+ import { string_book } from '@promptbook-local/types';
4
+ import { X } from 'lucide-react';
5
+ import { useEffect, useState } from 'react';
6
+ import { BookEditor } from '../../../../../src/book-components/BookEditor/BookEditor';
7
+ import { Portal } from '../Portal/Portal';
8
+
9
+ type NewAgentDialogProps = {
10
+ isOpen: boolean;
11
+ onClose: () => void;
12
+ initialAgentSource: string_book;
13
+ onCreate: (agentSource: string_book) => Promise<void>;
14
+ };
15
+
16
+ export function NewAgentDialog(props: NewAgentDialogProps) {
17
+ const { isOpen, onClose, initialAgentSource, onCreate } = props;
18
+ const [agentSource, setAgentSource] = useState(initialAgentSource);
19
+ const [isCreating, setIsCreating] = useState(false);
20
+
21
+ useEffect(() => {
22
+ setAgentSource(initialAgentSource);
23
+ }, [initialAgentSource]);
24
+
25
+ if (!isOpen) {
26
+ return null;
27
+ }
28
+
29
+ const handleCreate = async () => {
30
+ setIsCreating(true);
31
+ try {
32
+ await onCreate(agentSource);
33
+ } finally {
34
+ setIsCreating(false);
35
+ }
36
+ };
37
+
38
+ return (
39
+ <Portal>
40
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 backdrop-blur-sm animate-in fade-in duration-200">
41
+ <div className="relative w-full max-w-4xl h-[80vh] bg-white rounded-lg shadow-lg border border-gray-200 flex flex-col animate-in zoom-in-95 duration-200">
42
+ <div className="flex items-center justify-between p-4 border-b border-gray-200">
43
+ <h2 className="text-xl font-semibold text-gray-900">Create New Agent</h2>
44
+ <button
45
+ onClick={onClose}
46
+ className="text-gray-400 hover:text-gray-500 transition-colors"
47
+ >
48
+ <X className="w-5 h-5" />
49
+ <span className="sr-only">Close</span>
50
+ </button>
51
+ </div>
52
+
53
+ <div className="flex-1 overflow-hidden p-4">
54
+ <BookEditor
55
+ agentSource={agentSource}
56
+ onChange={(source) => setAgentSource(source)}
57
+ height="100%"
58
+ isVerbose={false}
59
+ />
60
+ </div>
61
+
62
+ <div className="flex items-center justify-end gap-3 p-4 border-t border-gray-200 bg-gray-50 rounded-b-lg">
63
+ <button
64
+ onClick={onClose}
65
+ className="px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-md transition-colors"
66
+ >
67
+ Cancel
68
+ </button>
69
+ <button
70
+ onClick={handleCreate}
71
+ disabled={isCreating}
72
+ className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
73
+ >
74
+ {isCreating ? (
75
+ <>
76
+ <span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
77
+ Creating...
78
+ </>
79
+ ) : (
80
+ 'Create Agent'
81
+ )}
82
+ </button>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </Portal>
87
+ );
88
+ }
@@ -1,15 +1,20 @@
1
+ import { HomeIcon } from 'lucide-react';
1
2
  import Link from 'next/link';
2
3
  import { ErrorPage } from '../ErrorPage/ErrorPage';
3
4
 
4
5
  export function NotFoundPage() {
5
6
  return (
6
- <ErrorPage title="404 Not Found" message="The page you are looking for does not exist.">
7
+ <ErrorPage
8
+ title="Agent Not Found :("
9
+ message="The agent you are looking for does not exist, but you can create your own!"
10
+ >
7
11
  <div className="flex justify-center">
8
12
  <Link
9
13
  href="/"
10
14
  className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
11
15
  >
12
- Go Home
16
+ <HomeIcon className="inline w-5 h-5 mr-2" />
17
+ Home
13
18
  </Link>
14
19
  </div>
15
20
  </ErrorPage>
@@ -1,19 +1,28 @@
1
+ import { string_char_emoji } from '@promptbook-local/types';
1
2
  import { DetailedHTMLProps, HTMLAttributes } from 'react';
2
3
 
3
4
  type OpenMojiIconProps = DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
4
- icon: string;
5
+ /**
6
+ * The OpenMoji character to display
7
+ */
8
+ icon: string_char_emoji | string;
9
+
10
+ /**
11
+ * @default 'black'
12
+ */
13
+ variant?: 'black' | 'color';
5
14
  };
6
15
 
7
16
  /**
8
17
  * Renders an emoji using the OpenMoji black and white font
9
18
  */
10
- export function OpenMojiIcon({ icon, className, style, ...rest }: OpenMojiIconProps) {
19
+ export function OpenMojiIcon(props: OpenMojiIconProps) {
20
+ const { icon, variant = 'black', className, style, ...rest } = props;
21
+
22
+ const fontFamily = variant === 'black' ? '"OpenMojiBlack", sans-serif' : '"OpenMojiColor", sans-serif';
23
+
11
24
  return (
12
- <span
13
- className={className}
14
- style={{ ...style, fontFamily: '"OpenMojiBlack", sans-serif' }}
15
- {...rest}
16
- >
25
+ <span className={className} style={{ ...style, fontFamily }} {...rest}>
17
26
  {icon}
18
27
  </span>
19
28
  );
@@ -1,16 +1,16 @@
1
1
  export function PrintHeader({ title }: { title?: string }) {
2
2
  return (
3
- <div className="hidden print:block mb-8 border-b-2 border-blue-600 pb-4">
3
+ <div className="hidden print:block mb-6 border-b-2 border-blue-600 pb-2">
4
4
  <div className="flex justify-between items-end">
5
5
  <div>
6
- <h1 className="text-3xl font-bold text-gray-900 font-poppins">Agents Server</h1>
6
+ <h1 className="text-2xl font-bold text-gray-900 font-poppins">Agents Server</h1>
7
7
  <div className="text-sm text-gray-500 mt-1 flex items-center gap-1">
8
8
  Powered by <span className="font-semibold text-blue-600">Promptbook</span>
9
9
  </div>
10
10
  </div>
11
- {title && <h2 className="text-xl font-semibold text-gray-700">{title}</h2>}
11
+ {title && <h2 className="text-lg font-semibold text-gray-700">{title}</h2>}
12
12
  </div>
13
- <div className="text-xs text-gray-400 mt-2 text-right">
13
+ <div className="text-xs text-gray-400 mt-1 text-right">
14
14
  {new Date().toLocaleDateString()}
15
15
  </div>
16
16
  </div>
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import { X } from 'lucide-react';
4
+ import { Portal } from '../Portal/Portal';
5
+
6
+ type RegisterUserDialogProps = {
7
+ isOpen: boolean;
8
+ onClose: () => void;
9
+ adminEmail: string;
10
+ };
11
+
12
+ export function RegisterUserDialog(props: RegisterUserDialogProps) {
13
+ const { isOpen, onClose, adminEmail } = props;
14
+
15
+ if (!isOpen) {
16
+ return null;
17
+ }
18
+
19
+ return (
20
+ <Portal>
21
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 backdrop-blur-sm animate-in fade-in duration-200">
22
+ <div className="relative w-full max-w-md bg-white rounded-lg shadow-lg border border-gray-200 p-6 animate-in zoom-in-95 duration-200">
23
+ <button
24
+ onClick={onClose}
25
+ className="absolute top-4 right-4 text-gray-400 hover:text-gray-500 transition-colors"
26
+ >
27
+ <X className="w-5 h-5" />
28
+ <span className="sr-only">Close</span>
29
+ </button>
30
+
31
+ <div className="mb-6">
32
+ <h2 className="text-xl font-semibold text-gray-900">Register New User</h2>
33
+ <p className="text-sm text-gray-500 mt-1">Create a new user account</p>
34
+ </div>
35
+
36
+ <div className="space-y-4">
37
+ <div className="p-4 bg-blue-50 border border-blue-200 rounded-md">
38
+ <p className="text-sm text-blue-800">
39
+ This Promptbook server has no email capability. Please contact the administrator at{' '}
40
+ <a
41
+ href={`mailto:${adminEmail}`}
42
+ className="font-medium text-blue-900 underline hover:text-blue-800"
43
+ >
44
+ {adminEmail}
45
+ </a>{' '}
46
+ to register new user.
47
+ </p>
48
+ </div>
49
+
50
+ <button
51
+ onClick={onClose}
52
+ className="w-full inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-gray-100 text-gray-900 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors"
53
+ >
54
+ Close
55
+ </button>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </Portal>
60
+ );
61
+ }
@@ -1,4 +1,5 @@
1
1
  import {
2
+ NEXT_PUBLIC_SITE_URL,
2
3
  NEXT_PUBLIC_VERCEL_BRANCH_URL,
3
4
  NEXT_PUBLIC_VERCEL_ENV,
4
5
  NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN,
@@ -24,6 +25,7 @@ import { TechInfoCard } from '../Homepage/TechInfoCard';
24
25
  export default function VercelDeploymentCard() {
25
26
  return (
26
27
  <TechInfoCard title="Vercel Deployment">
28
+ <p className="text-gray-600">NEXT_PUBLIC_SITE_URL: {NEXT_PUBLIC_SITE_URL?.href || 'undefined'}</p>
27
29
  <p className="text-gray-600">NEXT_PUBLIC_VERCEL_ENV: {NEXT_PUBLIC_VERCEL_ENV}</p>
28
30
  <p className="text-gray-600">NEXT_PUBLIC_VERCEL_TARGET_ENV: {NEXT_PUBLIC_VERCEL_TARGET_ENV}</p>
29
31
  <p className="text-gray-600">NEXT_PUBLIC_VERCEL_URL: {NEXT_PUBLIC_VERCEL_URL}</p>
@@ -1,26 +1,28 @@
1
1
  // Utility to generate content for robots.txt, security.txt, and humans.txt [DRY]
2
2
 
3
- import { NEXT_PUBLIC_SITE_URL } from '@/config';
3
+ import { $provideServer } from '@/src/tools/$provideServer';
4
4
 
5
- // Get base URL from environment or config
6
- const baseUrl = NEXT_PUBLIC_SITE_URL?.href || process.env.PUBLIC_URL || 'https://ptbk.io';
7
-
8
- export function generateRobotsTxt(): string {
9
- return ['User-agent: *', `Sitemap: ${baseUrl}sitemap.xml`, ''].join('\n');
5
+ export async function generateRobotsTxt(): Promise<string> {
6
+ const { publicUrl } = await $provideServer();
7
+ return ['User-agent: *', `Sitemap: ${publicUrl.href}sitemap.xml`, ''].join('\n');
10
8
  }
11
9
 
12
- export function generateSecurityTxt(): string {
10
+ export async function generateSecurityTxt(): Promise<string> {
11
+ const { publicUrl } = await $provideServer();
13
12
  // See https://securitytxt.org/ for more fields
14
13
  return [
15
14
  `Contact: mailto:security@ptbk.io`,
16
15
  `Expires: ${new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]}`,
17
- `Canonical: ${baseUrl}security.txt`,
16
+ `Canonical: ${publicUrl.href}security.txt`,
18
17
  '',
19
18
  ].join('\n');
20
19
  }
21
20
 
22
- export function generateHumansTxt(): string {
23
- return ['/* TEAM */', 'Developer: Promptbook Team', `Site: https://ptbk.io`, `Instance: ${baseUrl}`].join('\n');
21
+ export async function generateHumansTxt(): Promise<string> {
22
+ const { publicUrl } = await $provideServer();
23
+ return ['/* TEAM */', 'Developer: Promptbook Team', `Site: https://ptbk.io`, `Instance: ${publicUrl.href}`].join(
24
+ '\n',
25
+ );
24
26
  }
25
27
 
26
28
  /**
@@ -1,6 +1,6 @@
1
1
  // Utility to append ?headless param if present in current URL
2
- import { usePathname, useSearchParams } from 'next/navigation';
3
2
  import Link, { LinkProps } from 'next/link';
3
+ import { useSearchParams } from 'next/navigation';
4
4
  import { useMemo } from 'react';
5
5
 
6
6
  // Returns true if ?headless is present in current search params
@@ -18,7 +18,11 @@ export function appendHeadlessParam(href: string, isHeadless: boolean): string {
18
18
  }
19
19
 
20
20
  // Custom Link that preserves headless param
21
- export function HeadlessLink({ href, children, ...rest }: LinkProps & { children: React.ReactNode } & React.AnchorHTMLAttributes<HTMLAnchorElement>) {
21
+ export function HeadlessLink({
22
+ href,
23
+ children,
24
+ ...rest
25
+ }: LinkProps & { children: React.ReactNode } & React.AnchorHTMLAttributes<HTMLAnchorElement>) {
22
26
  const isHeadless = useIsHeadless();
23
27
  const finalHref = useMemo(() => appendHeadlessParam(String(href), isHeadless), [href, isHeadless]);
24
28
  return (
@@ -28,7 +32,7 @@ export function HeadlessLink({ href, children, ...rest }: LinkProps & { children
28
32
  );
29
33
  }
30
34
 
31
- import { useRouter } from "next/navigation";
35
+ import { useRouter } from 'next/navigation';
32
36
 
33
37
  // Helper for router.push
34
38
  export function pushWithHeadless(router: ReturnType<typeof useRouter>, href: string, isHeadless: boolean) {
@@ -1,6 +1,6 @@
1
- import { AgentsDatabaseSchema } from '@promptbook-local/types';
2
1
  import { $isRunningInBrowser } from '@promptbook-local/utils';
3
2
  import { createClient, SupabaseClient } from '@supabase/supabase-js';
3
+ import { AgentsServerDatabase } from './schema';
4
4
 
5
5
  /**
6
6
  * Internal cache for `$provideSupabaseForBrowser`
@@ -8,7 +8,7 @@ import { createClient, SupabaseClient } from '@supabase/supabase-js';
8
8
  * @private
9
9
  * @singleton
10
10
  */
11
- let supabase: SupabaseClient<AgentsDatabaseSchema>;
11
+ let supabase: SupabaseClient<AgentsServerDatabase>;
12
12
 
13
13
  /**
14
14
  * Get supabase client
@@ -27,7 +27,7 @@ export function $provideSupabaseForBrowser(): typeof supabase {
27
27
 
28
28
  if (!supabase) {
29
29
  // Create a single supabase client for interacting with your database
30
- supabase = createClient<AgentsDatabaseSchema>(
30
+ supabase = createClient<AgentsServerDatabase>(
31
31
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
32
32
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
33
33
  );
@@ -18,7 +18,7 @@ let supabase: SupabaseClient<AgentsServerDatabase>;
18
18
  *
19
19
  * @returns instance of supabase client
20
20
  */
21
- export function $provideSupabaseForServer(): typeof supabase {
21
+ export function $provideSupabaseForServer(): SupabaseClient<AgentsServerDatabase> {
22
22
  if (!$isRunningInNode()) {
23
23
  throw new Error(
24
24
  'Function `$provideSupabaseForServer` can not be used in browser, use `$provideSupabaseForBrowser` instead.',
@@ -1,6 +1,6 @@
1
- import { AgentsDatabaseSchema } from '@promptbook-local/types';
2
1
  import { $isRunningInWebWorker } from '@promptbook-local/utils';
3
2
  import { createClient, SupabaseClient } from '@supabase/supabase-js';
3
+ import { AgentsServerDatabase } from './schema';
4
4
 
5
5
  /**
6
6
  * Internal cache for `$provideSupabaseForWorker`
@@ -8,7 +8,7 @@ import { createClient, SupabaseClient } from '@supabase/supabase-js';
8
8
  * @private
9
9
  * @singleton
10
10
  */
11
- let supabase: SupabaseClient<AgentsDatabaseSchema>;
11
+ let supabase: SupabaseClient<AgentsServerDatabase>;
12
12
 
13
13
  /**
14
14
  * Get supabase client
@@ -27,7 +27,7 @@ export function $provideSupabaseForWorker(): typeof supabase {
27
27
 
28
28
  if (!supabase) {
29
29
  // Create a single supabase client for interacting with your database
30
- supabase = createClient<AgentsDatabaseSchema>(
30
+ supabase = createClient<AgentsServerDatabase>(
31
31
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
32
32
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
33
33
  );
@@ -38,7 +38,13 @@ export const metadataDefaults = [
38
38
  type: 'TEXT',
39
39
  },
40
40
  {
41
- key: 'IS_VOICE_CALLING_ENABLED',
41
+ key: 'SHOW_FEDERATED_SERVERS_PUBLICLY',
42
+ value: 'false',
43
+ note: 'Whether to show federated servers and their agents to anonymous users. When false, federated servers are only visible to authenticated users.',
44
+ type: 'BOOLEAN',
45
+ },
46
+ {
47
+ key: 'IS_EXPERIMENTAL_VOICE_CALLING_ENABLED',
42
48
  value: 'false',
43
49
  note: 'Enable or disable voice calling features for agents. When disabled, voice API endpoints will return 403 Forbidden.',
44
50
  type: 'BOOLEAN',
@@ -67,6 +73,18 @@ export const metadataDefaults = [
67
73
  note: 'Language for generating new agent names. Possible values: ENGLISH, CZECH.',
68
74
  type: 'TEXT_SINGLE_LINE',
69
75
  },
76
+ {
77
+ key: 'ADMIN_EMAIL',
78
+ value: 'support@ptbk.io',
79
+ note: 'Administrator email address used for password reset and user registration requests.',
80
+ type: 'TEXT_SINGLE_LINE',
81
+ },
82
+ {
83
+ key: 'DEFAULT_AGENT_VISIBILITY',
84
+ value: 'PRIVATE',
85
+ note: 'Default visibility for new agents. Can be PUBLIC or PRIVATE.',
86
+ type: 'TEXT_SINGLE_LINE',
87
+ },
70
88
  ] as const satisfies ReadonlyArray<{
71
89
  key: string;
72
90
  value: string;