@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,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
+ */
@@ -1,19 +1,25 @@
1
1
  'use server';
2
2
 
3
- import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
4
3
  // import { BookEditor } from '@promptbook-local/components';
5
4
  import { $provideServer } from '@/src/tools/$provideServer';
6
- import { parseAgentSource } from '@promptbook-local/core';
7
- import { Columns2Icon, MessagesSquareIcon, NotebookPenIcon } from 'lucide-react';
5
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
6
+ import { CodeIcon, HistoryIcon, NotebookPenIcon } from 'lucide-react';
8
7
  import { headers } from 'next/headers';
9
8
  import { notFound } from 'next/navigation';
10
9
  import { Color } from '../../../../../../src/utils/color/Color';
11
10
  import { withAlpha } from '../../../../../../src/utils/color/operators/withAlpha';
12
11
  import { $sideEffect } from '../../../../../../src/utils/organization/$sideEffect';
12
+ import { AGENT_ACTIONS, getAgentName, getAgentProfile } from './_utils';
13
13
  import { AgentChatWrapper } from './AgentChatWrapper';
14
14
  import { AgentQrCode } from './AgentQrCode';
15
15
  import { CopyField } from './CopyField';
16
16
  import { generateAgentMetadata } from './generateAgentMetadata';
17
+ import { InstallPwaButton } from './InstallPwaButton';
18
+ import { ServiceWorkerRegister } from './ServiceWorkerRegister';
19
+ import { ClearAgentChatHistoryButton } from './ClearAgentChatHistoryButton';
20
+ import { ClearAgentChatFeedbackButton } from './ClearAgentChatFeedbackButton';
21
+ import { CloneAgentButton } from './CloneAgentButton';
22
+ import { isUserAdmin } from '../../../utils/isUserAdmin';
17
23
  // import { Agent } from '@promptbook-local/core';
18
24
  // import { RemoteLlmExecutionTools } from '@promptbook-local/remote-client';
19
25
  // import { OpenAiAssistantExecutionTools } from '@promptbook-local/openai';
@@ -27,13 +33,12 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
27
33
 
28
34
  $sideEffect(headers());
29
35
 
30
- let { agentName } = await params;
31
- agentName = decodeURIComponent(agentName);
36
+ const agentName = await getAgentName(params);
37
+ const isAdmin = await isUserAdmin();
32
38
 
33
- const collection = await $provideAgentCollectionForServer();
34
- let agentSource;
39
+ let agentProfile;
35
40
  try {
36
- agentSource = await collection.getAgentSource(agentName);
41
+ agentProfile = await getAgentProfile(agentName);
37
42
  } catch (error) {
38
43
  if (
39
44
  error instanceof Error &&
@@ -45,7 +50,6 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
45
50
  }
46
51
  throw error;
47
52
  }
48
- const agentProfile = parseAgentSource(agentSource);
49
53
 
50
54
  const { publicUrl } = await $provideServer();
51
55
 
@@ -58,13 +62,15 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
58
62
  console.log('[🐱‍🚀]', { pageUrl: agentUrl });
59
63
 
60
64
  // Extract brand color from meta
61
- const brandColor = Color.from(agentProfile.meta.color || '#3b82f6'); // Default to blue-600
65
+ const brandColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
62
66
 
63
67
  // Mock agent actions
64
- const agentActions = ['Emails', 'Web chat', 'Read documents', 'Browser', 'WhatsApp', '<Coding/>'];
68
+ const agentActions = AGENT_ACTIONS;
65
69
 
66
70
  return (
67
71
  <div className="flex flex-col md:flex-row h-[calc(100vh-60px)] w-full overflow-hidden">
72
+ <ServiceWorkerRegister scope={`/agents/${encodeURIComponent(agentName)}/`} />
73
+
68
74
  {/* Left sidebar: Profile info */}
69
75
  <div
70
76
  className="w-full md:w-[400px] flex flex-col gap-6 p-6 overflow-y-auto border-r bg-gray-50 flex-shrink-0"
@@ -114,33 +120,46 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
114
120
  </div>
115
121
  </div>
116
122
 
117
- <div className="flex flex-col gap-2 mt-auto">
123
+ <div className="flex flex-col gap-3 mt-auto">
118
124
  <div className="flex gap-2">
119
125
  <a
120
- href={`/agents/${encodeURIComponent(agentName)}/chat`}
126
+ href={`/agents/${encodeURIComponent(agentName)}/book+chat`}
121
127
  // <- TODO: [🧠] Can I append path like this on current browser URL in href?
122
128
  className="flex-1 inline-flex items-center justify-center whitespace-nowrap bg-white hover:bg-gray-100 text-gray-800 px-4 py-2 rounded shadow font-semibold transition border border-gray-200"
123
129
  >
124
- <MessagesSquareIcon className="ml-2 w-4 h-4 mr-2" />
125
- Chat
130
+ <NotebookPenIcon className="ml-2 w-4 h-4 mr-2" />
131
+ Edit
126
132
  </a>
127
133
  <a
128
- href={`/agents/${encodeURIComponent(agentName)}/book+chat`}
134
+ href={`/agents/${encodeURIComponent(agentName)}/integration`}
129
135
  // <- TODO: [🧠] Can I append path like this on current browser URL in href?
130
136
  className="flex-1 inline-flex items-center justify-center whitespace-nowrap bg-white hover:bg-gray-100 text-gray-800 px-4 py-2 rounded shadow font-semibold transition border border-gray-200"
131
137
  >
132
- <Columns2Icon className="ml-2 w-4 h-4 mr-2" />
133
- Book + Chat
138
+ <CodeIcon className="ml-2 w-4 h-4 mr-2" />
139
+ Integration
134
140
  </a>
135
141
  <a
136
- href={`/agents/${encodeURIComponent(agentName)}/book`}
137
- // <- TODO: [🧠] Can I append path like this on current browser URL in href?
142
+ href={`/agents/${encodeURIComponent(agentName)}/history`}
138
143
  className="flex-1 inline-flex items-center justify-center whitespace-nowrap bg-white hover:bg-gray-100 text-gray-800 px-4 py-2 rounded shadow font-semibold transition border border-gray-200"
139
144
  >
140
- <NotebookPenIcon className="ml-2 w-4 h-4 mr-2" />
141
- Edit
145
+ <HistoryIcon className="ml-2 w-4 h-4 mr-2" />
146
+ History
142
147
  </a>
148
+ {isAdmin && <CloneAgentButton agentName={agentName} />}
149
+ <InstallPwaButton />
143
150
  </div>
151
+
152
+ {isAdmin && (
153
+ <div className="border-t border-dashed border-gray-300 pt-3">
154
+ <h2 className="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-600">
155
+ Maintenance
156
+ </h2>
157
+ <div className="flex flex-col gap-2">
158
+ <ClearAgentChatHistoryButton agentName={agentName} />
159
+ <ClearAgentChatFeedbackButton agentName={agentName} />
160
+ </div>
161
+ </div>
162
+ )}
144
163
  </div>
145
164
 
146
165
  <div className="flex flex-col items-center gap-4 pt-6 border-t border-gray-200 w-full">