@promptbook/cli 0.103.0-52 → 0.103.0-54

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 (142) hide show
  1. package/apps/agents-server/README.md +1 -1
  2. package/apps/agents-server/config.ts +3 -3
  3. package/apps/agents-server/next.config.ts +1 -1
  4. package/apps/agents-server/public/sw.js +16 -0
  5. package/apps/agents-server/src/app/AddAgentButton.tsx +24 -4
  6. package/apps/agents-server/src/app/actions.ts +15 -13
  7. package/apps/agents-server/src/app/admin/api-tokens/ApiTokensClient.tsx +186 -0
  8. package/apps/agents-server/src/app/admin/api-tokens/page.tsx +13 -0
  9. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +541 -0
  10. package/apps/agents-server/src/app/admin/chat-feedback/page.tsx +22 -0
  11. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +532 -0
  12. package/apps/agents-server/src/app/admin/chat-history/page.tsx +21 -0
  13. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +241 -27
  14. package/apps/agents-server/src/app/admin/models/page.tsx +22 -0
  15. package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +131 -0
  16. package/apps/agents-server/src/app/admin/users/[userId]/page.tsx +21 -0
  17. package/apps/agents-server/src/app/admin/users/page.tsx +18 -0
  18. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +10 -2
  19. package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatFeedbackButton.tsx +63 -0
  20. package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatHistoryButton.tsx +63 -0
  21. package/apps/agents-server/src/app/agents/[agentName]/CloneAgentButton.tsx +41 -0
  22. package/apps/agents-server/src/app/agents/[agentName]/InstallPwaButton.tsx +74 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/ServiceWorkerRegister.tsx +24 -0
  24. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +19 -0
  25. package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +67 -0
  26. package/apps/agents-server/src/app/agents/[agentName]/api/openai/chat/completions/route.ts +176 -0
  27. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +3 -0
  28. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +177 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +3 -0
  30. package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +53 -1
  31. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +11 -11
  32. package/apps/agents-server/src/app/agents/[agentName]/history/RestoreVersionButton.tsx +46 -0
  33. package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +12 -0
  34. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +62 -0
  35. package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +80 -0
  36. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +92 -0
  37. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +92 -0
  38. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +61 -0
  39. package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +102 -0
  40. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +64 -24
  41. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +47 -0
  42. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +19 -0
  43. package/apps/agents-server/src/app/api/agents/route.ts +22 -13
  44. package/apps/agents-server/src/app/api/api-tokens/route.ts +76 -0
  45. package/apps/agents-server/src/app/api/auth/login/route.ts +6 -44
  46. package/apps/agents-server/src/app/api/chat-feedback/[id]/route.ts +38 -0
  47. package/apps/agents-server/src/app/api/chat-feedback/route.ts +157 -0
  48. package/apps/agents-server/src/app/api/chat-history/[id]/route.ts +37 -0
  49. package/apps/agents-server/src/app/api/chat-history/route.ts +147 -0
  50. package/apps/agents-server/src/app/api/federated-agents/route.ts +17 -0
  51. package/apps/agents-server/src/app/api/upload/route.ts +9 -1
  52. package/apps/agents-server/src/app/docs/[docId]/page.tsx +63 -0
  53. package/apps/agents-server/src/app/docs/page.tsx +34 -0
  54. package/apps/agents-server/src/app/layout.tsx +29 -3
  55. package/apps/agents-server/src/app/manifest.ts +109 -0
  56. package/apps/agents-server/src/app/page.tsx +8 -45
  57. package/apps/agents-server/src/app/recycle-bin/RestoreAgentButton.tsx +40 -0
  58. package/apps/agents-server/src/app/recycle-bin/actions.ts +27 -0
  59. package/apps/agents-server/src/app/recycle-bin/page.tsx +58 -0
  60. package/apps/agents-server/src/app/restricted/page.tsx +33 -0
  61. package/apps/agents-server/src/app/test/og-image/README.md +1 -0
  62. package/apps/agents-server/src/app/test/og-image/opengraph-image.tsx +37 -0
  63. package/apps/agents-server/src/app/test/og-image/page.tsx +22 -0
  64. package/apps/agents-server/src/components/Footer/Footer.tsx +175 -0
  65. package/apps/agents-server/src/components/Header/Header.tsx +450 -79
  66. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +46 -14
  67. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +58 -0
  68. package/apps/agents-server/src/components/Homepage/Card.tsx +1 -1
  69. package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +21 -0
  70. package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +183 -0
  71. package/apps/agents-server/src/components/Homepage/ModelsSection.tsx +75 -0
  72. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +29 -3
  73. package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +18 -17
  74. package/apps/agents-server/src/components/Portal/Portal.tsx +38 -0
  75. package/apps/agents-server/src/components/UsersList/UsersList.tsx +82 -131
  76. package/apps/agents-server/src/components/UsersList/useUsersAdmin.ts +139 -0
  77. package/apps/agents-server/src/database/metadataDefaults.ts +38 -6
  78. package/apps/agents-server/src/database/migrations/2025-12-0010-llm-cache.sql +12 -0
  79. package/apps/agents-server/src/database/migrations/2025-12-0060-api-tokens.sql +13 -0
  80. package/apps/agents-server/src/database/schema.ts +51 -0
  81. package/apps/agents-server/src/middleware.ts +193 -92
  82. package/apps/agents-server/src/tools/$provideCdnForServer.ts +3 -7
  83. package/apps/agents-server/src/tools/$provideExecutionToolsForServer.ts +10 -1
  84. package/apps/agents-server/src/tools/$provideServer.ts +2 -2
  85. package/apps/agents-server/src/utils/authenticateUser.ts +42 -0
  86. package/apps/agents-server/src/utils/cache/SupabaseCacheStorage.ts +55 -0
  87. package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +63 -0
  88. package/apps/agents-server/src/utils/chatFeedbackAdmin.ts +96 -0
  89. package/apps/agents-server/src/utils/chatHistoryAdmin.ts +96 -0
  90. package/apps/agents-server/src/utils/getEffectiveFederatedServers.ts +22 -0
  91. package/apps/agents-server/src/utils/getFederatedAgents.ts +31 -8
  92. package/apps/agents-server/src/utils/getFederatedServersFromMetadata.ts +10 -0
  93. package/apps/agents-server/src/utils/getVisibleCommitmentDefinitions.ts +12 -0
  94. package/apps/agents-server/src/utils/isUserAdmin.ts +2 -2
  95. package/apps/agents-server/vercel.json +7 -0
  96. package/esm/index.es.js +279 -2
  97. package/esm/index.es.js.map +1 -1
  98. package/esm/typings/servers.d.ts +8 -1
  99. package/esm/typings/src/_packages/components.index.d.ts +2 -0
  100. package/esm/typings/src/_packages/core.index.d.ts +6 -0
  101. package/esm/typings/src/_packages/types.index.d.ts +2 -0
  102. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  103. package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +7 -0
  104. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +4 -0
  105. package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +12 -0
  106. package/esm/typings/src/book-components/icons/MicIcon.d.ts +8 -0
  107. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +17 -0
  108. package/esm/typings/src/commitments/ACTION/ACTION.d.ts +4 -0
  109. package/esm/typings/src/commitments/DELETE/DELETE.d.ts +4 -0
  110. package/esm/typings/src/commitments/FORMAT/FORMAT.d.ts +4 -0
  111. package/esm/typings/src/commitments/GOAL/GOAL.d.ts +4 -0
  112. package/esm/typings/src/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +4 -0
  113. package/esm/typings/src/commitments/MEMORY/MEMORY.d.ts +4 -0
  114. package/esm/typings/src/commitments/MESSAGE/AgentMessageCommitmentDefinition.d.ts +32 -0
  115. package/esm/typings/src/commitments/MESSAGE/InitialMessageCommitmentDefinition.d.ts +4 -0
  116. package/esm/typings/src/commitments/MESSAGE/MESSAGE.d.ts +4 -0
  117. package/esm/typings/src/commitments/MESSAGE/UserMessageCommitmentDefinition.d.ts +32 -0
  118. package/esm/typings/src/commitments/META/META.d.ts +4 -0
  119. package/esm/typings/src/commitments/META_COLOR/META_COLOR.d.ts +4 -0
  120. package/esm/typings/src/commitments/META_IMAGE/META_IMAGE.d.ts +4 -0
  121. package/esm/typings/src/commitments/META_LINK/META_LINK.d.ts +4 -0
  122. package/esm/typings/src/commitments/MODEL/MODEL.d.ts +4 -0
  123. package/esm/typings/src/commitments/NOTE/NOTE.d.ts +4 -0
  124. package/esm/typings/src/commitments/PERSONA/PERSONA.d.ts +4 -0
  125. package/esm/typings/src/commitments/RULE/RULE.d.ts +4 -0
  126. package/esm/typings/src/commitments/SAMPLE/SAMPLE.d.ts +4 -0
  127. package/esm/typings/src/commitments/SCENARIO/SCENARIO.d.ts +4 -0
  128. package/esm/typings/src/commitments/STYLE/STYLE.d.ts +4 -0
  129. package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +5 -0
  130. package/esm/typings/src/commitments/_base/CommitmentDefinition.d.ts +5 -0
  131. package/esm/typings/src/commitments/_base/NotYetImplementedCommitmentDefinition.d.ts +4 -0
  132. package/esm/typings/src/commitments/index.d.ts +20 -1
  133. package/esm/typings/src/execution/LlmExecutionTools.d.ts +9 -0
  134. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +2 -1
  135. package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +10 -1
  136. package/esm/typings/src/utils/normalization/normalizeMessageText.d.ts +9 -0
  137. package/esm/typings/src/utils/normalization/normalizeMessageText.test.d.ts +1 -0
  138. package/esm/typings/src/version.d.ts +1 -1
  139. package/package.json +2 -2
  140. package/umd/index.umd.js +279 -2
  141. package/umd/index.umd.js.map +1 -1
  142. package/apps/agents-server/src/utils/cdn/classes/DigitalOceanSpaces.ts +0 -119
@@ -2,8 +2,10 @@
2
2
 
3
3
  import { ResizablePanelsAuto } from '@common/components/ResizablePanelsAuto/ResizablePanelsAuto';
4
4
  import { string_agent_url, string_book } from '@promptbook-local/types';
5
- import { BookEditorWrapper } from '../book/BookEditorWrapper';
5
+ import { Book, MessageSquare } from 'lucide-react';
6
+ import { useEffect, useState } from 'react';
6
7
  import { AgentChatWrapper } from '../AgentChatWrapper';
8
+ import { BookEditorWrapper } from '../book/BookEditorWrapper';
7
9
 
8
10
  type AgentBookAndChatProps = {
9
11
  agentName: string;
@@ -13,6 +15,56 @@ type AgentBookAndChatProps = {
13
15
 
14
16
  export function AgentBookAndChat(props: AgentBookAndChatProps) {
15
17
  const { agentName, initialAgentSource, agentUrl } = props;
18
+ const [isMobile, setIsMobile] = useState(false);
19
+ const [activeTab, setActiveTab] = useState<'book' | 'chat'>('chat');
20
+ const [isMounted, setIsMounted] = useState(false);
21
+
22
+ useEffect(() => {
23
+ setIsMounted(true);
24
+ const checkMobile = () => setIsMobile(window.innerWidth < 1024);
25
+ checkMobile();
26
+ window.addEventListener('resize', checkMobile);
27
+ return () => window.removeEventListener('resize', checkMobile);
28
+ }, []);
29
+
30
+ if (!isMounted) {
31
+ return <div className="w-full h-full bg-white" />;
32
+ }
33
+
34
+ if (isMobile) {
35
+ return (
36
+ <div className="flex flex-col h-full w-full bg-white">
37
+ <div className="flex-grow overflow-hidden relative">
38
+ <div className={`w-full h-full ${activeTab === 'book' ? 'block' : 'hidden'}`}>
39
+ <BookEditorWrapper agentName={agentName} initialAgentSource={initialAgentSource} />
40
+ </div>
41
+ <div className={`w-full h-full ${activeTab === 'chat' ? 'block' : 'hidden'}`}>
42
+ <AgentChatWrapper agentUrl={agentUrl} />
43
+ </div>
44
+ </div>
45
+ <div className="flex-shrink-0 h-16 bg-white border-t border-gray-200 flex shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.05)] z-10">
46
+ <button
47
+ onClick={() => setActiveTab('book')}
48
+ className={`flex-1 flex flex-col items-center justify-center gap-1 transition-colors ${
49
+ activeTab === 'book' ? 'text-blue-600 bg-blue-50/50' : 'text-gray-500 hover:bg-gray-50'
50
+ }`}
51
+ >
52
+ <Book className="w-5 h-5" />
53
+ <span className="text-xs font-medium">Info</span>
54
+ </button>
55
+ <button
56
+ onClick={() => setActiveTab('chat')}
57
+ className={`flex-1 flex flex-col items-center justify-center gap-1 transition-colors ${
58
+ activeTab === 'chat' ? 'text-blue-600 bg-blue-50/50' : 'text-gray-500 hover:bg-gray-50'
59
+ }`}
60
+ >
61
+ <MessageSquare className="w-5 h-5" />
62
+ <span className="text-xs font-medium">Chat</span>
63
+ </button>
64
+ </div>
65
+ </div>
66
+ );
67
+ }
16
68
 
17
69
  return (
18
70
  <ResizablePanelsAuto name={`agent-book-and-chat-${agentName}`} className="w-full h-full">
@@ -1,15 +1,13 @@
1
- import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
2
- import { parseAgentSource } from '@promptbook-local/core';
1
+ import { $provideServer } from '@/src/tools/$provideServer';
3
2
  import { Metadata } from 'next';
3
+ import { getAgentName, getAgentProfile } from './_utils';
4
4
 
5
5
  export async function generateAgentMetadata({ params }: { params: Promise<{ agentName: string }> }): Promise<Metadata> {
6
- let { agentName } = await params;
7
- agentName = decodeURIComponent(agentName);
6
+ const { publicUrl } = await $provideServer();
7
+ const agentName = await getAgentName(params);
8
8
 
9
9
  try {
10
- const collection = await $provideAgentCollectionForServer();
11
- const agentSource = await collection.getAgentSource(agentName);
12
- const agentProfile = parseAgentSource(agentSource);
10
+ const agentProfile = await getAgentProfile(agentName);
13
11
 
14
12
  const title = agentProfile.meta.fullname || agentProfile.agentName;
15
13
  const description = agentProfile.meta.description || agentProfile.personaDescription || undefined;
@@ -17,22 +15,24 @@ export async function generateAgentMetadata({ params }: { params: Promise<{ agen
17
15
  // Extract image from meta
18
16
  const image = agentProfile.meta.image;
19
17
 
20
- return {
18
+ const metadata = {
19
+ metadataBase: publicUrl,
21
20
  title,
22
21
  description,
22
+ icons: image ? { icon: image } : undefined,
23
23
  openGraph: {
24
24
  title,
25
25
  description,
26
26
  type: 'website',
27
- images: image ? [{ url: image }] : undefined,
28
27
  },
29
28
  twitter: {
30
29
  card: 'summary_large_image',
31
30
  title,
32
31
  description,
33
- images: image ? [image] : undefined,
34
32
  },
35
- };
33
+ } satisfies Metadata;
34
+
35
+ return metadata;
36
36
  } catch (error) {
37
37
  console.warn(`Failed to generate metadata for agent ${agentName}`, error);
38
38
  return {
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { RefreshCcwIcon } from 'lucide-react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useState } from 'react';
6
+ import { restoreAgentVersion } from './actions';
7
+
8
+ type RestoreVersionButtonProps = {
9
+ agentName: string;
10
+ historyId: number;
11
+ };
12
+
13
+ export function RestoreVersionButton({ agentName, historyId }: RestoreVersionButtonProps) {
14
+ const router = useRouter();
15
+ const [isRestoring, setIsRestoring] = useState(false);
16
+
17
+ const handleRestore = async () => {
18
+ if (!confirm('Are you sure you want to restore this version? Current changes will be saved to history.')) {
19
+ return;
20
+ }
21
+
22
+ try {
23
+ setIsRestoring(true);
24
+ await restoreAgentVersion(agentName, historyId);
25
+ router.refresh();
26
+ router.push(`/agents/${agentName}`);
27
+ } catch (error) {
28
+ console.error('Failed to restore version:', error);
29
+ alert('Failed to restore version');
30
+ } finally {
31
+ setIsRestoring(false);
32
+ }
33
+ };
34
+
35
+ return (
36
+ <button
37
+ onClick={handleRestore}
38
+ disabled={isRestoring}
39
+ className="flex items-center gap-2 px-3 py-1 text-sm bg-white border border-gray-300 rounded hover:bg-gray-50 text-gray-700 disabled:opacity-50"
40
+ title="Restore this version"
41
+ >
42
+ <RefreshCcwIcon className={`w-3 h-3 ${isRestoring ? 'animate-spin' : ''}`} />
43
+ {isRestoring ? 'Restoring...' : 'Restore'}
44
+ </button>
45
+ );
46
+ }
@@ -0,0 +1,12 @@
1
+ 'use server';
2
+
3
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
4
+ import { revalidatePath } from 'next/cache';
5
+
6
+ export async function restoreAgentVersion(agentName: string, historyId: number) {
7
+ const collection = await $provideAgentCollectionForServer();
8
+ await collection.restoreAgent(historyId);
9
+
10
+ revalidatePath(`/agents/${agentName}`);
11
+ revalidatePath(`/agents/${agentName}/history`);
12
+ }
@@ -0,0 +1,62 @@
1
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
2
+ import { HistoryIcon } from 'lucide-react';
3
+ import Link from 'next/link';
4
+ import { RestoreVersionButton } from './RestoreVersionButton';
5
+
6
+ export const metadata = {
7
+ title: 'Agent History',
8
+ };
9
+
10
+ export default async function AgentHistoryPage({ params }: { params: Promise<{ agentName: string }> }) {
11
+ const { agentName } = await params;
12
+ const collection = await $provideAgentCollectionForServer();
13
+ const history = await collection.listAgentHistory(agentName);
14
+
15
+ return (
16
+ <div className="container mx-auto p-6 max-w-4xl">
17
+ <header className="flex items-center gap-4 mb-8">
18
+ <div className="bg-blue-100 p-3 rounded-full">
19
+ <HistoryIcon className="w-8 h-8 text-blue-600" />
20
+ </div>
21
+ <div>
22
+ <h1 className="text-3xl font-bold text-gray-900">History: {agentName}</h1>
23
+ <p className="text-gray-600">
24
+ Previous versions of this agent. <Link href={`/agents/${agentName}`} className="text-blue-600 hover:underline">Back to agent</Link>
25
+ </p>
26
+ </div>
27
+ </header>
28
+
29
+ {history.length === 0 ? (
30
+ <div className="text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300">
31
+ <p className="text-gray-500 text-lg">No history found</p>
32
+ </div>
33
+ ) : (
34
+ <div className="relative border-l border-gray-200 ml-4">
35
+ {history.map((item, index) => (
36
+ <div key={item.id} className="mb-8 ml-6">
37
+ <span className="absolute flex items-center justify-center w-6 h-6 bg-blue-100 rounded-full -left-3 ring-8 ring-white">
38
+ <span className="w-3 h-3 bg-blue-600 rounded-full"></span>
39
+ </span>
40
+ <div className="p-4 bg-white border border-gray-200 rounded-lg shadow-sm">
41
+ <div className="flex justify-between items-start mb-2">
42
+ <div>
43
+ <time className="block mb-1 text-sm font-normal leading-none text-gray-400">
44
+ {new Date(item.createdAt).toLocaleString()}
45
+ </time>
46
+ <h3 className="text-lg font-semibold text-gray-900">
47
+ Version {history.length - index}
48
+ </h3>
49
+ <p className="text-sm text-gray-500">
50
+ Hash: <code className="bg-gray-100 px-1 rounded">{item.agentHash.substring(0, 8)}</code>
51
+ </p>
52
+ </div>
53
+ <RestoreVersionButton agentName={agentName} historyId={item.id} />
54
+ </div>
55
+ </div>
56
+ </div>
57
+ ))}
58
+ </div>
59
+ )}
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,80 @@
1
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
+ import { serializeError } from '@promptbook-local/utils';
3
+ import { ImageResponse } from 'next/og';
4
+ import { assertsError } from '../../../../../../../../src/errors/assertsError';
5
+ import { Color } from '../../../../../../../../src/utils/color/Color';
6
+ import { keepUnused } from '../../../../../../../../src/utils/organization/keepUnused';
7
+ import { getAgentName, getAgentProfile } from '../../_utils';
8
+
9
+ const size = {
10
+ width: 256,
11
+ height: 256,
12
+ };
13
+
14
+ export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
15
+ keepUnused(request /* <- Note: We dont need `request` parameter */);
16
+
17
+ try {
18
+ const agentName = await getAgentName(params);
19
+ const agentProfile = await getAgentProfile(agentName);
20
+ const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
21
+
22
+ return new ImageResponse(
23
+ (
24
+ <div
25
+ style={{
26
+ width: '100%',
27
+ height: '100%',
28
+ backgroundColor: 'transparent',
29
+ display: 'flex',
30
+ alignItems: 'center',
31
+ justifyContent: 'center',
32
+ borderRadius: '50%',
33
+ overflow: 'hidden',
34
+ }}
35
+ >
36
+ <div
37
+ style={{
38
+ width: '100%',
39
+ height: '100%',
40
+ backgroundColor: agentColor.toHex(),
41
+ display: 'flex',
42
+ alignItems: 'center',
43
+ justifyContent: 'center',
44
+ }}
45
+ >
46
+ {/* Note: `next/image` is not working propperly with `next/og` */}
47
+ {/* eslint-disable-next-line @next/next/no-img-element */}
48
+ <img src={agentProfile.meta.image!} alt="Agent Icon" />
49
+ </div>
50
+ </div>
51
+ ),
52
+ {
53
+ ...size,
54
+ emoji: 'openmoji',
55
+ },
56
+ );
57
+ } catch (error) {
58
+ assertsError(error);
59
+
60
+ console.error(error);
61
+
62
+ return new Response(
63
+ JSON.stringify(
64
+ serializeError(error),
65
+ // <- TODO: [🐱‍🚀] Rename `serializeError` to `errorToJson`
66
+ null,
67
+ 4,
68
+ // <- TODO: [🐱‍🚀] Allow to configure pretty print for agent server
69
+ ),
70
+ {
71
+ status: 400, // <- TODO: [🐱‍🚀] Make `errorToHttpStatusCode`
72
+ headers: { 'Content-Type': 'application/json' },
73
+ },
74
+ );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * TODO: [🦚] DRY
80
+ */
@@ -0,0 +1,92 @@
1
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
+ import { serializeError } from '@promptbook-local/utils';
3
+ import { ImageResponse } from 'next/og';
4
+ import { assertsError } from '../../../../../../../../src/errors/assertsError';
5
+ import { Color } from '../../../../../../../../src/utils/color/Color';
6
+ import { textColor } from '../../../../../../../../src/utils/color/operators/furthest';
7
+ import { grayscale } from '../../../../../../../../src/utils/color/operators/grayscale';
8
+ import { keepUnused } from '../../../../../../../../src/utils/organization/keepUnused';
9
+ import { getAgentName, getAgentProfile } from '../../_utils';
10
+
11
+ const size = {
12
+ width: 1920,
13
+ height: 1080,
14
+ };
15
+
16
+ export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
17
+ keepUnused(request /* <- Note: We dont need `request` parameter */);
18
+
19
+ try {
20
+ const agentName = await getAgentName(params);
21
+ const agentProfile = await getAgentProfile(agentName);
22
+ const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
23
+ const backgroundColor = agentColor.then(grayscale(0.5));
24
+
25
+ return new ImageResponse(
26
+ (
27
+ <div
28
+ style={{
29
+ width: '100%',
30
+ height: '100%',
31
+ backgroundColor: backgroundColor.toHex(),
32
+ color: agentColor.then(textColor).toHex(),
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ }}
37
+ >
38
+ <div
39
+ style={{
40
+ width: '40%',
41
+ display: 'flex',
42
+ alignItems: 'center',
43
+ justifyContent: 'center',
44
+ }}
45
+ >
46
+ {/* Note: `next/image` is not working propperly with `next/og` */}
47
+ {/* eslint-disable-next-line @next/next/no-img-element */}
48
+ <img
49
+ style={{
50
+ width: '80%',
51
+ backgroundColor: agentColor.toHex(),
52
+ borderRadius: '50%',
53
+ }}
54
+ src={agentProfile.meta.image!}
55
+ alt="Agent Icon"
56
+ />
57
+ </div>
58
+ <div style={{ width: '60%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
59
+ <h1 style={{ fontSize: '110px' }}>{agentProfile.meta.fullname || agentName}</h1>
60
+ </div>
61
+ </div>
62
+ ),
63
+ {
64
+ ...size,
65
+ emoji: 'openmoji',
66
+ },
67
+ );
68
+ } catch (error) {
69
+ assertsError(error);
70
+
71
+ console.error(error);
72
+
73
+ return new Response(
74
+ JSON.stringify(
75
+ serializeError(error),
76
+ // <- TODO: [🐱‍🚀] Rename `serializeError` to `errorToJson`
77
+ null,
78
+ 4,
79
+ // <- TODO: [🐱‍🚀] Allow to configure pretty print for agent server
80
+ ),
81
+ {
82
+ status: 400, // <- TODO: [🐱‍🚀] Make `errorToHttpStatusCode`
83
+ headers: { 'Content-Type': 'application/json' },
84
+ },
85
+ );
86
+ }
87
+ }
88
+
89
+ /**
90
+ * TODO: [🦚] DRY
91
+ * TODO: [🦚] This should be the true screenshot NOT just image + Agent name
92
+ */
@@ -0,0 +1,92 @@
1
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
+ import { serializeError } from '@promptbook-local/utils';
3
+ import { ImageResponse } from 'next/og';
4
+ import { assertsError } from '../../../../../../../../src/errors/assertsError';
5
+ import { Color } from '../../../../../../../../src/utils/color/Color';
6
+ import { textColor } from '../../../../../../../../src/utils/color/operators/furthest';
7
+ import { grayscale } from '../../../../../../../../src/utils/color/operators/grayscale';
8
+ import { keepUnused } from '../../../../../../../../src/utils/organization/keepUnused';
9
+ import { getAgentName, getAgentProfile } from '../../_utils';
10
+
11
+ const size = {
12
+ width: 1080,
13
+ height: 1920,
14
+ };
15
+
16
+ export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
17
+ keepUnused(request /* <- Note: We dont need `request` parameter */);
18
+
19
+ try {
20
+ const agentName = await getAgentName(params);
21
+ const agentProfile = await getAgentProfile(agentName);
22
+ const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
23
+ const backgroundColor = agentColor.then(grayscale(0.5));
24
+
25
+ return new ImageResponse(
26
+ (
27
+ <div
28
+ style={{
29
+ width: '100%',
30
+ height: '100%',
31
+ backgroundColor: backgroundColor.toHex(),
32
+ color: agentColor.then(textColor).toHex(),
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ }}
37
+ >
38
+ <div
39
+ style={{
40
+ width: '40%',
41
+ display: 'flex',
42
+ alignItems: 'center',
43
+ justifyContent: 'center',
44
+ }}
45
+ >
46
+ {/* Note: `next/image` is not working propperly with `next/og` */}
47
+ {/* eslint-disable-next-line @next/next/no-img-element */}
48
+ <img
49
+ style={{
50
+ width: '80%',
51
+ backgroundColor: agentColor.toHex(),
52
+ borderRadius: '50%',
53
+ }}
54
+ src={agentProfile.meta.image!}
55
+ alt="Agent Icon"
56
+ />
57
+ </div>
58
+ <div style={{ width: '60%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
59
+ <h1 style={{ fontSize: '110px' }}>{agentProfile.meta.fullname || agentName}</h1>
60
+ </div>
61
+ </div>
62
+ ),
63
+ {
64
+ ...size,
65
+ emoji: 'openmoji',
66
+ },
67
+ );
68
+ } catch (error) {
69
+ assertsError(error);
70
+
71
+ console.error(error);
72
+
73
+ return new Response(
74
+ JSON.stringify(
75
+ serializeError(error),
76
+ // <- TODO: [🐱‍🚀] Rename `serializeError` to `errorToJson`
77
+ null,
78
+ 4,
79
+ // <- TODO: [🐱‍🚀] Allow to configure pretty print for agent server
80
+ ),
81
+ {
82
+ status: 400, // <- TODO: [🐱‍🚀] Make `errorToHttpStatusCode`
83
+ headers: { 'Content-Type': 'application/json' },
84
+ },
85
+ );
86
+ }
87
+ }
88
+
89
+ /**
90
+ * TODO: [🦚] DRY
91
+ * TODO: [🦚] This should be the true screenshot NOT just image + Agent name
92
+ */
@@ -0,0 +1,61 @@
1
+ 'use server';
2
+
3
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
4
+ import { $provideServer } from '@/src/tools/$provideServer';
5
+ import { PromptbookAgent } from '@promptbook-local/components';
6
+ import { parseAgentSource } from '@promptbook-local/core';
7
+ import { headers } from 'next/headers';
8
+ import spaceTrim from 'spacetrim';
9
+ import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
10
+ import { CodePreview } from '../../../../../../_common/components/CodePreview/CodePreview';
11
+ import { generateAgentMetadata } from '../generateAgentMetadata';
12
+
13
+ export const generateMetadata = generateAgentMetadata;
14
+
15
+ export default async function AgentChatPage({ params }: { params: Promise<{ agentName: string }> }) {
16
+ $sideEffect(headers());
17
+ let { agentName } = await params;
18
+ agentName = decodeURIComponent(agentName);
19
+
20
+ const collection = await $provideAgentCollectionForServer();
21
+ const agentSource = await collection.getAgentSource(agentName);
22
+ const { meta } = parseAgentSource(agentSource);
23
+ const { fullname, color, image, ...restMeta } = meta;
24
+ const { publicUrl } = await $provideServer();
25
+ const agentUrl = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
26
+
27
+ const code = spaceTrim(
28
+ (block) => `
29
+
30
+ import { PromptbookAgent } from '@promptbook/components';
31
+
32
+ export function YourComponent() {
33
+ return(
34
+ <PromptbookAgent
35
+ agentUrl="${agentUrl}"
36
+ meta={${block(JSON.stringify({ fullname, color, image, ...restMeta }, null, 4))}}
37
+ />
38
+ );
39
+ }
40
+
41
+ `,
42
+ );
43
+
44
+ return (
45
+ <main className="w-screen h-screen p-4">
46
+ <h1 className="text-2xl font-bold p-4 border-b">{meta.fullname || agentName} Integration Code</h1>
47
+ <p className="mt-4 mb-8 text-gray-600">
48
+ Use the following code to integrate the <strong>{meta.fullname || agentName}</strong> agent into your
49
+ React application using the <code>{'<PromptbookAgent />'}</code> component.
50
+ </p>
51
+
52
+ <CodePreview code={code} />
53
+ <PromptbookAgent agentUrl={agentUrl} meta={meta} />
54
+ </main>
55
+ );
56
+ }
57
+
58
+ /**
59
+ * TODO: Make this page better, bring from Promptbook.Studio
60
+ * TODO: [🚗] Components and pages here should be just tiny UI wraper around proper agent logic and conponents
61
+ */
@@ -0,0 +1,102 @@
1
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
+ import { serializeError } from '@promptbook-local/utils';
3
+ import { ImageResponse } from 'next/og';
4
+ import { assertsError } from '../../../../../../src/errors/assertsError';
5
+ import { Color } from '../../../../../../src/utils/color/Color';
6
+ import { textColor } from '../../../../../../src/utils/color/operators/furthest';
7
+ import { grayscale } from '../../../../../../src/utils/color/operators/grayscale';
8
+ import { getAgentName, getAgentProfile } from './_utils';
9
+
10
+ // export const runtime = 'edge';
11
+ // <- Note: On Vercel Edge runtime some modules are not working *(like `crypto`)*
12
+
13
+ export const alt = 'Agent Profile';
14
+ export const size = {
15
+ width: 1200,
16
+ height: 630,
17
+ };
18
+
19
+ export const contentType = 'image/png';
20
+
21
+ export default async function Image({ params }: { params: Promise<{ agentName: string }> }) {
22
+ try {
23
+ const agentName = await getAgentName(params);
24
+ const agentProfile = await getAgentProfile(agentName);
25
+ const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
26
+ const backgroundColor = agentColor.then(grayscale(0.5));
27
+
28
+ return new ImageResponse(
29
+ (
30
+ <div
31
+ style={{
32
+ width: '100%',
33
+ height: '100%',
34
+ backgroundColor: backgroundColor.toHex(),
35
+ color: agentColor.then(textColor).toHex(),
36
+ display: 'flex',
37
+ alignItems: 'center',
38
+ justifyContent: 'center',
39
+ }}
40
+ >
41
+ <div
42
+ style={{
43
+ width: '50%',
44
+ display: 'flex',
45
+ alignItems: 'center',
46
+ justifyContent: 'center',
47
+ }}
48
+ >
49
+ {/* Note: `next/image` is not working propperly with `next/og` */}
50
+ {/* eslint-disable-next-line @next/next/no-img-element */}
51
+ <img
52
+ style={{
53
+ width: '80%',
54
+ backgroundColor: agentColor.toHex(),
55
+ borderRadius: '50%',
56
+ }}
57
+ src={agentProfile.meta.image!}
58
+ alt="Agent Icon"
59
+ />
60
+ </div>
61
+ <div
62
+ style={{
63
+ width: '50%',
64
+ display: 'flex',
65
+ alignItems: 'center',
66
+ justifyContent: 'center',
67
+ textAlign: 'center',
68
+ }}
69
+ >
70
+ <h1 style={{ fontSize: '100px' }}>{agentProfile.meta.fullname || agentName}</h1>
71
+ </div>
72
+ </div>
73
+ ),
74
+ {
75
+ ...size,
76
+ emoji: 'openmoji',
77
+ },
78
+ );
79
+ } catch (error) {
80
+ assertsError(error);
81
+
82
+ console.error(error);
83
+
84
+ return new Response(
85
+ JSON.stringify(
86
+ serializeError(error),
87
+ // <- TODO: [🐱‍🚀] Rename `serializeError` to `errorToJson`
88
+ null,
89
+ 4,
90
+ // <- TODO: [🐱‍🚀] Allow to configure pretty print for agent server
91
+ ),
92
+ {
93
+ status: 400, // <- TODO: [🐱‍🚀] Make `errorToHttpStatusCode`
94
+ headers: { 'Content-Type': 'application/json' },
95
+ },
96
+ );
97
+ }
98
+ }
99
+
100
+ /**
101
+ * TODO: [🦚] DRY
102
+ */