@promptbook/cli 0.104.0-6 → 0.104.0-8

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 (48) hide show
  1. package/apps/agents-server/config.ts +1 -3
  2. package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +85 -0
  3. package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
  4. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +49 -10
  5. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +0 -3
  6. package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +5 -2
  7. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
  8. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +56 -38
  9. package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +10 -1
  10. package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
  11. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +5 -4
  12. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +5 -4
  13. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +8 -1
  14. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +8 -1
  15. package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +7 -2
  16. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +3 -4
  17. package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +15 -3
  18. package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
  19. package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
  20. package/apps/agents-server/src/app/page.tsx +4 -2
  21. package/apps/agents-server/src/app/recycle-bin/page.tsx +3 -1
  22. package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
  23. package/apps/agents-server/src/app/security.txt/route.ts +1 -1
  24. package/apps/agents-server/src/app/sitemap.xml/route.ts +4 -5
  25. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +22 -13
  26. package/apps/agents-server/src/components/Header/Header.tsx +4 -0
  27. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +46 -9
  28. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +32 -14
  29. package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +22 -6
  30. package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
  31. package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
  32. package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
  33. package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
  34. package/apps/agents-server/src/tools/$provideBrowserForServer.ts +29 -0
  35. package/apps/agents-server/src/tools/$provideCdnForServer.ts +1 -1
  36. package/esm/index.es.js +8 -9
  37. package/esm/index.es.js.map +1 -1
  38. package/esm/typings/servers.d.ts +8 -0
  39. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  40. package/esm/typings/src/_packages/types.index.d.ts +2 -0
  41. package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +2 -2
  42. package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
  43. package/esm/typings/src/types/typeAliases.d.ts +7 -1
  44. package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
  45. package/esm/typings/src/version.d.ts +1 -1
  46. package/package.json +1 -1
  47. package/umd/index.umd.js +8 -9
  48. package/umd/index.umd.js.map +1 -1
@@ -1,4 +1,5 @@
1
- import { generatePlaceholderAgentProfileImageUrl, PROMPTBOOK_COLOR } from '@promptbook-local/core';
1
+ import { $provideServer } from '@/src/tools/$provideServer';
2
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
3
  import { serializeError } from '@promptbook-local/utils';
3
4
  import { ImageResponse } from 'next/og';
4
5
  import { assertsError } from '../../../../../../../../src/errors/assertsError';
@@ -21,6 +22,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
21
22
  const agentProfile = await getAgentProfile(agentName);
22
23
  const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
23
24
  const backgroundColor = agentColor.then(grayscale(0.5));
25
+ const { publicUrl } = await $provideServer();
24
26
 
25
27
  return new ImageResponse(
26
28
  (
@@ -48,10 +50,9 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
48
50
  <img
49
51
  style={{
50
52
  width: '80%',
51
- backgroundColor: agentColor.toHex(),
52
- borderRadius: '50%',
53
+ // backgroundColor: agentColor.toHex(),
53
54
  }}
54
- src={agentProfile.meta.image || agentProfile.permanentId ||generatePlaceholderAgentProfileImageUrl(agentName)}
55
+ src={`${publicUrl.href}agents/${agentProfile.permanentId || agentName}/images/icon-256.png`}
55
56
  alt="Agent Icon"
56
57
  />
57
58
  </div>
@@ -25,6 +25,7 @@ export const generateMetadata = generateAgentMetadata;
25
25
 
26
26
  export default async function AgentIntegrationPage({ params }: { params: Promise<{ agentName: string }> }) {
27
27
  $sideEffect(headers());
28
+
28
29
  const agentName = await getAgentName(params);
29
30
  const isAdmin = await isUserAdmin();
30
31
 
@@ -187,7 +188,13 @@ export default async function AgentIntegrationPage({ params }: { params: Promise
187
188
  {agentProfile.meta.image && (
188
189
  // eslint-disable-next-line @next/next/no-img-element
189
190
  <img
190
- src={agentProfile.meta.image || agentProfile.permanentId ||generatePlaceholderAgentProfileImageUrl(agentName)}
191
+ src={
192
+ agentProfile.meta.image ||
193
+ generatePlaceholderAgentProfileImageUrl(
194
+ agentProfile.permanentId || agentName,
195
+ publicUrl,
196
+ )
197
+ }
191
198
  alt={agentProfile.meta.fullname || agentName}
192
199
  className="w-16 h-16 rounded-full object-cover border-2"
193
200
  style={{ borderColor: primaryColor }}
@@ -18,6 +18,7 @@ export const generateMetadata = generateAgentMetadata;
18
18
 
19
19
  export default async function AgentLinksPage({ params }: { params: Promise<{ agentName: string }> }) {
20
20
  $sideEffect(headers());
21
+
21
22
  const agentName = await getAgentName(params);
22
23
 
23
24
  let agentProfile;
@@ -56,7 +57,13 @@ export default async function AgentLinksPage({ params }: { params: Promise<{ age
56
57
  {agentProfile.meta.image && (
57
58
  // eslint-disable-next-line @next/next/no-img-element
58
59
  <img
59
- src={agentProfile.meta.image || agentProfile.permanentId || generatePlaceholderAgentProfileImageUrl(agentName)}
60
+ src={
61
+ agentProfile.meta.image ||
62
+ generatePlaceholderAgentProfileImageUrl(
63
+ agentProfile.permanentId || agentName,
64
+ publicUrl,
65
+ )
66
+ }
60
67
  alt={agentProfile.meta.fullname || agentName}
61
68
  className="w-16 h-16 rounded-full object-cover border-2"
62
69
  style={{ borderColor: primaryColor }}
@@ -1,3 +1,4 @@
1
+ import { $provideServer } from '@/src/tools/$provideServer';
1
2
  import { generatePlaceholderAgentProfileImageUrl, PROMPTBOOK_COLOR } from '@promptbook-local/core';
2
3
  import { serializeError } from '@promptbook-local/utils';
3
4
  import { ImageResponse } from 'next/og';
@@ -24,6 +25,7 @@ export default async function Image({ params }: { params: Promise<{ agentName: s
24
25
  const agentProfile = await getAgentProfile(agentName);
25
26
  const agentColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
26
27
  const backgroundColor = agentColor.then(grayscale(0.5));
28
+ const { publicUrl } = await $provideServer();
27
29
 
28
30
  return new ImageResponse(
29
31
  (
@@ -53,11 +55,14 @@ export default async function Image({ params }: { params: Promise<{ agentName: s
53
55
  width: '80%',
54
56
  backgroundColor: agentColor.toHex(),
55
57
  borderRadius: '50%',
58
+ aspectRatio: '1 / 1',
56
59
  }}
57
60
  src={
58
61
  agentProfile.meta.image ||
59
- agentProfile.permanentId ||
60
- generatePlaceholderAgentProfileImageUrl(agentProfile.permanentId || agentName)
62
+ generatePlaceholderAgentProfileImageUrl(
63
+ agentProfile.permanentId || agentName,
64
+ publicUrl,
65
+ )
61
66
  }
62
67
  alt="Agent Icon"
63
68
  />
@@ -27,6 +27,7 @@ export default async function AgentPage({
27
27
  const isAdmin = await isUserAdmin();
28
28
  const { headless: headlessParam } = await searchParams;
29
29
  const isHeadless = headlessParam !== undefined;
30
+ const { publicUrl } = await $provideServer();
30
31
 
31
32
  let agentProfile;
32
33
  try {
@@ -44,8 +45,6 @@ export default async function AgentPage({
44
45
  throw error;
45
46
  }
46
47
 
47
- const { publicUrl } = await $provideServer();
48
-
49
48
  // Build agent page URL for QR and copy
50
49
  const agentUrl = `${publicUrl.href}${encodeURIComponent(agentName)}`;
51
50
  // <- TODO: [🐱‍🚀] Better
@@ -64,6 +63,7 @@ export default async function AgentPage({
64
63
  <AgentProfileWrapper
65
64
  agent={agentProfile}
66
65
  agentUrl={agentUrl}
66
+ publicUrl={publicUrl}
67
67
  agentEmail={agentEmail}
68
68
  agentName={agentName}
69
69
  brandColorHex={brandColorHex}
@@ -97,8 +97,7 @@ export default async function AgentPage({
97
97
  brandColorHex={brandColorHex}
98
98
  avatarSrc={
99
99
  agentProfile.meta.image ||
100
- agentProfile.permanentId ||
101
- generatePlaceholderAgentProfileImageUrl(agentName)
100
+ generatePlaceholderAgentProfileImageUrl(agentProfile.permanentId || agentName, publicUrl)
102
101
  }
103
102
  isDeleted={isDeleted}
104
103
  />
@@ -1,6 +1,8 @@
1
1
  'use server';
2
2
 
3
3
  import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
4
+ import { $provideServer } from '@/src/tools/$provideServer';
5
+ import { generatePlaceholderAgentProfileImageUrl } from '@promptbook-local/core';
4
6
  import { ArrowLeftIcon, FileTextIcon } from 'lucide-react';
5
7
  import { headers } from 'next/headers';
6
8
  import Link from 'next/link';
@@ -8,12 +10,14 @@ import { notFound } from 'next/navigation';
8
10
  import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
9
11
  import { getAgentName, getAgentProfile } from '../_utils';
10
12
  import { generateAgentMetadata } from '../generateAgentMetadata';
11
- import { generatePlaceholderAgentProfileImageUrl } from '@promptbook-local/core';
12
13
 
13
14
  export const generateMetadata = generateAgentMetadata;
14
15
 
15
16
  export default async function AgentSystemMessagePage({ params }: { params: Promise<{ agentName: string }> }) {
16
17
  $sideEffect(headers());
18
+
19
+ const { publicUrl } = await $provideServer();
20
+
17
21
  const agentName = await getAgentName(params);
18
22
 
19
23
  let agentProfile;
@@ -45,7 +49,13 @@ export default async function AgentSystemMessagePage({ params }: { params: Promi
45
49
  {agentProfile.meta.image && (
46
50
  // eslint-disable-next-line @next/next/no-img-element
47
51
  <img
48
- src={agentProfile.meta.image|| agentProfile.permanentId ||generatePlaceholderAgentProfileImageUrl(agentName)}
52
+ src={
53
+ agentProfile.meta.image ||
54
+ generatePlaceholderAgentProfileImageUrl(
55
+ agentProfile.permanentId || agentName,
56
+ publicUrl,
57
+ )
58
+ }
49
59
  alt={agentProfile.meta.fullname || agentName}
50
60
  className="w-16 h-16 rounded-full object-cover border-2 border-gray-200"
51
61
  />
@@ -77,7 +87,9 @@ export default async function AgentSystemMessagePage({ params }: { params: Promi
77
87
  <div className="mt-6 bg-blue-50 rounded-lg p-4 border border-blue-200">
78
88
  <h3 className="text-md font-semibold text-blue-900 mb-2">Model Requirements</h3>
79
89
  <div className="text-sm text-blue-800">
80
- <p><strong>Model Variant:</strong> CHAT</p>
90
+ <p>
91
+ <strong>Model Variant:</strong> CHAT
92
+ </p>
81
93
  {/* TODO: [🧠] Add more model requirements if available */}
82
94
  </div>
83
95
  </div>
@@ -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
+ }
@@ -6,7 +6,7 @@ import { NextResponse } from 'next/server';
6
6
  export const dynamic = 'force-dynamic';
7
7
 
8
8
  export async function GET() {
9
- const txt = generateHumansTxt();
9
+ const txt = await generateHumansTxt();
10
10
  return new NextResponse(txt, {
11
11
  headers: {
12
12
  'Content-Type': 'text/plain',
@@ -34,6 +34,8 @@ const calendarWithSeconds = {
34
34
  export default async function HomePage() {
35
35
  $sideEffect(/* Note: [🐶] This will ensure dynamic rendering of page and avoid Next.js pre-render */ headers());
36
36
 
37
+ const { publicUrl } = await $provideServer();
38
+
37
39
  const currentUser = await getCurrentUser();
38
40
  const isAdmin = await isUserAdmin(); /* <- TODO: [👹] Here should be user permissions */
39
41
 
@@ -94,9 +96,9 @@ export default async function HomePage() {
94
96
  return (
95
97
  <div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
96
98
  <div className="container mx-auto px-4 py-16">
97
- <AgentsList agents={[...agents]} isAdmin={isAdmin} />
99
+ <AgentsList agents={[...agents]} isAdmin={isAdmin} publicUrl={publicUrl} />
98
100
 
99
- <ExternalAgentsSectionClient />
101
+ <ExternalAgentsSectionClient publicUrl={publicUrl} />
100
102
 
101
103
  {isAdmin && <UsersList allowCreate={false} />}
102
104
 
@@ -1,4 +1,5 @@
1
1
  import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
2
+ import { $provideServer } from '@/src/tools/$provideServer';
2
3
  import { TrashIcon } from 'lucide-react';
3
4
  import Link from 'next/link';
4
5
  import { DeletedAgentsList } from '../../components/Homepage/DeletedAgentsList';
@@ -9,6 +10,7 @@ export const metadata = {
9
10
  };
10
11
 
11
12
  export default async function RecycleBinPage() {
13
+ const { publicUrl } = await $provideServer();
12
14
  const collection = await $provideAgentCollectionForServer();
13
15
  const deletedAgents = await collection.listDeletedAgents();
14
16
  const isAdmin = await isUserAdmin();
@@ -34,7 +36,7 @@ export default async function RecycleBinPage() {
34
36
  </Link>
35
37
  </div>
36
38
  ) : (
37
- <DeletedAgentsList agents={deletedAgents} isAdmin={isAdmin} />
39
+ <DeletedAgentsList agents={deletedAgents} isAdmin={isAdmin} publicUrl={publicUrl} />
38
40
  )}
39
41
  </div>
40
42
  </div>
@@ -6,7 +6,7 @@ import { NextResponse } from 'next/server';
6
6
  export const dynamic = 'force-dynamic';
7
7
 
8
8
  export async function GET() {
9
- const txt = generateRobotsTxt();
9
+ const txt = await generateRobotsTxt();
10
10
  return new NextResponse(txt, {
11
11
  headers: {
12
12
  'Content-Type': 'text/plain',
@@ -6,7 +6,7 @@ import { NextResponse } from 'next/server';
6
6
  export const dynamic = 'force-dynamic';
7
7
 
8
8
  export async function GET() {
9
- const txt = generateSecurityTxt();
9
+ const txt = await generateSecurityTxt();
10
10
  return new NextResponse(txt, {
11
11
  headers: {
12
12
  'Content-Type': 'text/plain',
@@ -1,25 +1,24 @@
1
1
  // Dynamic sitemap.xml for Agents Server
2
2
 
3
- import { NEXT_PUBLIC_SITE_URL } from '@/config';
4
3
  import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
4
+ import { $provideServer } from '@/src/tools/$provideServer';
5
5
  import { spaceTrim } from '@promptbook-local/utils';
6
6
  import { NextResponse } from 'next/server';
7
7
 
8
8
  export const dynamic = 'force-dynamic';
9
9
 
10
10
  export async function GET() {
11
+ const { publicUrl } = await $provideServer();
12
+
11
13
  const collection = await $provideAgentCollectionForServer();
12
14
 
13
15
  // Assume collection.listAgents() returns an array of agent names
14
16
  const agents = await collection.listAgents();
15
17
 
16
- // Get base URL from environment or config
17
- const baseUrl = NEXT_PUBLIC_SITE_URL?.href || process.env.PUBLIC_URL || 'https://ptbk.io';
18
-
19
18
  const urls = agents
20
19
  .map(
21
20
  ({ permanentId, agentName }) =>
22
- `<url><loc>${baseUrl}agents/${encodeURIComponent(permanentId || agentName)}</loc></url>`,
21
+ `<url><loc>${publicUrl.href}agents/${encodeURIComponent(permanentId || agentName)}</loc></url>`,
23
22
  )
24
23
  .join('\n');
25
24
 
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { colorToDataUrl } from '@promptbook-local/color';
3
4
  import { generatePlaceholderAgentProfileImageUrl } from '@promptbook-local/core';
4
5
  import { AgentBasicInformation, string_agent_permanent_id } from '@promptbook-local/types';
5
6
  import { RepeatIcon } from 'lucide-react';
@@ -26,6 +27,11 @@ type AgentProfileProps = {
26
27
  */
27
28
  readonly agentUrl?: string;
28
29
 
30
+ /**
31
+ * Base URL of the agents server
32
+ */
33
+ readonly publicUrl: URL;
34
+
29
35
  /**
30
36
  * Email of the agent
31
37
  */
@@ -64,6 +70,7 @@ export function AgentProfile(props: AgentProfileProps) {
64
70
  agent,
65
71
  agentUrl = '',
66
72
  agentEmail = '',
73
+ publicUrl,
67
74
  permanentId,
68
75
  renderMenu,
69
76
  children,
@@ -71,11 +78,11 @@ export function AgentProfile(props: AgentProfileProps) {
71
78
  isHeadless = false,
72
79
  className,
73
80
  } = props;
74
- console.log('!!!!', { agent });
81
+
75
82
  const { meta, agentName } = agent;
76
83
  const fullname = (meta.fullname as string) || agentName || 'Agent';
77
84
  const personaDescription = agent.personaDescription || '';
78
- const imageUrl = meta.image || generatePlaceholderAgentProfileImageUrl(permanentId);
85
+ const imageUrl = meta.image || generatePlaceholderAgentProfileImageUrl(permanentId, publicUrl);
79
86
 
80
87
  const [isQrModalOpen, setIsQrModalOpen] = useState(false);
81
88
  const [isFlipped, setIsFlipped] = useState(false);
@@ -153,17 +160,19 @@ export function AgentProfile(props: AgentProfileProps) {
153
160
  // ['cornerShape' as really_any /* <- Note: `cornerShape` is non standard CSS property */]: 'squircle ',
154
161
  }}
155
162
  >
156
- {imageUrl ? (
157
- // eslint-disable-next-line @next/next/no-img-element
158
- <img src={imageUrl} alt={fullname} className="w-full h-full object-cover" />
159
- ) : (
160
- <div
161
- className="w-full h-full flex items-center justify-center text-3xl md:text-8xl font-bold text-white/80"
162
- style={{ backgroundColor: brandColorDarkHex }}
163
- >
164
- {fullname.charAt(0).toUpperCase()}
165
- </div>
166
- )}
163
+ {/* eslint-disable-next-line @next/next/no-img-element */}
164
+ <img
165
+ src={imageUrl}
166
+ alt={fullname}
167
+ className="w-full h-full object-cover"
168
+ // width={1024}
169
+ // height={1792}
170
+ // <- TODO: [🤐] DRY
171
+ style={{
172
+ objectFit: 'cover',
173
+ backgroundImage: `url(${colorToDataUrl(brandColorLightHex)})`,
174
+ }}
175
+ />
167
176
 
168
177
  {/* Flip hint icon */}
169
178
  <div className="absolute bottom-2 md:bottom-4 right-2 md:right-4 bg-black/30 p-1 md:p-2 rounded-full text-white/80 backdrop-blur-md opacity-0 group-hover:opacity-100 transition-opacity">
@@ -305,6 +305,10 @@ export function Header(props: HeaderProps) {
305
305
  label: 'Chat feedback',
306
306
  href: '/admin/chat-feedback',
307
307
  },
308
+ {
309
+ label: 'Browser',
310
+ href: '/admin/browser-test',
311
+ },
308
312
  ],
309
313
  },
310
314
  {
@@ -8,14 +8,50 @@ import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/
8
8
  import { useAgentBackground } from '../AgentProfile/useAgentBackground';
9
9
 
10
10
  type AgentCardProps = {
11
- agent: AgentBasicInformation;
12
- href: string;
13
- isAdmin?: boolean;
14
- onDelete?: (agentIdentifier: string) => void;
15
- onClone?: (agentIdentifier: string) => void;
16
- onToggleVisibility?: (agentIdentifier: string) => void;
17
- onRestore?: (agentIdentifier: string) => void;
18
- visibility?: 'PUBLIC' | 'PRIVATE';
11
+ /**
12
+ * @@@
13
+ */
14
+ readonly agent: AgentBasicInformation;
15
+
16
+ /**
17
+ * @@@
18
+ */
19
+ readonly href: string;
20
+
21
+ /**
22
+ * Base URL of the agents server
23
+ */
24
+ readonly publicUrl: URL;
25
+
26
+ /**
27
+ * @@@
28
+ */
29
+ readonly isAdmin?: boolean;
30
+
31
+ /**
32
+ * @@@
33
+ */
34
+ readonly onDelete?: (agentIdentifier: string) => void;
35
+
36
+ /**
37
+ * @@@
38
+ */
39
+ readonly onClone?: (agentIdentifier: string) => void;
40
+
41
+ /**
42
+ * @@@
43
+ */
44
+ readonly onToggleVisibility?: (agentIdentifier: string) => void;
45
+
46
+ /**
47
+ * @@@
48
+ */
49
+ readonly onRestore?: (agentIdentifier: string) => void;
50
+
51
+ /**
52
+ * @@@
53
+ */
54
+ readonly visibility?: 'PUBLIC' | 'PRIVATE';
19
55
  };
20
56
 
21
57
  const ACTION_BUTTON_CLASSES =
@@ -25,6 +61,7 @@ export function AgentCard({
25
61
  agent,
26
62
  href,
27
63
  isAdmin,
64
+ publicUrl,
28
65
  onDelete,
29
66
  onClone,
30
67
  onToggleVisibility,
@@ -33,7 +70,7 @@ export function AgentCard({
33
70
  }: AgentCardProps) {
34
71
  const { meta, agentName } = agent;
35
72
  const fullname = (meta.fullname as string) || agentName || 'Agent';
36
- const imageUrl = meta.image || generatePlaceholderAgentProfileImageUrl(agentName);
73
+ const imageUrl = meta.image || generatePlaceholderAgentProfileImageUrl(agentName, publicUrl);
37
74
  const personaDescription = agent.personaDescription || '';
38
75
 
39
76
  const { brandColorLightHex, brandColorDarkHex, backgroundImage } = useAgentBackground(meta.color);
@@ -1,10 +1,10 @@
1
1
  // Client Component for rendering and deleting agents
2
2
  'use client';
3
3
 
4
- import React, { useState } from 'react';
5
- import { useRouter } from 'next/navigation';
6
4
  import { TrashIcon } from 'lucide-react';
7
5
  import Link from 'next/link';
6
+ import { useRouter } from 'next/navigation';
7
+ import { useState } from 'react';
8
8
  import { AddAgentButton } from '../../app/AddAgentButton';
9
9
  import { AgentCard } from './AgentCard';
10
10
  import { Section } from './Section';
@@ -16,16 +16,29 @@ type AgentWithVisibility = AgentBasicInformation & {
16
16
  };
17
17
 
18
18
  type AgentsListProps = {
19
- agents: AgentWithVisibility[];
20
- isAdmin: boolean;
19
+ /**
20
+ * @@@
21
+ */
22
+ readonly agents: AgentWithVisibility[];
23
+
24
+ /**
25
+ * @@@
26
+ */
27
+ readonly isAdmin: boolean;
28
+
29
+ /**
30
+ * Base URL of the agents server
31
+ */
32
+ readonly publicUrl: URL;
21
33
  };
22
34
 
23
- export function AgentsList({ agents: initialAgents, isAdmin }: AgentsListProps) {
35
+ export function AgentsList(props: AgentsListProps) {
36
+ const { agents: initialAgents, isAdmin, publicUrl } = props;
24
37
  const router = useRouter();
25
38
  const [agents, setAgents] = useState(Array.from(initialAgents));
26
39
 
27
40
  const handleDelete = async (agentIdentifier: string) => {
28
- const agent = agents.find(a => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
41
+ const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
29
42
  if (!agent) return;
30
43
  if (!window.confirm(`Delete agent "${agent.agentName}"? It will be moved to Recycle Bin.`)) return;
31
44
 
@@ -45,12 +58,14 @@ export function AgentsList({ agents: initialAgents, isAdmin }: AgentsListProps)
45
58
  };
46
59
 
47
60
  const handleClone = async (agentIdentifier: string) => {
48
- const agent = agents.find(a => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
61
+ const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
49
62
  if (!agent) return;
50
63
  if (!window.confirm(`Clone agent "${agent.agentName}"?`)) return;
51
64
 
52
65
  try {
53
- const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}/clone`, { method: 'POST' });
66
+ const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}/clone`, {
67
+ method: 'POST',
68
+ });
54
69
  if (response.ok) {
55
70
  const newAgent = await response.json();
56
71
  setAgents([...agents, newAgent]);
@@ -64,7 +79,7 @@ export function AgentsList({ agents: initialAgents, isAdmin }: AgentsListProps)
64
79
  };
65
80
 
66
81
  const handleToggleVisibility = async (agentIdentifier: string) => {
67
- const agent = agents.find(a => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
82
+ const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
68
83
  if (!agent) return;
69
84
 
70
85
  const newVisibility = agent.visibility === 'PUBLIC' ? 'PRIVATE' : 'PUBLIC';
@@ -78,11 +93,13 @@ export function AgentsList({ agents: initialAgents, isAdmin }: AgentsListProps)
78
93
 
79
94
  if (response.ok) {
80
95
  // Update the local state
81
- setAgents(agents.map(a =>
82
- a.permanentId === agent.permanentId || a.agentName === agent.agentName
83
- ? { ...a, visibility: newVisibility }
84
- : a
85
- ));
96
+ setAgents(
97
+ agents.map((a) =>
98
+ a.permanentId === agent.permanentId || a.agentName === agent.agentName
99
+ ? { ...a, visibility: newVisibility }
100
+ : a,
101
+ ),
102
+ );
86
103
  router.refresh(); // Refresh server data to ensure consistency
87
104
  } else {
88
105
  alert('Failed to update agent visibility');
@@ -95,6 +112,7 @@ export function AgentsList({ agents: initialAgents, isAdmin }: AgentsListProps)
95
112
  <AgentCard
96
113
  key={agent.permanentId || agent.agentName}
97
114
  agent={agent}
115
+ publicUrl={publicUrl}
98
116
  href={`/agents/${encodeURIComponent(agent.permanentId || agent.agentName)}`}
99
117
  isAdmin={isAdmin}
100
118
  onDelete={handleDelete}
@@ -1,26 +1,41 @@
1
1
  // Client Component for rendering deleted agents
2
2
  'use client';
3
3
 
4
- import React, { useState } from 'react';
4
+ import { useState } from 'react';
5
5
  import { AgentCard } from './AgentCard';
6
6
 
7
7
  import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
8
8
 
9
9
  type DeletedAgentsListProps = {
10
- agents: readonly AgentBasicInformation[];
11
- isAdmin: boolean;
10
+ /**
11
+ * @@@
12
+ */
13
+ readonly agents: readonly AgentBasicInformation[];
14
+
15
+ /**
16
+ * @@@
17
+ */
18
+ readonly isAdmin: boolean;
19
+
20
+ /**
21
+ * Base URL of the agents server
22
+ */
23
+ readonly publicUrl: URL;
12
24
  };
13
25
 
14
- export function DeletedAgentsList({ agents: initialAgents, isAdmin }: DeletedAgentsListProps) {
26
+ export function DeletedAgentsList(props: DeletedAgentsListProps) {
27
+ const { agents: initialAgents, isAdmin, publicUrl } = props;
15
28
  const [agents, setAgents] = useState(Array.from(initialAgents));
16
29
 
17
30
  const handleRestore = async (agentIdentifier: string) => {
18
- const agent = agents.find(a => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
31
+ const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
19
32
  if (!agent) return;
20
33
  if (!window.confirm(`Restore agent "${agent.agentName}"?`)) return;
21
34
 
22
35
  try {
23
- const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}/restore`, { method: 'POST' });
36
+ const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}/restore`, {
37
+ method: 'POST',
38
+ });
24
39
  if (response.ok) {
25
40
  // Update local state immediately
26
41
  setAgents(agents.filter((a) => a.permanentId !== agent.permanentId && a.agentName !== agent.agentName));
@@ -40,6 +55,7 @@ export function DeletedAgentsList({ agents: initialAgents, isAdmin }: DeletedAge
40
55
  <AgentCard
41
56
  key={agent.permanentId || agent.agentName}
42
57
  agent={agent}
58
+ publicUrl={publicUrl}
43
59
  href={`/agents/${encodeURIComponent(agent.permanentId || agent.agentName)}`}
44
60
  isAdmin={isAdmin}
45
61
  onRestore={handleRestore}