@promptbook/cli 0.103.0-48 → 0.103.0-49
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.
- package/apps/agents-server/README.md +1 -1
- package/apps/agents-server/TODO.txt +6 -5
- package/apps/agents-server/config.ts +130 -0
- package/apps/agents-server/next.config.ts +1 -1
- package/apps/agents-server/public/fonts/OpenMoji-black-glyf.woff2 +0 -0
- package/apps/agents-server/public/fonts/download-font.js +22 -0
- package/apps/agents-server/src/app/[agentName]/[...rest]/page.tsx +6 -0
- package/apps/agents-server/src/app/[agentName]/page.tsx +1 -0
- package/apps/agents-server/src/app/actions.ts +37 -2
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/AgentQrCode.tsx +55 -0
- package/apps/agents-server/src/app/agents/[agentName]/AgentUrlCopy.tsx +4 -5
- package/apps/agents-server/src/app/agents/[agentName]/CopyField.tsx +44 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +100 -24
- package/apps/agents-server/src/app/agents/[agentName]/api/feedback/route.ts +54 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/route.ts +6 -6
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts +3 -3
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +6 -7
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +4 -5
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +9 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +23 -0
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/{AgentBookAndChatComponent.tsx → AgentBookAndChatComponent.tsx.todo} +4 -4
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +28 -17
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx.todo +21 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatWrapper.tsx +34 -4
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +4 -1
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +42 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +109 -106
- package/apps/agents-server/src/app/agents/page.tsx +1 -1
- package/apps/agents-server/src/app/api/auth/login/route.ts +65 -0
- package/apps/agents-server/src/app/api/auth/logout/route.ts +7 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +116 -0
- package/apps/agents-server/src/app/api/upload/route.ts +7 -3
- package/apps/agents-server/src/app/api/users/[username]/route.ts +75 -0
- package/apps/agents-server/src/app/api/users/route.ts +71 -0
- package/apps/agents-server/src/app/globals.css +35 -1
- package/apps/agents-server/src/app/layout.tsx +43 -23
- package/apps/agents-server/src/app/metadata/MetadataClient.tsx +271 -0
- package/apps/agents-server/src/app/metadata/page.tsx +13 -0
- package/apps/agents-server/src/app/not-found.tsx +5 -0
- package/apps/agents-server/src/app/page.tsx +84 -46
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +123 -0
- package/apps/agents-server/src/components/ErrorPage/ErrorPage.tsx +33 -0
- package/apps/agents-server/src/components/ForbiddenPage/ForbiddenPage.tsx +15 -0
- package/apps/agents-server/src/components/Header/Header.tsx +146 -0
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +27 -0
- package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +40 -0
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +109 -0
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +17 -0
- package/apps/agents-server/src/components/UsersList/UsersList.tsx +190 -0
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +60 -0
- package/apps/agents-server/src/database/$getTableName.ts +18 -0
- package/apps/agents-server/src/database/$provideSupabase.ts +2 -2
- package/apps/agents-server/src/database/$provideSupabaseForServer.ts +3 -3
- package/apps/agents-server/src/database/getMetadata.ts +31 -0
- package/apps/agents-server/src/database/metadataDefaults.ts +32 -0
- package/apps/agents-server/src/database/schema.sql +81 -33
- package/apps/agents-server/src/database/schema.ts +35 -1
- package/apps/agents-server/src/middleware.ts +162 -0
- package/apps/agents-server/src/tools/$provideAgentCollectionForServer.ts +11 -7
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +1 -1
- package/apps/agents-server/src/tools/$provideExecutionToolsForServer.ts +11 -13
- package/apps/agents-server/src/tools/$provideOpenAiAssistantExecutionToolsForServer.ts +7 -7
- package/apps/agents-server/src/tools/$provideServer.ts +39 -0
- package/apps/agents-server/src/utils/auth.ts +33 -0
- package/apps/agents-server/src/utils/cdn/utils/nameToSubfolderPath.ts +1 -1
- package/apps/agents-server/src/utils/getCurrentUser.ts +32 -0
- package/apps/agents-server/src/utils/isIpAllowed.ts +101 -0
- package/apps/agents-server/src/utils/isUserAdmin.ts +31 -0
- package/apps/agents-server/src/utils/session.ts +50 -0
- package/apps/agents-server/tailwind.config.ts +2 -0
- package/esm/index.es.js +147 -31
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +1 -0
- package/esm/typings/src/_packages/types.index.d.ts +2 -0
- package/esm/typings/src/_packages/utils.index.d.ts +2 -0
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +12 -2
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +14 -8
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabaseOptions.d.ts +10 -0
- package/esm/typings/src/commitments/MESSAGE/InitialMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/config.d.ts +1 -0
- package/esm/typings/src/errors/DatabaseError.d.ts +2 -2
- package/esm/typings/src/errors/WrappedError.d.ts +2 -2
- package/esm/typings/src/execution/ExecutionTask.d.ts +2 -2
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +6 -1
- package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizardOrCli.d.ts +2 -2
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +11 -3
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +6 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +6 -2
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +6 -1
- package/esm/typings/src/remote-server/startAgentServer.d.ts +2 -2
- package/esm/typings/src/utils/color/Color.d.ts +7 -0
- package/esm/typings/src/utils/color/Color.test.d.ts +1 -0
- package/esm/typings/src/utils/environment/$getGlobalScope.d.ts +2 -2
- package/esm/typings/src/utils/misc/computeHash.d.ts +11 -0
- package/esm/typings/src/utils/misc/computeHash.test.d.ts +1 -0
- package/esm/typings/src/utils/organization/$sideEffect.d.ts +2 -2
- package/esm/typings/src/utils/organization/$side_effect.d.ts +2 -2
- package/esm/typings/src/utils/organization/TODO_USE.d.ts +2 -2
- package/esm/typings/src/utils/organization/keepUnused.d.ts +2 -2
- package/esm/typings/src/utils/organization/preserve.d.ts +3 -3
- package/esm/typings/src/utils/organization/really_any.d.ts +7 -0
- package/esm/typings/src/utils/serialization/asSerializable.d.ts +2 -2
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +147 -31
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/config.ts.todo +0 -38
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
@import 'tailwindcss/components';
|
|
3
3
|
@import 'tailwindcss/utilities';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* OpenMoji black and white CSS
|
|
7
|
+
*
|
|
8
|
+
* https://github.com/hfg-gmuend/openmoji/blob/master/font/OpenMoji-black-glyf/openmoji.css
|
|
9
|
+
*/
|
|
10
|
+
@font-face {
|
|
11
|
+
font-family: 'OpenMojiBlack';
|
|
12
|
+
src: url('/fonts/OpenMoji-black-glyf.woff2') format('woff2');
|
|
13
|
+
unicode-range: U+23, U+2A, U+2D, U+30-39, U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2117, U+2120, U+2122,
|
|
14
|
+
U+2139, U+2194-2199, U+21A9, U+21AA, U+229C, U+231A, U+231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FE, U+24C2,
|
|
15
|
+
U+25A1, U+25AA-25AE, U+25B6, U+25C0, U+25C9, U+25D0, U+25D1, U+25E7-25EA, U+25ED, U+25EE, U+25FB-25FE,
|
|
16
|
+
U+2600-2605, U+260E, U+2611, U+2614, U+2615, U+2618, U+261D, U+2620, U+2622, U+2623, U+2626, U+262A, U+262E,
|
|
17
|
+
U+262F, U+2638-263A, U+2640, U+2642, U+2648-2653, U+265F, U+2660, U+2663, U+2665, U+2666, U+2668, U+267B,
|
|
18
|
+
U+267E, U+267F, U+2691-2697, U+2699, U+269B, U+269C, U+26A0, U+26A1, U+26A7, U+26AA, U+26AB, U+26B0, U+26B1,
|
|
19
|
+
U+26BD, U+26BE, U+26C4, U+26C5, U+26C8, U+26CE, U+26CF, U+26D1, U+26D3, U+26D4, U+26E9, U+26EA, U+26F0-26F5,
|
|
20
|
+
U+26F7-26FA, U+26FD, U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728,
|
|
21
|
+
U+2733, U+2734, U+2744, U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763, U+2764, U+2795-2797, U+27A1,
|
|
22
|
+
U+27B0, U+27BF, U+2934, U+2935, U+2B05-2B07, U+2B0C, U+2B0D, U+2B1B, U+2B1C, U+2B1F-2B24, U+2B2E, U+2B2F,
|
|
23
|
+
U+2B50, U+2B55, U+2B58, U+2B8F, U+2BBA-2BBC, U+2BC3, U+2BC4, U+2BEA, U+2BEB, U+3030, U+303D, U+3297, U+3299,
|
|
24
|
+
U+E000-E009, U+E010, U+E011, U+E040-E06D, U+E080-E0B4, U+E0C0-E0CC, U+E0FF-E10D, U+E140-E14A, U+E150-E157,
|
|
25
|
+
U+E181-E189, U+E1C0-E1C4, U+E1C6-E1D9, U+E200-E216, U+E240-E269, U+E280-E283, U+E2C0-E2C4, U+E2C6-E2DA,
|
|
26
|
+
U+E300-E303, U+E305-E30F, U+E312-E316, U+E318-E322, U+E324-E329, U+E32B, U+E340-E348, U+E380, U+E381, U+F000,
|
|
27
|
+
U+F77A, U+F8FF, U+FE0F, U+1F004, U+1F0CF, U+1F10D-1F10F, U+1F12F, U+1F16D-1F171, U+1F17E, U+1F17F, U+1F18E,
|
|
28
|
+
U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201, U+1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250, U+1F251,
|
|
29
|
+
U+1F260-1F265, U+1F300-1F321, U+1F324-1F393, U+1F396, U+1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5,
|
|
30
|
+
U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E, U+1F550-1F567, U+1F56F, U+1F570, U+1F573-1F57A, U+1F587,
|
|
31
|
+
U+1F58A-1F58D, U+1F590, U+1F595, U+1F596, U+1F5A4, U+1F5A5, U+1F5A8, U+1F5B1, U+1F5B2, U+1F5BC, U+1F5C2-1F5C4,
|
|
32
|
+
U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3, U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5,
|
|
33
|
+
U+1F6CB-1F6D2, U+1F6D5-1F6D7, U+1F6DC-1F6E5, U+1F6E9, U+1F6EB, U+1F6EC, U+1F6F0, U+1F6F3-1F6FC, U+1F7E0-1F7EB,
|
|
34
|
+
U+1F7F0, U+1F90C-1F93A, U+1F93C-1F945, U+1F947-1F9FF, U+1FA70-1FA7C, U+1FA80-1FA88, U+1FA90-1FABD,
|
|
35
|
+
U+1FABF-1FAC5, U+1FACE-1FADB, U+1FAE0-1FAE8, U+1FAF0-1FAF8, U+1FBC5-1FBC9, U+E0061-E0067, U+E0069,
|
|
36
|
+
U+E006C-E0079, U+E007F;
|
|
37
|
+
}
|
|
38
|
+
|
|
5
39
|
:root {
|
|
6
40
|
--background: #ffffff;
|
|
7
41
|
--foreground: #171717;
|
|
@@ -17,7 +51,7 @@
|
|
|
17
51
|
body {
|
|
18
52
|
background: var(--background);
|
|
19
53
|
color: var(--foreground);
|
|
20
|
-
font-family:
|
|
54
|
+
font-family: var(--font-barlow-condensed), 'OpenMojiBlack', Arial, Helvetica, sans-serif;
|
|
21
55
|
}
|
|
22
56
|
|
|
23
57
|
/* Custom utilities */
|
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
import faviconLogoImage from '@/public/favicon.ico';
|
|
2
|
+
import { LayoutWrapper } from '@/src/components/LayoutWrapper/LayoutWrapper';
|
|
2
3
|
import type { Metadata } from 'next';
|
|
3
4
|
import { Barlow_Condensed } from 'next/font/google';
|
|
5
|
+
import { getMetadata } from '../database/getMetadata';
|
|
6
|
+
import { isUserAdmin } from '../utils/isUserAdmin';
|
|
4
7
|
import './globals.css';
|
|
5
8
|
|
|
6
9
|
const barlowCondensed = Barlow_Condensed({
|
|
7
10
|
subsets: ['latin'],
|
|
8
11
|
weight: ['300', '400', '500', '600', '700'],
|
|
12
|
+
variable: '--font-barlow-condensed',
|
|
9
13
|
});
|
|
10
14
|
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
title:
|
|
18
|
-
description:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
16
|
+
const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
|
|
17
|
+
const serverDescription = (await getMetadata('SERVER_DESCRIPTION')) || 'Agents server powered by Promptbook';
|
|
18
|
+
const serverUrl = (await getMetadata('SERVER_URL')) || 'https://ptbk.io';
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
title: serverName,
|
|
22
|
+
description: serverDescription,
|
|
23
|
+
// TODO: keywords: ['@@@'],
|
|
24
|
+
authors: [{ name: 'Promptbook Team' }],
|
|
25
|
+
openGraph: {
|
|
26
|
+
title: serverName,
|
|
27
|
+
description: serverDescription,
|
|
28
|
+
type: 'website',
|
|
29
|
+
images: [
|
|
30
|
+
/*
|
|
22
31
|
TODO:
|
|
23
32
|
{
|
|
24
33
|
url: 'https://www.ptbk.io/design',
|
|
@@ -27,21 +36,28 @@ export const metadata: Metadata = {
|
|
|
27
36
|
alt: 'Promptbook agents server',
|
|
28
37
|
},
|
|
29
38
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
twitter: {
|
|
42
|
+
card: 'summary_large_image',
|
|
43
|
+
title: serverName,
|
|
44
|
+
description: serverDescription,
|
|
45
|
+
// TODO: images: ['https://www.ptbk.io/design'],
|
|
46
|
+
},
|
|
47
|
+
metadataBase: new URL(serverUrl),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
39
50
|
|
|
40
|
-
export default function RootLayout({
|
|
51
|
+
export default async function RootLayout({
|
|
41
52
|
children,
|
|
42
53
|
}: Readonly<{
|
|
43
54
|
children: React.ReactNode;
|
|
44
55
|
}>) {
|
|
56
|
+
const isAdmin = await isUserAdmin();
|
|
57
|
+
const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
|
|
58
|
+
const serverLogoUrl = (await getMetadata('SERVER_LOGO_URL')) || null;
|
|
59
|
+
const serverFaviconUrl = (await getMetadata('SERVER_FAVICON_URL')) || faviconLogoImage.src;
|
|
60
|
+
|
|
45
61
|
return (
|
|
46
62
|
<html lang="en">
|
|
47
63
|
<head>
|
|
@@ -64,9 +80,13 @@ export default function RootLayout({
|
|
|
64
80
|
/>
|
|
65
81
|
*/}
|
|
66
82
|
{/* Default favicon as a fallback */}
|
|
67
|
-
<link rel="icon" href={
|
|
83
|
+
<link rel="icon" href={serverFaviconUrl} type="image/x-icon" />
|
|
68
84
|
</head>
|
|
69
|
-
<body className={`${barlowCondensed.
|
|
85
|
+
<body className={`${barlowCondensed.variable} antialiased bg-white text-gray-900`}>
|
|
86
|
+
<LayoutWrapper isAdmin={isAdmin} serverName={serverName} serverLogoUrl={serverLogoUrl}>
|
|
87
|
+
{children}
|
|
88
|
+
</LayoutWrapper>
|
|
89
|
+
</body>
|
|
70
90
|
</html>
|
|
71
91
|
);
|
|
72
92
|
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { metadataDefaults } from '../../database/metadataDefaults';
|
|
5
|
+
|
|
6
|
+
type MetadataEntry = {
|
|
7
|
+
id: number;
|
|
8
|
+
key: string;
|
|
9
|
+
value: string;
|
|
10
|
+
note: string | null;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
updatedAt: string;
|
|
13
|
+
isDefault?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function MetadataClient() {
|
|
17
|
+
const [metadata, setMetadata] = useState<MetadataEntry[]>([]);
|
|
18
|
+
const [loading, setLoading] = useState(true);
|
|
19
|
+
const [error, setError] = useState<string | null>(null);
|
|
20
|
+
const [editingId, setEditingId] = useState<number | null>(null);
|
|
21
|
+
const [formState, setFormState] = useState<{
|
|
22
|
+
key: string;
|
|
23
|
+
value: string;
|
|
24
|
+
note: string;
|
|
25
|
+
}>({ key: '', value: '', note: '' });
|
|
26
|
+
|
|
27
|
+
const fetchMetadata = async () => {
|
|
28
|
+
try {
|
|
29
|
+
setLoading(true);
|
|
30
|
+
const response = await fetch('/api/metadata');
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new Error('Failed to fetch metadata');
|
|
33
|
+
}
|
|
34
|
+
const data: MetadataEntry[] = await response.json();
|
|
35
|
+
|
|
36
|
+
// Merge defaults
|
|
37
|
+
const mergedData = [...data];
|
|
38
|
+
for (const def of metadataDefaults) {
|
|
39
|
+
if (!mergedData.find((m) => m.key === def.key)) {
|
|
40
|
+
mergedData.push({
|
|
41
|
+
id: -1,
|
|
42
|
+
key: def.key,
|
|
43
|
+
value: def.value,
|
|
44
|
+
note: def.note,
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
updatedAt: new Date().toISOString(),
|
|
47
|
+
isDefault: true,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Sort by key
|
|
52
|
+
mergedData.sort((a, b) => a.key.localeCompare(b.key));
|
|
53
|
+
|
|
54
|
+
setMetadata(mergedData);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
57
|
+
} finally {
|
|
58
|
+
setLoading(false);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
fetchMetadata();
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
setError(null);
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// If editingId is -1 (default value) or null (new value), use POST to create
|
|
72
|
+
// If editingId is > 0 (existing value), use PUT to update
|
|
73
|
+
const method = editingId && editingId !== -1 ? 'PUT' : 'POST';
|
|
74
|
+
const response = await fetch('/api/metadata', {
|
|
75
|
+
method,
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify(formState),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
const data = await response.json();
|
|
82
|
+
throw new Error(data.error || 'Failed to save metadata');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setFormState({ key: '', value: '', note: '' });
|
|
86
|
+
setEditingId(null);
|
|
87
|
+
fetchMetadata();
|
|
88
|
+
} catch (err) {
|
|
89
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const handleEdit = (entry: MetadataEntry) => {
|
|
94
|
+
setEditingId(entry.id);
|
|
95
|
+
setFormState({
|
|
96
|
+
key: entry.key,
|
|
97
|
+
value: entry.value,
|
|
98
|
+
note: entry.note || '',
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleDelete = async (key: string) => {
|
|
103
|
+
if (!confirm('Are you sure you want to delete this metadata?')) return;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const response = await fetch(`/api/metadata?key=${encodeURIComponent(key)}`, {
|
|
107
|
+
method: 'DELETE',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (!response.ok) {
|
|
111
|
+
const data = await response.json();
|
|
112
|
+
throw new Error(data.error || 'Failed to delete metadata');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fetchMetadata();
|
|
116
|
+
} catch (err) {
|
|
117
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const handleCancel = () => {
|
|
122
|
+
setEditingId(null);
|
|
123
|
+
setFormState({ key: '', value: '', note: '' });
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (loading && metadata.length === 0) {
|
|
127
|
+
return <div className="p-8 text-center">Loading metadata...</div>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div className="container mx-auto p-8 max-w-4xl">
|
|
132
|
+
<h1 className="text-3xl font-bold mb-8">Metadata Management</h1>
|
|
133
|
+
|
|
134
|
+
{error && (
|
|
135
|
+
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-6">
|
|
136
|
+
{error}
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
139
|
+
|
|
140
|
+
<div className="bg-white shadow rounded-lg p-6 mb-8">
|
|
141
|
+
<h2 className="text-xl font-semibold mb-4">
|
|
142
|
+
{editingId ? 'Edit Metadata' : 'Add New Metadata'}
|
|
143
|
+
</h2>
|
|
144
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
145
|
+
<div>
|
|
146
|
+
<label htmlFor="key" className="block text-sm font-medium text-gray-700 mb-1">
|
|
147
|
+
Key
|
|
148
|
+
</label>
|
|
149
|
+
<input
|
|
150
|
+
type="text"
|
|
151
|
+
id="key"
|
|
152
|
+
value={formState.key}
|
|
153
|
+
onChange={(e) => setFormState({ ...formState, key: e.target.value })}
|
|
154
|
+
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
155
|
+
required
|
|
156
|
+
disabled={!!editingId} // Key cannot be changed during edit
|
|
157
|
+
placeholder="e.g., SERVER_NAME"
|
|
158
|
+
/>
|
|
159
|
+
{editingId && <p className="text-xs text-gray-500 mt-1">Key cannot be changed.</p>}
|
|
160
|
+
</div>
|
|
161
|
+
<div>
|
|
162
|
+
<label htmlFor="value" className="block text-sm font-medium text-gray-700 mb-1">
|
|
163
|
+
Value
|
|
164
|
+
</label>
|
|
165
|
+
<textarea
|
|
166
|
+
id="value"
|
|
167
|
+
value={formState.value}
|
|
168
|
+
onChange={(e) => setFormState({ ...formState, value: e.target.value })}
|
|
169
|
+
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 min-h-[100px]"
|
|
170
|
+
required
|
|
171
|
+
placeholder="Metadata value..."
|
|
172
|
+
/>
|
|
173
|
+
</div>
|
|
174
|
+
<div>
|
|
175
|
+
<label htmlFor="note" className="block text-sm font-medium text-gray-700 mb-1">
|
|
176
|
+
Note (Optional)
|
|
177
|
+
</label>
|
|
178
|
+
<input
|
|
179
|
+
type="text"
|
|
180
|
+
id="note"
|
|
181
|
+
value={formState.note}
|
|
182
|
+
onChange={(e) => setFormState({ ...formState, note: e.target.value })}
|
|
183
|
+
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
184
|
+
placeholder="Description of this metadata"
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
<div className="flex space-x-3">
|
|
188
|
+
<button
|
|
189
|
+
type="submit"
|
|
190
|
+
className="bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition-colors"
|
|
191
|
+
>
|
|
192
|
+
{editingId ? 'Update Metadata' : 'Add Metadata'}
|
|
193
|
+
</button>
|
|
194
|
+
{editingId && (
|
|
195
|
+
<button
|
|
196
|
+
type="button"
|
|
197
|
+
onClick={handleCancel}
|
|
198
|
+
className="bg-gray-200 text-gray-800 py-2 px-4 rounded-md hover:bg-gray-300 transition-colors"
|
|
199
|
+
>
|
|
200
|
+
Cancel
|
|
201
|
+
</button>
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
</form>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div className="bg-white shadow rounded-lg overflow-hidden">
|
|
208
|
+
<table className="min-w-full divide-y divide-gray-200">
|
|
209
|
+
<thead className="bg-gray-50">
|
|
210
|
+
<tr>
|
|
211
|
+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
212
|
+
Key
|
|
213
|
+
</th>
|
|
214
|
+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
215
|
+
Value
|
|
216
|
+
</th>
|
|
217
|
+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
218
|
+
Note
|
|
219
|
+
</th>
|
|
220
|
+
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
221
|
+
Actions
|
|
222
|
+
</th>
|
|
223
|
+
</tr>
|
|
224
|
+
</thead>
|
|
225
|
+
<tbody className="bg-white divide-y divide-gray-200">
|
|
226
|
+
{metadata.length === 0 ? (
|
|
227
|
+
<tr>
|
|
228
|
+
<td colSpan={4} className="px-6 py-4 text-center text-gray-500">
|
|
229
|
+
No metadata found.
|
|
230
|
+
</td>
|
|
231
|
+
</tr>
|
|
232
|
+
) : (
|
|
233
|
+
metadata.map((entry) => (
|
|
234
|
+
<tr key={entry.id}>
|
|
235
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
236
|
+
{entry.key}
|
|
237
|
+
</td>
|
|
238
|
+
<td className="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">
|
|
239
|
+
{entry.value}
|
|
240
|
+
</td>
|
|
241
|
+
<td className="px-6 py-4 text-sm text-gray-500">
|
|
242
|
+
{entry.note || '-'}
|
|
243
|
+
</td>
|
|
244
|
+
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
|
245
|
+
<button
|
|
246
|
+
onClick={() => handleEdit(entry)}
|
|
247
|
+
className="text-blue-600 hover:text-blue-900 mr-4"
|
|
248
|
+
>
|
|
249
|
+
Edit
|
|
250
|
+
</button>
|
|
251
|
+
{!entry.isDefault && (
|
|
252
|
+
<button
|
|
253
|
+
onClick={() => handleDelete(entry.key)}
|
|
254
|
+
className="text-red-600 hover:text-red-900"
|
|
255
|
+
>
|
|
256
|
+
Delete
|
|
257
|
+
</button>
|
|
258
|
+
)}
|
|
259
|
+
{entry.isDefault && (
|
|
260
|
+
<span className="text-gray-400 text-xs italic ml-2">Default</span>
|
|
261
|
+
)}
|
|
262
|
+
</td>
|
|
263
|
+
</tr>
|
|
264
|
+
))
|
|
265
|
+
)}
|
|
266
|
+
</tbody>
|
|
267
|
+
</table>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ForbiddenPage } from '../../components/ForbiddenPage/ForbiddenPage';
|
|
2
|
+
import { isUserAdmin } from '../../utils/isUserAdmin';
|
|
3
|
+
import { MetadataClient } from './MetadataClient';
|
|
4
|
+
|
|
5
|
+
export default async function MetadataPage() {
|
|
6
|
+
const isAdmin = await isUserAdmin();
|
|
7
|
+
|
|
8
|
+
if (!isAdmin) {
|
|
9
|
+
return <ForbiddenPage />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return <MetadataClient />;
|
|
13
|
+
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
'use server';
|
|
2
2
|
|
|
3
|
-
import logoImage from '@/public/logo-blue-white-256.png';
|
|
4
3
|
import { getSingleLlmExecutionTools } from '@promptbook-local/core';
|
|
5
4
|
import moment from 'moment';
|
|
6
5
|
import { headers } from 'next/headers';
|
|
7
|
-
import Image from 'next/image';
|
|
8
6
|
import Link from 'next/link';
|
|
9
7
|
import { AvatarProfile } from '../../../../src/book-components/AvatarProfile/AvatarProfile/AvatarProfile';
|
|
10
8
|
import { AboutPromptbookInformation } from '../../../../src/utils/misc/xAboutPromptbookInformation';
|
|
11
9
|
import { $sideEffect } from '../../../../src/utils/organization/$sideEffect';
|
|
10
|
+
import { AuthControls } from '../components/Auth/AuthControls';
|
|
11
|
+
import { UsersList } from '../components/UsersList/UsersList';
|
|
12
|
+
import VercelDeploymentCard from '../components/VercelDeploymentCard/VercelDeploymentCard';
|
|
12
13
|
import { getLongRunningTask } from '../deamons/longRunningTask';
|
|
13
14
|
import { $provideAgentCollectionForServer } from '../tools/$provideAgentCollectionForServer';
|
|
14
15
|
import { $provideExecutionToolsForServer } from '../tools/$provideExecutionToolsForServer';
|
|
16
|
+
import { $provideServer } from '../tools/$provideServer';
|
|
17
|
+
import { getCurrentUser } from '../utils/getCurrentUser';
|
|
18
|
+
import { isUserAdmin } from '../utils/isUserAdmin';
|
|
15
19
|
import { AddAgentButton } from './AddAgentButton';
|
|
16
20
|
|
|
17
21
|
// Add calendar formats that include seconds
|
|
@@ -27,6 +31,9 @@ const calendarWithSeconds = {
|
|
|
27
31
|
export default async function HomePage() {
|
|
28
32
|
$sideEffect(/* Note: [🐶] This will ensure dynamic rendering of page and avoid Next.js pre-render */ headers());
|
|
29
33
|
|
|
34
|
+
const isAdmin = await isUserAdmin(); /* <- TODO: [👹] Here should be user permissions */
|
|
35
|
+
const currentUser = await getCurrentUser();
|
|
36
|
+
|
|
30
37
|
const collection = await $provideAgentCollectionForServer();
|
|
31
38
|
const agents = await collection.listAgents();
|
|
32
39
|
|
|
@@ -35,19 +42,20 @@ export default async function HomePage() {
|
|
|
35
42
|
const executionTools = await $provideExecutionToolsForServer();
|
|
36
43
|
const models = await getSingleLlmExecutionTools(executionTools.llm).listModels();
|
|
37
44
|
|
|
45
|
+
const host = (await headers()).get('host');
|
|
46
|
+
|
|
38
47
|
return (
|
|
39
48
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
|
|
40
49
|
<div className="container mx-auto px-4 py-16">
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
</h1>
|
|
50
|
+
<div className="flex justify-end mb-4">
|
|
51
|
+
<AuthControls initialUser={currentUser} />
|
|
52
|
+
</div>
|
|
45
53
|
|
|
46
54
|
<>
|
|
47
|
-
<h2 className="text-3xl text-gray-900 mt-
|
|
55
|
+
<h2 className="text-3xl text-gray-900 mt-4 mb-4">Agents ({agents.length})</h2>
|
|
48
56
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
49
57
|
{agents.map((agent) => (
|
|
50
|
-
<Link key={agent.agentName} href={
|
|
58
|
+
<Link key={agent.agentName} href={`/${agent.agentName}`}>
|
|
51
59
|
<AvatarProfile
|
|
52
60
|
{...{ agent }}
|
|
53
61
|
style={
|
|
@@ -61,50 +69,80 @@ export default async function HomePage() {
|
|
|
61
69
|
/>
|
|
62
70
|
</Link>
|
|
63
71
|
))}
|
|
64
|
-
<AddAgentButton />
|
|
72
|
+
{isAdmin && <AddAgentButton />}
|
|
65
73
|
</div>
|
|
66
74
|
</>
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
|
|
76
|
+
{isAdmin && <UsersList />}
|
|
77
|
+
|
|
78
|
+
{isAdmin && (
|
|
79
|
+
<>
|
|
80
|
+
<h2 className="text-3xl text-gray-900 mt-16 mb-4">Models ({models.length})</h2>
|
|
81
|
+
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
82
|
+
{models.map(({ modelName, modelTitle, modelDescription }) => (
|
|
83
|
+
<Link key={modelName} href={`#[🐱🚀]`}>
|
|
84
|
+
<div className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400">
|
|
85
|
+
<h2 className="text-2xl font-semibold text-gray-900 mb-2">{modelTitle}</h2>
|
|
86
|
+
<code>{modelName}</code>
|
|
87
|
+
<p className="text-gray-600">{modelDescription}</p>
|
|
88
|
+
</div>
|
|
89
|
+
</Link>
|
|
90
|
+
))}
|
|
91
|
+
</div>
|
|
92
|
+
</>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{isAdmin && (
|
|
96
|
+
<>
|
|
97
|
+
{/* Note: Shown in <AboutPromptbookInformation />: <h2 className="text-3xl text-gray-900 mt-16 mb-4">About Promptbook</h2> */}
|
|
98
|
+
<AboutPromptbookInformation />
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{isAdmin && (
|
|
103
|
+
<>
|
|
104
|
+
<h2 className="text-3xl text-gray-900 mt-16 mb-4">Technical Information</h2>
|
|
105
|
+
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
106
|
+
<Link
|
|
107
|
+
href={'#'}
|
|
108
|
+
className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
|
|
109
|
+
>
|
|
110
|
+
<h2 className="text-2xl font-semibold text-gray-900 mb-2">
|
|
111
|
+
Long running task {longRunningTask.taskId}
|
|
112
|
+
</h2>
|
|
113
|
+
<p className="text-gray-600">Tick: {longRunningTask.tick}</p>
|
|
114
|
+
<p className="text-gray-600">
|
|
115
|
+
Created At:{' '}
|
|
116
|
+
{moment(longRunningTask.createdAt).calendar(undefined, calendarWithSeconds)}
|
|
117
|
+
</p>
|
|
118
|
+
<p className="text-gray-600">
|
|
119
|
+
Updated At:{' '}
|
|
120
|
+
{moment(longRunningTask.updatedAt).calendar(undefined, calendarWithSeconds)}
|
|
121
|
+
</p>
|
|
78
122
|
</Link>
|
|
79
|
-
))}
|
|
80
|
-
</div>
|
|
81
|
-
</>
|
|
82
123
|
|
|
83
|
-
|
|
84
|
-
<h2 className="text-3xl text-gray-900 mt-16 mb-4">About</h2>
|
|
85
|
-
<AboutPromptbookInformation />
|
|
86
|
-
</>
|
|
124
|
+
<VercelDeploymentCard />
|
|
87
125
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
</
|
|
105
|
-
</
|
|
106
|
-
|
|
107
|
-
|
|
126
|
+
<Link
|
|
127
|
+
href={'#'}
|
|
128
|
+
className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
|
|
129
|
+
>
|
|
130
|
+
<h2 className="text-2xl font-semibold text-gray-900 mb-2">HTTP Information</h2>
|
|
131
|
+
|
|
132
|
+
<p className="text-gray-600">Host: {host}</p>
|
|
133
|
+
</Link>
|
|
134
|
+
|
|
135
|
+
<Link
|
|
136
|
+
href={'#'}
|
|
137
|
+
className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
|
|
138
|
+
>
|
|
139
|
+
<h2 className="text-2xl font-semibold text-gray-900 mb-2">Server</h2>
|
|
140
|
+
|
|
141
|
+
<pre>{JSON.stringify(await $provideServer(), null, 2)}</pre>
|
|
142
|
+
</Link>
|
|
143
|
+
</div>
|
|
144
|
+
</>
|
|
145
|
+
)}
|
|
108
146
|
</div>
|
|
109
147
|
</div>
|
|
110
148
|
);
|