@promptbook/cli 0.103.0-52 → 0.103.0-53

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 (107) 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/chat-feedback/ChatFeedbackClient.tsx +541 -0
  8. package/apps/agents-server/src/app/admin/chat-feedback/page.tsx +22 -0
  9. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +532 -0
  10. package/apps/agents-server/src/app/admin/chat-history/page.tsx +21 -0
  11. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +241 -27
  12. package/apps/agents-server/src/app/admin/models/page.tsx +22 -0
  13. package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +131 -0
  14. package/apps/agents-server/src/app/admin/users/[userId]/page.tsx +21 -0
  15. package/apps/agents-server/src/app/admin/users/page.tsx +18 -0
  16. package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatFeedbackButton.tsx +63 -0
  17. package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatHistoryButton.tsx +63 -0
  18. package/apps/agents-server/src/app/agents/[agentName]/CloneAgentButton.tsx +41 -0
  19. package/apps/agents-server/src/app/agents/[agentName]/InstallPwaButton.tsx +74 -0
  20. package/apps/agents-server/src/app/agents/[agentName]/ServiceWorkerRegister.tsx +24 -0
  21. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +19 -0
  22. package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +67 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +3 -0
  24. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +177 -0
  25. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +3 -0
  26. package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +53 -1
  27. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +11 -11
  28. package/apps/agents-server/src/app/agents/[agentName]/history/RestoreVersionButton.tsx +46 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +12 -0
  30. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +62 -0
  31. package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +80 -0
  32. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +92 -0
  33. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +92 -0
  34. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +61 -0
  35. package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +102 -0
  36. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +41 -22
  37. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +47 -0
  38. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +19 -0
  39. package/apps/agents-server/src/app/api/agents/route.ts +22 -13
  40. package/apps/agents-server/src/app/api/auth/login/route.ts +6 -44
  41. package/apps/agents-server/src/app/api/chat-feedback/[id]/route.ts +38 -0
  42. package/apps/agents-server/src/app/api/chat-feedback/route.ts +157 -0
  43. package/apps/agents-server/src/app/api/chat-history/[id]/route.ts +37 -0
  44. package/apps/agents-server/src/app/api/chat-history/route.ts +147 -0
  45. package/apps/agents-server/src/app/api/federated-agents/route.ts +17 -0
  46. package/apps/agents-server/src/app/api/upload/route.ts +9 -1
  47. package/apps/agents-server/src/app/docs/[docId]/page.tsx +62 -0
  48. package/apps/agents-server/src/app/docs/page.tsx +33 -0
  49. package/apps/agents-server/src/app/layout.tsx +29 -3
  50. package/apps/agents-server/src/app/manifest.ts +109 -0
  51. package/apps/agents-server/src/app/page.tsx +8 -45
  52. package/apps/agents-server/src/app/recycle-bin/RestoreAgentButton.tsx +40 -0
  53. package/apps/agents-server/src/app/recycle-bin/actions.ts +27 -0
  54. package/apps/agents-server/src/app/recycle-bin/page.tsx +58 -0
  55. package/apps/agents-server/src/app/restricted/page.tsx +33 -0
  56. package/apps/agents-server/src/app/test/og-image/README.md +1 -0
  57. package/apps/agents-server/src/app/test/og-image/opengraph-image.tsx +37 -0
  58. package/apps/agents-server/src/app/test/og-image/page.tsx +22 -0
  59. package/apps/agents-server/src/components/Footer/Footer.tsx +175 -0
  60. package/apps/agents-server/src/components/Header/Header.tsx +445 -79
  61. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +46 -14
  62. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +58 -0
  63. package/apps/agents-server/src/components/Homepage/Card.tsx +1 -1
  64. package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +21 -0
  65. package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +183 -0
  66. package/apps/agents-server/src/components/Homepage/ModelsSection.tsx +75 -0
  67. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +28 -3
  68. package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +18 -17
  69. package/apps/agents-server/src/components/Portal/Portal.tsx +38 -0
  70. package/apps/agents-server/src/components/UsersList/UsersList.tsx +82 -131
  71. package/apps/agents-server/src/components/UsersList/useUsersAdmin.ts +139 -0
  72. package/apps/agents-server/src/database/metadataDefaults.ts +38 -6
  73. package/apps/agents-server/src/middleware.ts +146 -93
  74. package/apps/agents-server/src/tools/$provideServer.ts +2 -2
  75. package/apps/agents-server/src/utils/authenticateUser.ts +42 -0
  76. package/apps/agents-server/src/utils/chatFeedbackAdmin.ts +96 -0
  77. package/apps/agents-server/src/utils/chatHistoryAdmin.ts +96 -0
  78. package/apps/agents-server/src/utils/getEffectiveFederatedServers.ts +22 -0
  79. package/apps/agents-server/src/utils/getFederatedAgents.ts +31 -8
  80. package/apps/agents-server/src/utils/getFederatedServersFromMetadata.ts +10 -0
  81. package/apps/agents-server/src/utils/getVisibleCommitmentDefinitions.ts +12 -0
  82. package/apps/agents-server/src/utils/isUserAdmin.ts +2 -2
  83. package/apps/agents-server/vercel.json +7 -0
  84. package/esm/index.es.js +153 -2
  85. package/esm/index.es.js.map +1 -1
  86. package/esm/typings/servers.d.ts +8 -1
  87. package/esm/typings/src/_packages/components.index.d.ts +2 -0
  88. package/esm/typings/src/_packages/core.index.d.ts +6 -0
  89. package/esm/typings/src/_packages/types.index.d.ts +2 -0
  90. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  91. package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +7 -0
  92. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +4 -0
  93. package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +12 -0
  94. package/esm/typings/src/book-components/icons/MicIcon.d.ts +8 -0
  95. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +17 -0
  96. package/esm/typings/src/commitments/MESSAGE/AgentMessageCommitmentDefinition.d.ts +28 -0
  97. package/esm/typings/src/commitments/MESSAGE/UserMessageCommitmentDefinition.d.ts +28 -0
  98. package/esm/typings/src/commitments/index.d.ts +20 -1
  99. package/esm/typings/src/execution/LlmExecutionTools.d.ts +9 -0
  100. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +2 -1
  101. package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +10 -1
  102. package/esm/typings/src/utils/normalization/normalizeMessageText.d.ts +9 -0
  103. package/esm/typings/src/utils/normalization/normalizeMessageText.test.d.ts +1 -0
  104. package/esm/typings/src/version.d.ts +1 -1
  105. package/package.json +1 -1
  106. package/umd/index.umd.js +153 -2
  107. package/umd/index.umd.js.map +1 -1
@@ -0,0 +1,33 @@
1
+ import Link from 'next/link';
2
+ import { Card } from '../../components/Homepage/Card';
3
+ import { Section } from '../../components/Homepage/Section';
4
+ import { getVisibleCommitmentDefinitions } from '../../utils/getVisibleCommitmentDefinitions';
5
+
6
+ export default function DocsPage() {
7
+ const groupedCommitments = getVisibleCommitmentDefinitions();
8
+
9
+ return (
10
+ <div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
11
+ <div className="container mx-auto px-4 py-16">
12
+ <Section title="Documentation">
13
+ {groupedCommitments.map(({ primary, aliases }) => (
14
+ <Link key={primary.type} href={`/docs/${primary.type}`} className="block h-full group">
15
+ <Card className="h-full group-hover:border-blue-500 transition-colors">
16
+ <h3 className="text-xl font-semibold mb-2 group-hover:text-blue-600 transition-colors">
17
+ {primary.type}
18
+ {aliases.length > 0 && (
19
+ <span className="text-gray-400 font-normal text-lg">
20
+ {' / '}
21
+ {aliases.join(' / ')}
22
+ </span>
23
+ )}
24
+ </h3>
25
+ {primary.description && <p className="text-gray-600 line-clamp-3">{primary.description}</p>}
26
+ </Card>
27
+ </Link>
28
+ ))}
29
+ </Section>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
@@ -3,7 +3,10 @@ import { LayoutWrapper } from '@/src/components/LayoutWrapper/LayoutWrapper';
3
3
  import type { Metadata } from 'next';
4
4
  import { Barlow_Condensed } from 'next/font/google';
5
5
  import { getMetadata } from '../database/getMetadata';
6
+ import { $provideAgentCollectionForServer } from '../tools/$provideAgentCollectionForServer';
7
+ import { $provideServer } from '../tools/$provideServer';
6
8
  import { isUserAdmin } from '../utils/isUserAdmin';
9
+ import { getCurrentUser } from '../utils/getCurrentUser';
7
10
  import './globals.css';
8
11
 
9
12
  const barlowCondensed = Barlow_Condensed({
@@ -13,9 +16,9 @@ const barlowCondensed = Barlow_Condensed({
13
16
  });
14
17
 
15
18
  export async function generateMetadata(): Promise<Metadata> {
19
+ const { publicUrl } = await $provideServer();
16
20
  const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
17
21
  const serverDescription = (await getMetadata('SERVER_DESCRIPTION')) || 'Agents server powered by Promptbook';
18
- const serverUrl = (await getMetadata('SERVER_URL')) || 'https://ptbk.io';
19
22
 
20
23
  return {
21
24
  title: serverName,
@@ -44,7 +47,7 @@ export async function generateMetadata(): Promise<Metadata> {
44
47
  description: serverDescription,
45
48
  // TODO: images: ['https://www.ptbk.io/design'],
46
49
  },
47
- metadataBase: new URL(serverUrl),
50
+ metadataBase: publicUrl,
48
51
  };
49
52
  }
50
53
 
@@ -54,9 +57,22 @@ export default async function RootLayout({
54
57
  children: React.ReactNode;
55
58
  }>) {
56
59
  const isAdmin = await isUserAdmin();
60
+ const currentUser = await getCurrentUser();
57
61
  const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
58
62
  const serverLogoUrl = (await getMetadata('SERVER_LOGO_URL')) || null;
59
63
  const serverFaviconUrl = (await getMetadata('SERVER_FAVICON_URL')) || faviconLogoImage.src;
64
+ const isFooterShown = ((await getMetadata('IS_FOOTER_SHOWN')) || 'true') === 'true';
65
+
66
+ let footerLinks = [];
67
+ try {
68
+ const footerLinksString = (await getMetadata('FOOTER_LINKS')) || '[]';
69
+ footerLinks = JSON.parse(footerLinksString);
70
+ } catch (error) {
71
+ console.error('Failed to parse FOOTER_LINKS', error);
72
+ }
73
+
74
+ const collection = await $provideAgentCollectionForServer();
75
+ const agents = await collection.listAgents();
60
76
 
61
77
  return (
62
78
  <html lang="en">
@@ -83,9 +99,19 @@ export default async function RootLayout({
83
99
  <link rel="icon" href={serverFaviconUrl} type="image/x-icon" />
84
100
  </head>
85
101
  <body className={`${barlowCondensed.variable} antialiased bg-white text-gray-900`}>
86
- <LayoutWrapper isAdmin={isAdmin} serverName={serverName} serverLogoUrl={serverLogoUrl}>
102
+ <LayoutWrapper
103
+ isAdmin={isAdmin}
104
+ currentUser={currentUser}
105
+ serverName={serverName}
106
+ serverLogoUrl={serverLogoUrl}
107
+ agents={JSON.parse(JSON.stringify(agents))}
108
+ isFooterShown={isFooterShown}
109
+ footerLinks={footerLinks}
110
+ >
87
111
  {children}
88
112
  </LayoutWrapper>
113
+ {/* Global portal root for modals/popups */}
114
+ <div id="portal-root" />
89
115
  </body>
90
116
  </html>
91
117
  );
@@ -0,0 +1,109 @@
1
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
+ import { MetadataRoute } from 'next';
3
+ import { headers } from 'next/headers';
4
+ import { Color } from '../../../../src/utils/color/Color';
5
+ import { getMetadata } from '../database/getMetadata';
6
+ import { $provideServer } from '../tools/$provideServer';
7
+ import { getAgentProfile } from './agents/[agentName]/_utils';
8
+
9
+ export default async function manifest(): Promise<MetadataRoute.Manifest> {
10
+ const { publicUrl } = await $provideServer();
11
+ const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
12
+ const serverDescription = (await getMetadata('SERVER_DESCRIPTION')) || 'Agents server powered by Promptbook';
13
+
14
+ const referer = (await headers()).get('referer') || '';
15
+ const isAgentManifest = referer.includes('/agents/');
16
+
17
+ if (!isAgentManifest) {
18
+ return {
19
+ name: serverName,
20
+ short_name: serverName,
21
+ description: serverDescription,
22
+ start_url: publicUrl.href,
23
+ display_override: ['fullscreen', 'minimal-ui'],
24
+ display: 'standalone',
25
+ background_color: PROMPTBOOK_COLOR.toHex(),
26
+ theme_color: PROMPTBOOK_COLOR.toHex(),
27
+ icons: [
28
+ // TODO: Create icons for the server homepage
29
+ ],
30
+ scope: publicUrl.href,
31
+ } satisfies MetadataRoute.Manifest;
32
+ }
33
+
34
+ const agentName = decodeURIComponent(referer.split('/agents/')[1]?.split('/')[0]);
35
+
36
+ try {
37
+ const agentProfile = await getAgentProfile(agentName);
38
+
39
+ const name = agentProfile.meta.fullname || agentProfile.agentName;
40
+ const short_name = agentProfile.agentName;
41
+ const description = agentProfile.meta.description || agentProfile.personaDescription || `Agent ${name}`;
42
+
43
+ // Extract brand color from meta
44
+ const brandColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
45
+ const theme_color = brandColor.toHex();
46
+ const background_color = '#ffffff';
47
+
48
+ const agentUrl = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
49
+
50
+ const icons: MetadataRoute.Manifest['icons'] = [
51
+ {
52
+ src: `${agentUrl}/images/icon-256.png`,
53
+ sizes: '256x256',
54
+ type: 'image/png',
55
+ purpose: 'any',
56
+ },
57
+ {
58
+ src: `${agentUrl}/images/icon-256.png`,
59
+ sizes: '256x256',
60
+ type: 'image/png',
61
+ purpose: 'maskable',
62
+ },
63
+ ];
64
+
65
+ const screenshots: MetadataRoute.Manifest['screenshots'] = [
66
+ {
67
+ src: `${agentUrl}/images/screenshot-fullhd.png`,
68
+ sizes: '1920x1080',
69
+ type: 'image/png',
70
+ form_factor: 'wide',
71
+ label: 'Full HD Screenshot',
72
+ },
73
+ {
74
+ src: `${agentUrl}/images/screenshot-phone.png`,
75
+ sizes: '1080x1920',
76
+ type: 'image/png',
77
+ form_factor: 'narrow',
78
+ label: 'Phone Screenshot',
79
+ },
80
+ ];
81
+
82
+ return {
83
+ id: agentUrl,
84
+ name,
85
+ short_name,
86
+ description,
87
+ start_url: `${agentUrl}/chat`,
88
+ scope: agentUrl,
89
+ display_override: ['fullscreen', 'minimal-ui'],
90
+ display: 'standalone',
91
+ background_color,
92
+ theme_color,
93
+ icons,
94
+ screenshots,
95
+ } satisfies MetadataRoute.Manifest;
96
+ } catch (error) {
97
+ console.warn(`Failed to generate manifest for agent ${agentName}`, error);
98
+ return {
99
+ name: agentName,
100
+ short_name: agentName,
101
+ start_url: `${publicUrl.href}agents/${encodeURIComponent(agentName)}/chat`,
102
+ display_override: ['fullscreen', 'minimal-ui'],
103
+ display: 'standalone',
104
+ background_color: '#ffffff',
105
+ theme_color: PROMPTBOOK_COLOR.toHex(),
106
+ icons: [],
107
+ } satisfies MetadataRoute.Manifest;
108
+ }
109
+ }
@@ -5,22 +5,18 @@ import moment from 'moment';
5
5
  import { headers } from 'next/headers';
6
6
  import { AboutPromptbookInformation } from '../../../../src/utils/misc/xAboutPromptbookInformation';
7
7
  import { $sideEffect } from '../../../../src/utils/organization/$sideEffect';
8
- import { AuthControls } from '../components/Auth/AuthControls';
9
- import { AgentCard } from '../components/Homepage/AgentCard';
10
- import { ModelCard } from '../components/Homepage/ModelCard';
8
+ import { AgentsList } from '../components/Homepage/AgentsList';
9
+ import { ExternalAgentsSectionClient } from '../components/Homepage/ExternalAgentsSectionClient';
10
+ import { ModelsSection } from '../components/Homepage/ModelsSection';
11
11
  import { Section } from '../components/Homepage/Section';
12
12
  import { TechInfoCard } from '../components/Homepage/TechInfoCard';
13
13
  import { UsersList } from '../components/UsersList/UsersList';
14
14
  import VercelDeploymentCard from '../components/VercelDeploymentCard/VercelDeploymentCard';
15
- import { getMetadata } from '../database/getMetadata';
16
15
  import { getLongRunningTask } from '../deamons/longRunningTask';
17
16
  import { $provideAgentCollectionForServer } from '../tools/$provideAgentCollectionForServer';
18
17
  import { $provideExecutionToolsForServer } from '../tools/$provideExecutionToolsForServer';
19
18
  import { $provideServer } from '../tools/$provideServer';
20
- import { getCurrentUser } from '../utils/getCurrentUser';
21
- import { getFederatedAgents } from '../utils/getFederatedAgents';
22
19
  import { isUserAdmin } from '../utils/isUserAdmin';
23
- import { AddAgentButton } from './AddAgentButton';
24
20
 
25
21
  // Add calendar formats that include seconds
26
22
  const calendarWithSeconds = {
@@ -36,19 +32,10 @@ export default async function HomePage() {
36
32
  $sideEffect(/* Note: [🐶] This will ensure dynamic rendering of page and avoid Next.js pre-render */ headers());
37
33
 
38
34
  const isAdmin = await isUserAdmin(); /* <- TODO: [👹] Here should be user permissions */
39
- const currentUser = await getCurrentUser();
40
35
 
41
36
  const collection = await $provideAgentCollectionForServer();
42
37
  const agents = await collection.listAgents();
43
38
 
44
- const federatedServersString = (await getMetadata('FEDERATED_SERVERS')) || '';
45
- const federatedServers = federatedServersString
46
- .split(',')
47
- .map((s) => s.trim())
48
- .filter((s) => s !== '');
49
-
50
- const externalAgents = await getFederatedAgents(federatedServers);
51
-
52
39
  const longRunningTask = getLongRunningTask();
53
40
 
54
41
  const executionTools = await $provideExecutionToolsForServer();
@@ -59,38 +46,14 @@ export default async function HomePage() {
59
46
  return (
60
47
  <div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
61
48
  <div className="container mx-auto px-4 py-16">
62
- <div className="flex justify-end mb-4">
63
- <AuthControls initialUser={currentUser} />
64
- </div>
65
-
66
- <Section title={`Agents (${agents.length})`}>
67
- {agents.map((agent) => (
68
- <AgentCard key={agent.agentName} agent={agent} href={`/${agent.agentName}`} />
69
- ))}
70
- {isAdmin && <AddAgentButton />}
71
- </Section>
72
-
73
- {externalAgents.length > 0 && (
74
- <Section title={`External Agents (${externalAgents.length})`}>
75
- {externalAgents.map((agent) => (
76
- <AgentCard key={agent.url} agent={agent} href={agent.url} />
77
- ))}
78
- </Section>
79
- )}
49
+ <AgentsList agents={[...agents]} isAdmin={isAdmin} />
50
+
51
+ <ExternalAgentsSectionClient />
80
52
 
81
- {isAdmin && <UsersList />}
53
+ {isAdmin && <UsersList allowCreate={false} />}
82
54
 
83
55
  {isAdmin && (
84
- <Section title={`Models (${models.length})`}>
85
- {models.map(({ modelName, modelTitle, modelDescription }) => (
86
- <ModelCard
87
- key={modelName}
88
- modelName={modelName}
89
- modelTitle={modelTitle || modelName}
90
- modelDescription={modelDescription}
91
- />
92
- ))}
93
- </Section>
56
+ <ModelsSection models={models} maxVisible={11} showViewAllLink />
94
57
  )}
95
58
 
96
59
  {isAdmin && (
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+
3
+ import { RefreshCcwIcon } from 'lucide-react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useState } from 'react';
6
+ import { restoreDeletedAgent } from './actions';
7
+
8
+ type RestoreAgentButtonProps = {
9
+ agentName: string;
10
+ };
11
+
12
+ export function RestoreAgentButton({ agentName }: RestoreAgentButtonProps) {
13
+ const router = useRouter();
14
+ const [isRestoring, setIsRestoring] = useState(false);
15
+
16
+ const handleRestore = async () => {
17
+ try {
18
+ setIsRestoring(true);
19
+ await restoreDeletedAgent(agentName);
20
+ router.refresh();
21
+ } catch (error) {
22
+ console.error('Failed to restore agent:', error);
23
+ alert('Failed to restore agent');
24
+ } finally {
25
+ setIsRestoring(false);
26
+ }
27
+ };
28
+
29
+ return (
30
+ <button
31
+ onClick={handleRestore}
32
+ disabled={isRestoring}
33
+ className="flex items-center gap-2 px-3 py-2 bg-white border border-gray-300 rounded hover:bg-gray-50 text-gray-700 disabled:opacity-50"
34
+ title="Restore agent"
35
+ >
36
+ <RefreshCcwIcon className={`w-4 h-4 ${isRestoring ? 'animate-spin' : ''}`} />
37
+ {isRestoring ? 'Restoring...' : 'Restore'}
38
+ </button>
39
+ );
40
+ }
@@ -0,0 +1,27 @@
1
+ 'use server';
2
+
3
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
4
+ import { revalidatePath } from 'next/cache';
5
+
6
+ export async function restoreDeletedAgent(agentName: string) {
7
+ const collection = await $provideAgentCollectionForServer();
8
+
9
+ // Find the latest history item for this agent
10
+ const history = await collection.listAgentHistory(agentName);
11
+
12
+ if (history.length === 0) {
13
+ throw new Error(`No history found for agent ${agentName}`);
14
+ }
15
+
16
+ const latestVersion = history[0];
17
+
18
+ if (!latestVersion) {
19
+ throw new Error(`No history found for agent ${agentName}`);
20
+ }
21
+
22
+ await collection.restoreAgent(latestVersion.id);
23
+
24
+ revalidatePath('/recycle-bin');
25
+ revalidatePath(`/agents/${agentName}`);
26
+ revalidatePath('/');
27
+ }
@@ -0,0 +1,58 @@
1
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
2
+ import { TrashIcon } from 'lucide-react';
3
+ import Link from 'next/link';
4
+ import { RestoreAgentButton } from './RestoreAgentButton';
5
+
6
+ export const metadata = {
7
+ title: 'Recycle Bin',
8
+ };
9
+
10
+ export default async function RecycleBinPage() {
11
+ const collection = await $provideAgentCollectionForServer();
12
+ const deletedAgents = await collection.listDeletedAgents();
13
+
14
+ return (
15
+ <div className="container mx-auto p-6 max-w-4xl">
16
+ <header className="flex items-center gap-4 mb-8">
17
+ <div className="bg-red-100 p-3 rounded-full">
18
+ <TrashIcon className="w-8 h-8 text-red-600" />
19
+ </div>
20
+ <div>
21
+ <h1 className="text-3xl font-bold text-gray-900">Recycle Bin</h1>
22
+ <p className="text-gray-600">Restore deleted agents from here.</p>
23
+ </div>
24
+ </header>
25
+
26
+ {deletedAgents.length === 0 ? (
27
+ <div className="text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300">
28
+ <p className="text-gray-500 text-lg">Recycle bin is empty</p>
29
+ <Link href="/" className="text-blue-600 hover:underline mt-2 inline-block">
30
+ Go back to agents
31
+ </Link>
32
+ </div>
33
+ ) : (
34
+ <div className="grid gap-4">
35
+ {deletedAgents.map((agentName) => (
36
+ <div
37
+ key={agentName}
38
+ className="bg-white p-4 rounded-lg shadow-sm border border-gray-200 flex items-center justify-between hover:shadow-md transition-shadow"
39
+ >
40
+ <div className="flex items-center gap-3">
41
+ <div className="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center text-gray-500 font-bold text-lg">
42
+ {agentName.charAt(0).toUpperCase()}
43
+ </div>
44
+ <div>
45
+ <h3 className="font-semibold text-lg text-gray-900">{agentName}</h3>
46
+ <p className="text-sm text-gray-500">Deleted agent</p>
47
+ </div>
48
+ </div>
49
+ <div className="flex gap-2">
50
+ <RestoreAgentButton agentName={agentName} />
51
+ </div>
52
+ </div>
53
+ ))}
54
+ </div>
55
+ )}
56
+ </div>
57
+ );
58
+ }
@@ -0,0 +1,33 @@
1
+ import { ShieldAlert } from 'lucide-react';
2
+ import Link from 'next/link';
3
+
4
+ export default function RestrictedPage() {
5
+ return (
6
+ <div className="flex flex-col items-center justify-center min-h-[calc(100vh-4rem)] p-4 text-center">
7
+ <div className="bg-red-50 p-6 rounded-full mb-6">
8
+ <ShieldAlert className="w-12 h-12 text-red-600" />
9
+ </div>
10
+ <h1 className="text-2xl font-bold text-gray-900 mb-2">Access Restricted</h1>
11
+ <p className="text-gray-600 max-w-md mb-8">
12
+ This server is restricted to authorized users or specific IP addresses.
13
+ Please log in to continue or contact the administrator if you believe this is an error.
14
+ </p>
15
+
16
+ {/* The Login button in the header is available, so we can just point to it or instruct the user */}
17
+ <div className="p-4 bg-blue-50 text-blue-800 rounded-lg max-w-md text-sm">
18
+ <p>
19
+ <strong>Tip:</strong> Use the <strong>Log in</strong> button in the top right corner to access your account.
20
+ </p>
21
+ </div>
22
+
23
+ <div className="mt-8 flex gap-4">
24
+ <Link
25
+ href="/"
26
+ className="px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-md transition-colors"
27
+ >
28
+ Go Home
29
+ </Link>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1 @@
1
+ In this folder we are testing miscellaneous things related to Promptbook Agents Server
@@ -0,0 +1,37 @@
1
+ import { ImageResponse } from 'next/og';
2
+
3
+ export const runtime = 'edge';
4
+
5
+ export const alt = 'Agent Profile';
6
+ export const size = {
7
+ width: 1200,
8
+ height: 630,
9
+ };
10
+
11
+ export const contentType = 'image/png';
12
+
13
+ export default async function Image() {
14
+ return new ImageResponse(
15
+ (
16
+ <div
17
+ style={{
18
+ display: 'flex',
19
+ flexDirection: 'row',
20
+ width: '100%',
21
+ height: '100%',
22
+ backgroundColor: 'white',
23
+ }}
24
+ >
25
+ <h1>Hello</h1>
26
+ </div>
27
+ ),
28
+ {
29
+ ...size,
30
+ emoji: 'openmoji',
31
+ },
32
+ );
33
+ }
34
+
35
+ /**
36
+ * TODO: [webpack.cache.PackFileCacheStrategy] Serializing big strings (126kiB) impacts deserialization performance (consider using Buffer instead and decode when needed)
37
+ */
@@ -0,0 +1,22 @@
1
+ import { NEXT_PUBLIC_SITE_URL } from '@/config';
2
+ import type { Metadata } from 'next';
3
+
4
+ export const metadata: Metadata = {
5
+ metadataBase: NEXT_PUBLIC_SITE_URL,
6
+ title: 'Test OG Image',
7
+ description: 'Testing og-image',
8
+ };
9
+
10
+ export default function TestPage() {
11
+ return (
12
+ <div className="min-h-screen flex items-center justify-center bg-gray-100">
13
+ <div className="bg-white p-8 rounded-lg shadow-md max-w-md w-full">
14
+ <h1 className="text-3xl font-bold text-red-600 mb-4 text-center">Test page</h1>
15
+ <p className="text-gray-700 mb-6 text-center">Testing og-image</p>
16
+ <p className="text-gray-600 text-sm text-center mt-4">
17
+ View page source or share this URL to see the OG image in action
18
+ </p>
19
+ </div>
20
+ </div>
21
+ );
22
+ }