@promptbook/cli 0.104.0-0 → 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 (204) 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/WebsiteIntegrationTabs.tsx +26 -0
  48. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +32 -8
  49. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
  50. package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
  51. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
  52. package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
  53. package/apps/agents-server/src/app/agents/[agentName]/website-integration/page.tsx +35 -18
  54. package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
  55. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
  56. package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
  57. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
  58. package/apps/agents-server/src/app/api/agents/route.ts +28 -3
  59. package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
  60. package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
  61. package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
  62. package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
  63. package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
  64. package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
  65. package/apps/agents-server/src/app/api/embed.js/route.ts +87 -67
  66. package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
  67. package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
  68. package/apps/agents-server/src/app/api/messages/route.ts +102 -0
  69. package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
  70. package/apps/agents-server/src/app/api/upload/route.ts +128 -45
  71. package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
  72. package/apps/agents-server/src/app/docs/page.tsx +12 -12
  73. package/apps/agents-server/src/app/embed/layout.tsx +31 -0
  74. package/apps/agents-server/src/app/embed/page.tsx +22 -9
  75. package/apps/agents-server/src/app/globals.css +140 -33
  76. package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
  77. package/apps/agents-server/src/app/layout.tsx +27 -22
  78. package/apps/agents-server/src/app/page.tsx +54 -6
  79. package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
  80. package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
  81. package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
  82. package/apps/agents-server/src/app/security.txt/route.ts +1 -1
  83. package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
  84. package/apps/agents-server/src/app/swagger/page.tsx +14 -0
  85. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +41 -116
  86. package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
  87. package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
  88. package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
  89. package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
  90. package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
  91. package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
  92. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
  93. package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
  94. package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
  95. package/apps/agents-server/src/components/Header/Header.tsx +114 -40
  96. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +145 -23
  97. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
  98. package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
  99. package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
  100. package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
  101. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
  102. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
  103. package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
  104. package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
  105. package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
  106. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
  107. package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
  108. package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
  109. package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
  110. package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
  111. package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
  112. package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
  113. package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
  114. package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
  115. package/apps/agents-server/src/database/migrate.ts +34 -1
  116. package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
  117. package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
  118. package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
  119. package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
  120. package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
  121. package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
  122. package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
  123. package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
  124. package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
  125. package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
  126. package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
  127. package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
  128. package/apps/agents-server/src/database/schema.ts +231 -4
  129. package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
  130. package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
  131. package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
  132. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
  133. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
  134. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
  135. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
  136. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
  137. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
  138. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
  139. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
  140. package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
  141. package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
  142. package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
  143. package/apps/agents-server/src/message-providers/index.ts +13 -0
  144. package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
  145. package/apps/agents-server/src/middleware.ts +19 -23
  146. package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
  147. package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
  148. package/apps/agents-server/src/utils/auth.ts +117 -17
  149. package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
  150. package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
  151. package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
  152. package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
  153. package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
  154. package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
  155. package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
  156. package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
  157. package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
  158. package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
  159. package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
  160. package/esm/index.es.js +2890 -2737
  161. package/esm/index.es.js.map +1 -1
  162. package/esm/typings/servers.d.ts +8 -0
  163. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  164. package/esm/typings/src/_packages/types.index.d.ts +10 -2
  165. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
  166. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
  167. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
  168. package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
  169. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
  170. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  171. package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
  172. package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  173. package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
  174. package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
  175. package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
  176. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +21 -11
  177. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +80 -14
  178. package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
  179. package/esm/typings/src/commitments/index.d.ts +2 -1
  180. package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
  181. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
  182. package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
  183. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
  184. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
  185. package/esm/typings/src/types/Message.d.ts +49 -0
  186. package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
  187. package/esm/typings/src/types/typeAliases.d.ts +23 -1
  188. package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
  189. package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
  190. package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
  191. package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
  192. package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
  193. package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
  194. package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
  195. package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
  196. package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
  197. package/esm/typings/src/version.d.ts +1 -1
  198. package/package.json +1 -1
  199. package/umd/index.umd.js +4018 -3865
  200. package/umd/index.umd.js.map +1 -1
  201. package/apps/agents-server/package-lock.json +0 -27
  202. package/apps/agents-server/public/fonts/download-font.js +0 -22
  203. package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
  204. package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
@@ -7,8 +7,9 @@ import { parseAgentSource } from '@promptbook-local/core';
7
7
  import { headers } from 'next/headers';
8
8
  import spaceTrim from 'spacetrim';
9
9
  import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
10
- import { CodePreview } from '../../../../../../_common/components/CodePreview/CodePreview';
10
+ import { just } from '../../../../../../../src/utils/organization/just';
11
11
  import { generateAgentMetadata } from '../generateAgentMetadata';
12
+ import { WebsiteIntegrationTabs } from '../integration/WebsiteIntegrationTabs';
12
13
 
13
14
  export const generateMetadata = generateAgentMetadata;
14
15
 
@@ -24,20 +25,31 @@ export default async function WebsiteIntegrationAgentPage({ params }: { params:
24
25
  const { publicUrl } = await $provideServer();
25
26
  const agentUrl = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
26
27
 
27
- const code = spaceTrim(
28
+ const reactCode = spaceTrim(
28
29
  (block) => `
29
-
30
- import { PromptbookAgent } from '@promptbook/components';
30
+ import { PromptbookAgentIntegration } from '@promptbook/components';
31
31
 
32
32
  export function YourComponent() {
33
33
  return(
34
- <PromptbookAgent
34
+ <PromptbookAgentIntegration
35
35
  agentUrl="${agentUrl}"
36
36
  meta={${block(JSON.stringify({ fullname, color, image, ...restMeta }, null, 4))}}
37
37
  />
38
38
  );
39
39
  }
40
-
40
+ `,
41
+ );
42
+
43
+ // HTML Integration Code - use single quotes for meta attribute to allow JSON with double quotes inside
44
+ const metaJsonString = JSON.stringify({ fullname, color, image, ...restMeta }, null, 4);
45
+ const htmlCode = spaceTrim(
46
+ (block) => `
47
+ <script src="${publicUrl.href}api/embed.js" async defer></script>
48
+
49
+ <promptbook-agent-integration
50
+ agent-url="${agentUrl}"
51
+ meta='${block(metaJsonString)}'
52
+ />
41
53
  `,
42
54
  );
43
55
 
@@ -49,19 +61,24 @@ export default async function WebsiteIntegrationAgentPage({ params }: { params:
49
61
  React application using the <code>{'<PromptbookAgent />'}</code> component.
50
62
  </p>
51
63
 
52
- <CodePreview code={code} />
53
- <PromptbookAgentIntegration
54
- // formfactor="profile"
55
- agentUrl={agentUrl}
56
- meta={meta}
57
- style={
58
- {
59
- // width: '400px',
60
- // height: '600px',
61
- // outline: `2px solid red`
64
+ <WebsiteIntegrationTabs reactCode={reactCode} htmlCode={htmlCode} />
65
+ {just(false) && (
66
+ <PromptbookAgentIntegration
67
+ // formfactor="profile"
68
+ agentUrl={agentUrl}
69
+ meta={meta}
70
+ style={
71
+ {
72
+ // width: '400px',
73
+ // height: '600px',
74
+ // outline: `2px solid red`
75
+ }
62
76
  }
63
- }
64
- />
77
+ />
78
+ )}
79
+ {htmlCode}
80
+ {just(true) && <div dangerouslySetInnerHTML={{ __html: htmlCode }} />}
81
+ {just(true) && <div dangerouslySetInnerHTML={{ __html: `<h1>Test</h1>` }} />}
65
82
  </main>
66
83
  );
67
84
  }
@@ -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
+ }
@@ -1,14 +1,56 @@
1
1
  // DELETE /api/agents/[agentName]
2
+ // PATCH /api/agents/[agentName] - update agent visibility
3
+ // POST /api/agents/[agentName]/restore - restore deleted agent
4
+ import { $getTableName } from '@/src/database/$getTableName';
5
+ import { $provideSupabaseForServer } from '@/src/database/$provideSupabaseForServer';
2
6
  import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
3
7
  import { TODO_any } from '@promptbook-local/types';
4
8
  import { NextResponse } from 'next/server';
5
9
 
10
+ export async function PATCH(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
11
+ const { agentName } = await params;
12
+
13
+ try {
14
+ const body = await request.json();
15
+ const { visibility }: { visibility: 'PUBLIC' | 'PRIVATE' } = body;
16
+
17
+ if (!visibility || !['PUBLIC', 'PRIVATE'].includes(visibility)) {
18
+ return NextResponse.json(
19
+ { success: false, error: 'Invalid visibility value. Must be PUBLIC or PRIVATE.' },
20
+ { status: 400 },
21
+ );
22
+ }
23
+
24
+ const supabase = $provideSupabaseForServer();
25
+ // const { tablePrefix } = await $provideServer();
26
+
27
+ const updateResult = await supabase
28
+ .from(await $getTableName(`Agent`))
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ .update({ visibility } as any)
31
+ .or(`agentName.eq.${agentName},permanentId.eq.${agentName}`)
32
+ .is('deletedAt', null);
33
+
34
+ if (updateResult.error) {
35
+ return NextResponse.json({ success: false, error: updateResult.error.message }, { status: 500 });
36
+ }
37
+
38
+ return NextResponse.json({ success: true });
39
+ } catch (error) {
40
+ return NextResponse.json(
41
+ { success: false, error: (error as TODO_any)?.message || 'Failed to update agent visibility' },
42
+ { status: 500 },
43
+ );
44
+ }
45
+ }
46
+
6
47
  export async function DELETE(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
7
48
  const { agentName } = await params;
8
49
  const collection = await $provideAgentCollectionForServer();
9
50
 
10
51
  try {
11
- await collection.deleteAgent(agentName);
52
+ const agentId = await collection.getAgentPermanentId(agentName);
53
+ await collection.deleteAgent(agentId);
12
54
  return NextResponse.json({ success: true });
13
55
  } catch (error) {
14
56
  return NextResponse.json(
@@ -1,5 +1,7 @@
1
+ import { $getTableName } from '@/src/database/$getTableName';
1
2
  import { $provideServer } from '@/src/tools/$provideServer';
2
3
  import { NextResponse } from 'next/server';
4
+ import { $provideSupabaseForServer } from '../../../database/$provideSupabaseForServer';
3
5
  import { $provideAgentCollectionForServer } from '../../../tools/$provideAgentCollectionForServer';
4
6
  import { getFederatedServersFromMetadata } from '../../../utils/getFederatedServersFromMetadata';
5
7
 
@@ -8,13 +10,36 @@ export const dynamic = 'force-dynamic';
8
10
  export async function GET() {
9
11
  try {
10
12
  const collection = await $provideAgentCollectionForServer();
11
- const agents = await collection.listAgents();
13
+ const allAgents = await collection.listAgents();
12
14
  const federatedServers = await getFederatedServersFromMetadata();
13
15
  const { publicUrl } = await $provideServer();
14
16
 
15
- const agentsWithUrl = agents.map((agent) => ({
17
+ // Filter to only include PUBLIC agents for federated API
18
+ const supabase = $provideSupabaseForServer();
19
+ const visibilityResult = await supabase
20
+ .from(await $getTableName(`Agent`))
21
+ .select('agentName, visibility')
22
+ .is('deletedAt', null);
23
+
24
+ let publicAgents = allAgents;
25
+ if (!visibilityResult.error) {
26
+ const visibilityMap = new Map(
27
+ visibilityResult.data.map((item: { agentName: string; visibility: 'PUBLIC' | 'PRIVATE' }) => [
28
+ item.agentName,
29
+ item.visibility,
30
+ ]),
31
+ );
32
+
33
+ // Only include PUBLIC agents in federated API
34
+ publicAgents = allAgents.filter((agent) => {
35
+ const visibility = visibilityMap.get(agent.agentName);
36
+ return visibility === 'PUBLIC';
37
+ });
38
+ }
39
+
40
+ const agentsWithUrl = publicAgents.map((agent) => ({
16
41
  ...agent,
17
- url: `${publicUrl.href}agents/${encodeURIComponent(agent.agentName)}`,
42
+ url: `${publicUrl.href}agents/${encodeURIComponent(agent.permanentId || agent.agentName)}`,
18
43
  }));
19
44
 
20
45
  const response = NextResponse.json({
@@ -1,10 +1,13 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { keepUnused } from '../../../../../../src/utils/organization/keepUnused';
1
4
  import { $getTableName } from '../../../database/$getTableName';
2
5
  import { $provideSupabase } from '../../../database/$provideSupabase';
3
6
  import { isUserAdmin } from '../../../utils/isUserAdmin';
4
- import { randomUUID } from 'crypto';
5
- import { NextRequest, NextResponse } from 'next/server';
6
7
 
7
8
  export async function GET(request: NextRequest) {
9
+ keepUnused(request);
10
+
8
11
  if (!(await isUserAdmin())) {
9
12
  return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
10
13
  }
@@ -35,11 +38,7 @@ export async function POST(request: NextRequest) {
35
38
  const supabase = $provideSupabase();
36
39
  const table = await $getTableName('ApiTokens');
37
40
 
38
- const { data, error } = await supabase
39
- .from(table)
40
- .insert({ token, note })
41
- .select()
42
- .single();
41
+ const { data, error } = await supabase.from(table).insert({ token, note }).select().single();
43
42
 
44
43
  if (error) {
45
44
  return NextResponse.json({ error: error.message }, { status: 500 });
@@ -0,0 +1,141 @@
1
+ import { $provideBrowserForServer } from '@/src/tools/$provideBrowserForServer';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { OpenAI } from 'openai';
4
+
5
+ export const maxDuration = 60; // Allow longer timeout for agent actions
6
+
7
+ export async function POST(request: NextRequest) {
8
+ try {
9
+ const { goal, action, plan } = await request.json();
10
+
11
+ if (!goal) {
12
+ return NextResponse.json({ error: 'Goal is required' }, { status: 400 });
13
+ }
14
+
15
+ const browserContext = await $provideBrowserForServer();
16
+ const pages = browserContext.pages();
17
+ let page = pages[0];
18
+
19
+ if (!page) {
20
+ page = await browserContext.newPage();
21
+ await page.goto('https://www.facebook.com/');
22
+ }
23
+
24
+ // Ensure we are on Facebook
25
+ if (!page.url().includes('facebook.com')) {
26
+ await page.goto('https://www.facebook.com/');
27
+ }
28
+
29
+ const openai = new OpenAI({
30
+ apiKey: process.env.OPENAI_API_KEY,
31
+ });
32
+
33
+ // Get page context (simplified for now: just text content)
34
+ // In a real scenario, we would use accessibility tree or screenshots
35
+ const pageText = await page.evaluate(() => document.body.innerText.slice(0, 10000)); // Limit text size
36
+
37
+ if (action === 'plan') {
38
+ const systemPrompt = `
39
+ You are an autonomous agent interacting with Facebook.
40
+ Your goal is to help the user achieve: "${goal}".
41
+ Analyze the current page content and propose a step-by-step plan.
42
+ Keep the plan concise and actionable.
43
+ Do not execute any actions yet, just list them.
44
+ `;
45
+
46
+ const completion = await openai.chat.completions.create({
47
+ model: 'gpt-4o',
48
+ messages: [
49
+ { role: 'system', content: systemPrompt },
50
+ { role: 'user', content: `Current page text (truncated):\n${pageText}` },
51
+ ],
52
+ });
53
+
54
+ const plan = completion.choices[0].message.content;
55
+ return NextResponse.json({ plan });
56
+ } else if (action === 'execute') {
57
+ // This is a simplified execution. Ideally, this would be a loop.
58
+ // We ask the LLM to give us a JSON of actions to perform immediately.
59
+
60
+ const systemPrompt = `
61
+ You are an autonomous agent interacting with Facebook.
62
+ Your goal is to help the user achieve: "${goal}".
63
+ ${plan ? `You have previously agreed to this plan:\n${plan}\n\n` : ''}
64
+ Based on the current page content, generate a list of low-level actions to perform NOW to advance the plan.
65
+
66
+ Supported actions:
67
+ - { "type": "click", "selector": "css selector", "description": "reason" }
68
+ - { "type": "type", "selector": "css selector", "text": "text to type", "description": "reason" }
69
+ - { "type": "scroll", "amount": number, "description": "reason" }
70
+ - { "type": "wait", "ms": number, "description": "reason" }
71
+ - { "type": "navigate", "url": "url", "description": "reason" }
72
+
73
+ Return a JSON object with a key "actions" containing an array of these actions.
74
+ Example: { "actions": [{ "type": "scroll", "amount": 500, "description": "scanning feed" }, { "type": "click", "selector": "...", "description": "liking post" }] }
75
+
76
+ IMPORTANT: Return ONLY valid JSON.
77
+ `;
78
+
79
+ const completion = await openai.chat.completions.create({
80
+ model: 'gpt-4o',
81
+ messages: [
82
+ { role: 'system', content: systemPrompt },
83
+ { role: 'user', content: `Current page text (truncated):\n${pageText}` },
84
+ ],
85
+ response_format: { type: 'json_object' },
86
+ });
87
+
88
+ const responseContent = completion.choices[0].message.content;
89
+ if (!responseContent) {
90
+ throw new Error('No response from AI');
91
+ }
92
+
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ const { actions } = JSON.parse(responseContent) as { actions: any[] };
95
+ const results = [];
96
+
97
+ for (const act of actions) {
98
+ console.log('Executing action:', act);
99
+ try {
100
+ if (act.type === 'click') {
101
+ // Try to find element and click
102
+ // We might need to use evaluate if selector is complex or text-based, but let's stick to CSS for now
103
+ // Or ask LLM to give text-based selector?
104
+ // Playwright's locator is powerful.
105
+ // Let's assume standard CSS selectors for now.
106
+ if (act.selector) {
107
+ // Try to wait for it briefly
108
+ await page.waitForSelector(act.selector, { timeout: 2000 });
109
+ await page.click(act.selector);
110
+ }
111
+ } else if (act.type === 'type') {
112
+ if (act.selector && act.text) {
113
+ await page.waitForSelector(act.selector, { timeout: 2000 });
114
+ await page.type(act.selector, act.text);
115
+ }
116
+ } else if (act.type === 'scroll') {
117
+ await page.evaluate((y) => window.scrollBy(0, y), act.amount || 500);
118
+ } else if (act.type === 'wait') {
119
+ await page.waitForTimeout(act.ms || 1000);
120
+ } else if (act.type === 'navigate') {
121
+ if (act.url) await page.goto(act.url);
122
+ }
123
+ results.push({ action: act, status: 'success' });
124
+ } catch (error) {
125
+ console.error('Action failed:', act, error);
126
+ results.push({ action: act, status: 'error', error: String(error) });
127
+ }
128
+ // Small delay between actions
129
+ await page.waitForTimeout(500);
130
+ }
131
+
132
+ return NextResponse.json({ success: true, results });
133
+ }
134
+
135
+ return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
136
+
137
+ } catch (error) {
138
+ console.error('Error in Act on Facebook:', error);
139
+ return NextResponse.json({ error: String(error) }, { status: 500 });
140
+ }
141
+ }
@@ -0,0 +1,30 @@
1
+ import { $provideBrowserForServer } from '@/src/tools/$provideBrowserForServer';
2
+ import { serializeError } from '@promptbook-local/utils';
3
+ import { NextResponse } from 'next/server';
4
+ import { assertsError } from '../../../../../../../src/errors/assertsError';
5
+
6
+ export async function GET() {
7
+ try {
8
+ const browserContext = await $provideBrowserForServer();
9
+
10
+ const page = await browserContext.newPage();
11
+
12
+ await page.goto('https://ptbk.io');
13
+ const screenshotBuffer = await page.screenshot();
14
+
15
+ // await page.close();
16
+ // Do not close browser
17
+ // <- TODO: !!!! Fix
18
+
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ return new NextResponse(new Blob([screenshotBuffer as any]), {
21
+ headers: {
22
+ 'Content-Type': 'image/png',
23
+ },
24
+ });
25
+ } catch (error) {
26
+ assertsError(error);
27
+ console.error('Error taking screenshot:', error);
28
+ return NextResponse.json({ error: serializeError(error) }, { status: 500 });
29
+ }
30
+ }
@@ -0,0 +1,62 @@
1
+ import { $provideBrowserForServer } from '@/src/tools/$provideBrowserForServer';
2
+ import { NextResponse } from 'next/server';
3
+ import { just } from '../../../../../../../src/utils/organization/just';
4
+
5
+ export const dynamic = 'force-dynamic';
6
+
7
+ export async function GET() {
8
+ const encoder = new TextEncoder();
9
+ const stream = new ReadableStream({
10
+ async start(controller) {
11
+ try {
12
+ console.log('Starting Facebook scroll session');
13
+ const browserContext = await $provideBrowserForServer();
14
+ const page = await browserContext.newPage();
15
+
16
+ await page.goto('https://www.facebook.com/');
17
+ console.log('Navigated to Facebook');
18
+
19
+ // Loop for streaming
20
+ while (just(true)) {
21
+ // Check for login (simple check for now)
22
+ // Facebook uses role="banner" for the top navigation bar when logged in
23
+ const isLoggedIn = await page.$('div[role="banner"]');
24
+
25
+ if (isLoggedIn) {
26
+ // Scroll down
27
+ await page.evaluate(() => window.scrollBy(0, 500));
28
+ // console.log('Scrolled');
29
+ } else {
30
+ // console.log('Waiting for login...');
31
+ }
32
+
33
+ const buffer = await page.screenshot({ type: 'jpeg', quality: 50 });
34
+
35
+ const boundary = '--myboundary';
36
+ const header = `\r\n${boundary}\r\nContent-Type: image/jpeg\r\nContent-Length: ${buffer.length}\r\n\r\n`;
37
+
38
+ controller.enqueue(encoder.encode(header));
39
+ controller.enqueue(buffer);
40
+
41
+ // Short delay
42
+ await new Promise((r) => setTimeout(r, 200));
43
+ }
44
+ } catch (error) {
45
+ console.error('Stream error:', error);
46
+ try {
47
+ controller.close();
48
+ } catch (e) {
49
+ // Ignore error if controller is already closed
50
+ }
51
+ }
52
+ },
53
+ });
54
+
55
+ return new NextResponse(stream, {
56
+ headers: {
57
+ 'Content-Type': 'multipart/x-mixed-replace; boundary=myboundary',
58
+ 'Cache-Control': 'no-cache',
59
+ Connection: 'keep-alive',
60
+ },
61
+ });
62
+ }
@@ -0,0 +1,61 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import spaceTrim from 'spacetrim';
3
+ import { getGroupedCommitmentDefinitions } from '../../../../../../../src/commitments';
4
+ import { keepUnused } from '../../../../../../../src/utils/organization/keepUnused';
5
+
6
+ export const dynamic = 'force-static';
7
+
8
+ export async function GET(request: NextRequest) {
9
+ keepUnused(request);
10
+
11
+ const groupedCommitments = getGroupedCommitmentDefinitions();
12
+
13
+ const content = spaceTrim(
14
+ (block) => `
15
+ # Promptbook Documentation
16
+
17
+ Promptbook is a language for defining AI agents. It is based on Markdown and uses a set of commitments to define the behavior of the agent.
18
+
19
+ ## Commitments
20
+
21
+ The following commands (commitments) are available in Promptbook:
22
+
23
+ ${block(
24
+ groupedCommitments
25
+ .map(({ primary, aliases }) => {
26
+ const title = primary.type;
27
+ const description = primary.description;
28
+ const documentation = primary.documentation;
29
+ const aliasList = aliases.length > 0 ? `**Aliases:** ${aliases.join(', ')}` : '';
30
+
31
+ return spaceTrim(
32
+ (block) => `
33
+ ### ${title}
34
+
35
+ ${description}
36
+
37
+ ${aliasList}
38
+
39
+ #### Usage
40
+
41
+ ${block(getSafeCodeBlock(documentation))}
42
+ `,
43
+ );
44
+ })
45
+ .join('\n\n'),
46
+ )}
47
+ `,
48
+ );
49
+
50
+ return new NextResponse(content, {
51
+ headers: {
52
+ 'Content-Type': 'text/markdown; charset=utf-8',
53
+ },
54
+ });
55
+ }
56
+
57
+ function getSafeCodeBlock(content: string, lang = 'markdown'): string {
58
+ const maxBackticks = Math.max(0, ...(content.match(/`+/g) || []).map((m) => m.length));
59
+ const fence = '`'.repeat(Math.max(3, maxBackticks + 1));
60
+ return `${fence}${lang}\n${content}\n${fence}`;
61
+ }
@@ -0,0 +1,48 @@
1
+ import { $getTableName } from '../../../../../database/$getTableName';
2
+ import { $provideSupabaseForServer } from '../../../../../database/$provideSupabaseForServer';
3
+ import { parseInboundSendgridEmail } from '../../../../../message-providers/email/sendgrid/parseInboundSendgridEmail';
4
+ import { NextRequest, NextResponse } from 'next/server';
5
+
6
+ export async function POST(request: NextRequest) {
7
+ try {
8
+ const formData = await request.formData();
9
+ const rawEmail = formData.get('email');
10
+
11
+ if (typeof rawEmail !== 'string') {
12
+ return NextResponse.json({ error: 'Missing email field' }, { status: 400 });
13
+ }
14
+
15
+ const email = await parseInboundSendgridEmail(rawEmail);
16
+
17
+ const supabase = await $provideSupabaseForServer();
18
+ const { error } = await supabase
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ .from(await $getTableName('Message'))
21
+ .insert({
22
+ channel: 'EMAIL',
23
+ direction: 'INBOUND',
24
+ sender: email.sender,
25
+ recipients: email.recipients,
26
+ content: email.content,
27
+ metadata: {
28
+ subject: email.subject,
29
+ cc: email.cc,
30
+ ...email.metadata,
31
+ },
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ } as any);
34
+
35
+ if (error) {
36
+ console.error('Failed to insert message', error);
37
+ return NextResponse.json({ error: error.message }, { status: 500 });
38
+ }
39
+
40
+ return NextResponse.json({ success: true });
41
+ } catch (error) {
42
+ console.error('Error processing inbound email', error);
43
+ return NextResponse.json(
44
+ { error: error instanceof Error ? error.message : 'Unknown error' },
45
+ { status: 500 },
46
+ );
47
+ }
48
+ }