@promptbook/cli 0.103.0-49 → 0.103.0-51
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/package.json +1 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +7 -6
- package/apps/agents-server/src/app/[agentName]/[...rest]/page.tsx +6 -1
- package/apps/agents-server/src/app/{metadata → admin/metadata}/MetadataClient.tsx +5 -13
- package/apps/agents-server/src/app/{metadata → admin/metadata}/page.tsx +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +21 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +23 -3
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +10 -2
- package/apps/agents-server/src/app/api/agents/route.ts +34 -0
- package/apps/agents-server/src/app/api/embed.js/route.ts +93 -0
- package/apps/agents-server/src/app/embed/page.tsx +24 -0
- package/apps/agents-server/src/app/page.tsx +59 -79
- package/apps/agents-server/src/components/Header/Header.tsx +28 -8
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +28 -0
- package/apps/agents-server/src/components/Homepage/Card.tsx +18 -0
- package/apps/agents-server/src/components/Homepage/ModelCard.tsx +29 -0
- package/apps/agents-server/src/components/Homepage/Section.tsx +17 -0
- package/apps/agents-server/src/components/Homepage/TechInfoCard.tsx +20 -0
- package/apps/agents-server/src/components/UsersList/UsersList.tsx +6 -6
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +3 -8
- package/apps/agents-server/src/database/metadataDefaults.ts +5 -0
- package/apps/agents-server/src/database/migrate.ts +131 -0
- package/apps/agents-server/src/database/{schema.sql → migrations/2025-11-0001-initial-schema.sql} +1 -17
- package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +16 -0
- package/apps/agents-server/src/middleware.ts +47 -9
- package/apps/agents-server/src/utils/getFederatedAgents.ts +66 -0
- package/esm/index.es.js +1 -1
- package/esm/typings/src/_packages/components.index.d.ts +2 -0
- package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgent.d.ts +23 -0
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +8 -0
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +7 -0
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +5 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +1 -1
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { useRouter } from 'next/navigation';
|
|
4
|
+
import { Card } from '../components/Homepage/Card';
|
|
3
5
|
import { $createAgentAction } from './actions';
|
|
4
6
|
|
|
5
7
|
export function AddAgentButton() {
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
|
|
6
10
|
const handleAddAgent = async () => {
|
|
7
11
|
await $createAgentAction();
|
|
8
12
|
// TODO: Add proper error handling and UI feedback
|
|
9
|
-
|
|
13
|
+
router.refresh();
|
|
10
14
|
};
|
|
11
15
|
|
|
12
16
|
return (
|
|
13
|
-
<div
|
|
14
|
-
|
|
15
|
-
onClick={handleAddAgent}
|
|
16
|
-
>
|
|
17
|
-
+ Add New Agent
|
|
17
|
+
<div onClick={handleAddAgent} className="cursor-pointer">
|
|
18
|
+
<Card>+ Add New Agent</Card>
|
|
18
19
|
</div>
|
|
19
20
|
);
|
|
20
21
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { redirect } from 'next/navigation';
|
|
1
|
+
import { notFound, redirect } from 'next/navigation';
|
|
2
2
|
|
|
3
3
|
export default async function RedirectPage({ params }: { params: Promise<{ agentName: string; rest: string[] }> }) {
|
|
4
4
|
const { agentName, rest } = await params;
|
|
5
|
+
|
|
6
|
+
if (agentName === 'agents' || agentName === 'api' || agentName.startsWith('.')) {
|
|
7
|
+
notFound();
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
redirect(`/agents/${agentName}/${rest.join('/')}`);
|
|
6
11
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
-
import { metadataDefaults } from '
|
|
4
|
+
import { metadataDefaults } from '../../../database/metadataDefaults';
|
|
5
5
|
|
|
6
6
|
type MetadataEntry = {
|
|
7
7
|
id: number;
|
|
@@ -132,15 +132,11 @@ export function MetadataClient() {
|
|
|
132
132
|
<h1 className="text-3xl font-bold mb-8">Metadata Management</h1>
|
|
133
133
|
|
|
134
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>
|
|
135
|
+
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-6">{error}</div>
|
|
138
136
|
)}
|
|
139
137
|
|
|
140
138
|
<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>
|
|
139
|
+
<h2 className="text-xl font-semibold mb-4">{editingId ? 'Edit Metadata' : 'Add New Metadata'}</h2>
|
|
144
140
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
145
141
|
<div>
|
|
146
142
|
<label htmlFor="key" className="block text-sm font-medium text-gray-700 mb-1">
|
|
@@ -235,12 +231,8 @@ export function MetadataClient() {
|
|
|
235
231
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
236
232
|
{entry.key}
|
|
237
233
|
</td>
|
|
238
|
-
<td className="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">
|
|
239
|
-
|
|
240
|
-
</td>
|
|
241
|
-
<td className="px-6 py-4 text-sm text-gray-500">
|
|
242
|
-
{entry.note || '-'}
|
|
243
|
-
</td>
|
|
234
|
+
<td className="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">{entry.value}</td>
|
|
235
|
+
<td className="px-6 py-4 text-sm text-gray-500">{entry.note || '-'}</td>
|
|
244
236
|
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
|
245
237
|
<button
|
|
246
238
|
onClick={() => handleEdit(entry)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ForbiddenPage } from '
|
|
2
|
-
import { isUserAdmin } from '
|
|
1
|
+
import { ForbiddenPage } from '../../../components/ForbiddenPage/ForbiddenPage';
|
|
2
|
+
import { isUserAdmin } from '../../../utils/isUserAdmin';
|
|
3
3
|
import { MetadataClient } from './MetadataClient';
|
|
4
4
|
|
|
5
5
|
export default async function MetadataPage() {
|
|
@@ -11,6 +11,17 @@ import { assertsError } from '../../../../../../../../src/errors/assertsError';
|
|
|
11
11
|
*/
|
|
12
12
|
export const maxDuration = 300;
|
|
13
13
|
|
|
14
|
+
export async function OPTIONS(request: Request) {
|
|
15
|
+
return new Response(null, {
|
|
16
|
+
status: 200,
|
|
17
|
+
headers: {
|
|
18
|
+
'Access-Control-Allow-Origin': '*',
|
|
19
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
20
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
14
25
|
export async function POST(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
15
26
|
let { agentName } = await params;
|
|
16
27
|
agentName = decodeURIComponent(agentName);
|
|
@@ -110,6 +121,12 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
|
|
|
110
121
|
platform,
|
|
111
122
|
});
|
|
112
123
|
|
|
124
|
+
// Note: [🐱🚀] Save the learned data
|
|
125
|
+
const newAgentSource = agent.agentSource.value;
|
|
126
|
+
if (newAgentSource !== agentSource) {
|
|
127
|
+
await collection.updateAgentSource(agentName, newAgentSource);
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
controller.close();
|
|
114
131
|
})
|
|
115
132
|
.catch((error) => {
|
|
@@ -120,7 +137,10 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
|
|
|
120
137
|
|
|
121
138
|
return new Response(readableStream, {
|
|
122
139
|
status: 200,
|
|
123
|
-
headers: {
|
|
140
|
+
headers: {
|
|
141
|
+
'Content-Type': 'text/markdown',
|
|
142
|
+
'Access-Control-Allow-Origin': '*', // <- Note: Allow embedding on other websites
|
|
143
|
+
},
|
|
124
144
|
});
|
|
125
145
|
} catch (error) {
|
|
126
146
|
assertsError(error);
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
2
|
-
import { parseAgentSource } from '@promptbook-local/core';
|
|
2
|
+
import { computeAgentHash, parseAgentSource } from '@promptbook-local/core';
|
|
3
3
|
import { serializeError } from '@promptbook-local/utils';
|
|
4
4
|
import { assertsError } from '../../../../../../../../src/errors/assertsError';
|
|
5
5
|
import { keepUnused } from '../../../../../../../../src/utils/organization/keepUnused';
|
|
6
6
|
|
|
7
|
+
export async function OPTIONS(request: Request) {
|
|
8
|
+
keepUnused(request);
|
|
9
|
+
return new Response(null, {
|
|
10
|
+
status: 200,
|
|
11
|
+
headers: {
|
|
12
|
+
'Access-Control-Allow-Origin': '*',
|
|
13
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
14
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
8
20
|
keepUnused(request /* <- Note: We dont need `request` parameter */);
|
|
9
21
|
let { agentName } = await params;
|
|
@@ -13,10 +25,15 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
|
|
|
13
25
|
const collection = await $provideAgentCollectionForServer();
|
|
14
26
|
const agentSource = await collection.getAgentSource(agentName);
|
|
15
27
|
const agentProfile = parseAgentSource(agentSource);
|
|
28
|
+
const agentHash = computeAgentHash(agentSource);
|
|
16
29
|
|
|
17
30
|
return new Response(
|
|
18
31
|
JSON.stringify(
|
|
19
|
-
|
|
32
|
+
{
|
|
33
|
+
...agentProfile,
|
|
34
|
+
agentHash,
|
|
35
|
+
parameters: [], // <- TODO: [😰] Implement parameters
|
|
36
|
+
},
|
|
20
37
|
// <- TODO: [🐱🚀] Rename `serializeError` to `errorToJson`
|
|
21
38
|
null,
|
|
22
39
|
4,
|
|
@@ -24,7 +41,10 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
|
|
|
24
41
|
),
|
|
25
42
|
{
|
|
26
43
|
status: 200,
|
|
27
|
-
headers: {
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
'Access-Control-Allow-Origin': '*', // <- Note: Allow embedding on other websites
|
|
47
|
+
},
|
|
28
48
|
},
|
|
29
49
|
);
|
|
30
50
|
} catch (error) {
|
|
@@ -4,7 +4,7 @@ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentColle
|
|
|
4
4
|
// import { BookEditor } from '@promptbook-local/components';
|
|
5
5
|
import { $provideServer } from '@/src/tools/$provideServer';
|
|
6
6
|
import { parseAgentSource } from '@promptbook-local/core';
|
|
7
|
-
import { Columns2Icon,
|
|
7
|
+
import { Columns2Icon, MessagesSquareIcon, NotebookPenIcon } from 'lucide-react';
|
|
8
8
|
import { headers } from 'next/headers';
|
|
9
9
|
import { notFound } from 'next/navigation';
|
|
10
10
|
import { Color } from '../../../../../../src/utils/color/Color';
|
|
@@ -114,6 +114,14 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
114
114
|
|
|
115
115
|
<div className="flex flex-col gap-2 mt-auto">
|
|
116
116
|
<div className="flex gap-2">
|
|
117
|
+
<a
|
|
118
|
+
href={`/agents/${encodeURIComponent(agentName)}/chat`}
|
|
119
|
+
// <- TODO: [🧠] Can I append path like this on current browser URL in href?
|
|
120
|
+
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"
|
|
121
|
+
>
|
|
122
|
+
<MessagesSquareIcon className="ml-2 w-4 h-4 mr-2" />
|
|
123
|
+
Chat
|
|
124
|
+
</a>
|
|
117
125
|
<a
|
|
118
126
|
href={`/agents/${encodeURIComponent(agentName)}/book+chat`}
|
|
119
127
|
// <- TODO: [🧠] Can I append path like this on current browser URL in href?
|
|
@@ -127,7 +135,7 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
127
135
|
// <- TODO: [🧠] Can I append path like this on current browser URL in href?
|
|
128
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"
|
|
129
137
|
>
|
|
130
|
-
<
|
|
138
|
+
<NotebookPenIcon className="ml-2 w-4 h-4 mr-2" />
|
|
131
139
|
Edit
|
|
132
140
|
</a>
|
|
133
141
|
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getMetadata } from '../../../database/getMetadata';
|
|
3
|
+
import { $provideAgentCollectionForServer } from '../../../tools/$provideAgentCollectionForServer';
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic';
|
|
6
|
+
|
|
7
|
+
export async function GET() {
|
|
8
|
+
try {
|
|
9
|
+
const collection = await $provideAgentCollectionForServer();
|
|
10
|
+
const agents = await collection.listAgents();
|
|
11
|
+
const serverUrl = (await getMetadata('SERVER_URL')) || '';
|
|
12
|
+
const federatedServersString = (await getMetadata('FEDERATED_SERVERS')) || '';
|
|
13
|
+
const federatedServers = federatedServersString
|
|
14
|
+
.split(',')
|
|
15
|
+
.map((s) => s.trim())
|
|
16
|
+
.filter((s) => s !== '');
|
|
17
|
+
|
|
18
|
+
const agentsWithUrl = agents.map((agent) => ({
|
|
19
|
+
...agent,
|
|
20
|
+
url: `${serverUrl}/${agent.agentName}`,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
return NextResponse.json({
|
|
24
|
+
agents: agentsWithUrl,
|
|
25
|
+
federatedServers,
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Error fetching agents:', error);
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{ error: 'Failed to fetch agents' },
|
|
31
|
+
{ status: 500 },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
|
|
3
|
+
export async function GET(request: NextRequest) {
|
|
4
|
+
const protocol = request.nextUrl.protocol;
|
|
5
|
+
const host = request.nextUrl.host;
|
|
6
|
+
const baseUrl = `${protocol}//${host}`;
|
|
7
|
+
|
|
8
|
+
const script = `
|
|
9
|
+
(function() {
|
|
10
|
+
if (customElements.get('promptbook-agent')) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class PromptbookAgentElement extends HTMLElement {
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
this.iframe = null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static get observedAttributes() {
|
|
21
|
+
return ['agent-url'];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
connectedCallback() {
|
|
25
|
+
this.render();
|
|
26
|
+
window.addEventListener('message', this.handleMessage.bind(this));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
disconnectedCallback() {
|
|
30
|
+
window.removeEventListener('message', this.handleMessage.bind(this));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
34
|
+
if (name === 'agent-url' && oldValue !== newValue) {
|
|
35
|
+
this.render();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
handleMessage(event) {
|
|
40
|
+
if (event.data && event.data.type === 'PROMPTBOOK_AGENT_RESIZE') {
|
|
41
|
+
if (event.data.isOpen) {
|
|
42
|
+
this.iframe.style.width = '450px';
|
|
43
|
+
this.iframe.style.height = '650px';
|
|
44
|
+
this.iframe.style.maxHeight = '90vh';
|
|
45
|
+
this.iframe.style.maxWidth = '90vw';
|
|
46
|
+
this.iframe.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
|
|
47
|
+
this.iframe.style.borderRadius = '12px';
|
|
48
|
+
} else {
|
|
49
|
+
this.iframe.style.width = '60px';
|
|
50
|
+
this.iframe.style.height = '60px';
|
|
51
|
+
this.iframe.style.boxShadow = 'none';
|
|
52
|
+
this.iframe.style.borderRadius = '0';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
render() {
|
|
58
|
+
const agentUrl = this.getAttribute('agent-url');
|
|
59
|
+
if (!agentUrl) return;
|
|
60
|
+
|
|
61
|
+
if (!this.iframe) {
|
|
62
|
+
this.attachShadow({ mode: 'open' });
|
|
63
|
+
this.iframe = document.createElement('iframe');
|
|
64
|
+
this.iframe.style.border = 'none';
|
|
65
|
+
this.iframe.style.position = 'fixed';
|
|
66
|
+
this.iframe.style.bottom = '20px';
|
|
67
|
+
this.iframe.style.right = '20px';
|
|
68
|
+
this.iframe.style.width = '60px';
|
|
69
|
+
this.iframe.style.height = '60px';
|
|
70
|
+
this.iframe.style.zIndex = '2147483647'; // Max z-index
|
|
71
|
+
this.iframe.style.transition = 'width 0.3s ease, height 0.3s ease';
|
|
72
|
+
this.iframe.style.backgroundColor = 'transparent';
|
|
73
|
+
this.iframe.setAttribute('allow', 'microphone'); // Allow microphone if needed for voice
|
|
74
|
+
this.shadowRoot.appendChild(this.iframe);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Construct embed URL pointing to the Next.js page we created
|
|
78
|
+
const embedUrl = '${baseUrl}/embed?agentUrl=' + encodeURIComponent(agentUrl);
|
|
79
|
+
this.iframe.src = embedUrl;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
customElements.define('promptbook-agent', PromptbookAgentElement);
|
|
84
|
+
})();
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
return new NextResponse(script, {
|
|
88
|
+
headers: {
|
|
89
|
+
'Content-Type': 'application/javascript',
|
|
90
|
+
'Access-Control-Allow-Origin': '*',
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { PromptbookAgent } from '@promptbook-local/components';
|
|
4
|
+
import { useSearchParams } from 'next/navigation';
|
|
5
|
+
|
|
6
|
+
export default function EmbedPage() {
|
|
7
|
+
const searchParams = useSearchParams();
|
|
8
|
+
const agentUrl = searchParams.get('agentUrl');
|
|
9
|
+
|
|
10
|
+
if (!agentUrl) {
|
|
11
|
+
return <div className="text-red-500">Missing agentUrl parameter</div>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="w-full h-full bg-transparent">
|
|
16
|
+
<PromptbookAgent
|
|
17
|
+
agentUrl={agentUrl}
|
|
18
|
+
onOpenChange={(isOpen) => {
|
|
19
|
+
window.parent.postMessage({ type: 'PROMPTBOOK_AGENT_RESIZE', isOpen }, '*');
|
|
20
|
+
}}
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
import { getSingleLlmExecutionTools } from '@promptbook-local/core';
|
|
4
4
|
import moment from 'moment';
|
|
5
5
|
import { headers } from 'next/headers';
|
|
6
|
-
import Link from 'next/link';
|
|
7
|
-
import { AvatarProfile } from '../../../../src/book-components/AvatarProfile/AvatarProfile/AvatarProfile';
|
|
8
6
|
import { AboutPromptbookInformation } from '../../../../src/utils/misc/xAboutPromptbookInformation';
|
|
9
7
|
import { $sideEffect } from '../../../../src/utils/organization/$sideEffect';
|
|
10
8
|
import { AuthControls } from '../components/Auth/AuthControls';
|
|
9
|
+
import { AgentCard } from '../components/Homepage/AgentCard';
|
|
10
|
+
import { ModelCard } from '../components/Homepage/ModelCard';
|
|
11
|
+
import { Section } from '../components/Homepage/Section';
|
|
12
|
+
import { TechInfoCard } from '../components/Homepage/TechInfoCard';
|
|
11
13
|
import { UsersList } from '../components/UsersList/UsersList';
|
|
12
14
|
import VercelDeploymentCard from '../components/VercelDeploymentCard/VercelDeploymentCard';
|
|
15
|
+
import { getMetadata } from '../database/getMetadata';
|
|
13
16
|
import { getLongRunningTask } from '../deamons/longRunningTask';
|
|
14
17
|
import { $provideAgentCollectionForServer } from '../tools/$provideAgentCollectionForServer';
|
|
15
18
|
import { $provideExecutionToolsForServer } from '../tools/$provideExecutionToolsForServer';
|
|
16
19
|
import { $provideServer } from '../tools/$provideServer';
|
|
17
20
|
import { getCurrentUser } from '../utils/getCurrentUser';
|
|
21
|
+
import { getFederatedAgents } from '../utils/getFederatedAgents';
|
|
18
22
|
import { isUserAdmin } from '../utils/isUserAdmin';
|
|
19
23
|
import { AddAgentButton } from './AddAgentButton';
|
|
20
24
|
|
|
@@ -37,12 +41,20 @@ export default async function HomePage() {
|
|
|
37
41
|
const collection = await $provideAgentCollectionForServer();
|
|
38
42
|
const agents = await collection.listAgents();
|
|
39
43
|
|
|
44
|
+
const federatedServersString = (await getMetadata('FEDERATED_SERVERS')) || '';
|
|
45
|
+
const federatedServers = federatedServersString
|
|
46
|
+
.split(',')
|
|
47
|
+
.map((s) => s.trim())
|
|
48
|
+
.filter((s) => s !== '');
|
|
49
|
+
|
|
50
|
+
const externalAgents = await getFederatedAgents(federatedServers);
|
|
51
|
+
|
|
40
52
|
const longRunningTask = getLongRunningTask();
|
|
41
53
|
|
|
42
54
|
const executionTools = await $provideExecutionToolsForServer();
|
|
43
55
|
const models = await getSingleLlmExecutionTools(executionTools.llm).listModels();
|
|
44
56
|
|
|
45
|
-
const host = (await headers()).get('host');
|
|
57
|
+
const host = (await headers()).get('host') || 'unknown';
|
|
46
58
|
|
|
47
59
|
return (
|
|
48
60
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
|
|
@@ -51,45 +63,34 @@ export default async function HomePage() {
|
|
|
51
63
|
<AuthControls initialUser={currentUser} />
|
|
52
64
|
</div>
|
|
53
65
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
backgroundColor: `${agent.meta.color}22`, // <- TODO: Use Color object here
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
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"
|
|
69
|
-
/>
|
|
70
|
-
</Link>
|
|
66
|
+
<Section title={`Agents (${agents.length})`}>
|
|
67
|
+
{agents.map((agent) => (
|
|
68
|
+
<AgentCard key={agent.agentName} agent={agent} href={`/${agent.agentName}`} />
|
|
69
|
+
))}
|
|
70
|
+
{isAdmin && <AddAgentButton />}
|
|
71
|
+
</Section>
|
|
72
|
+
|
|
73
|
+
{externalAgents.length > 0 && (
|
|
74
|
+
<Section title={`External Agents (${externalAgents.length})`}>
|
|
75
|
+
{externalAgents.map((agent) => (
|
|
76
|
+
<AgentCard key={agent.url} agent={agent} href={agent.url} />
|
|
71
77
|
))}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
</>
|
|
78
|
+
</Section>
|
|
79
|
+
)}
|
|
75
80
|
|
|
76
81
|
{isAdmin && <UsersList />}
|
|
77
82
|
|
|
78
83
|
{isAdmin && (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</Link>
|
|
90
|
-
))}
|
|
91
|
-
</div>
|
|
92
|
-
</>
|
|
84
|
+
<Section title={`Models (${models.length})`}>
|
|
85
|
+
{models.map(({ modelName, modelTitle, modelDescription }) => (
|
|
86
|
+
<ModelCard
|
|
87
|
+
key={modelName}
|
|
88
|
+
modelName={modelName}
|
|
89
|
+
modelTitle={modelTitle || modelName}
|
|
90
|
+
modelDescription={modelDescription}
|
|
91
|
+
/>
|
|
92
|
+
))}
|
|
93
|
+
</Section>
|
|
93
94
|
)}
|
|
94
95
|
|
|
95
96
|
{isAdmin && (
|
|
@@ -100,48 +101,27 @@ export default async function HomePage() {
|
|
|
100
101
|
)}
|
|
101
102
|
|
|
102
103
|
{isAdmin && (
|
|
103
|
-
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<VercelDeploymentCard />
|
|
125
|
-
|
|
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
|
-
</>
|
|
104
|
+
<Section title="Technical Information">
|
|
105
|
+
<TechInfoCard title={`Long running task ${longRunningTask.taskId}`}>
|
|
106
|
+
<p className="text-gray-600">Tick: {longRunningTask.tick}</p>
|
|
107
|
+
<p className="text-gray-600">
|
|
108
|
+
Created At: {moment(longRunningTask.createdAt).calendar(undefined, calendarWithSeconds)}
|
|
109
|
+
</p>
|
|
110
|
+
<p className="text-gray-600">
|
|
111
|
+
Updated At: {moment(longRunningTask.updatedAt).calendar(undefined, calendarWithSeconds)}
|
|
112
|
+
</p>
|
|
113
|
+
</TechInfoCard>
|
|
114
|
+
|
|
115
|
+
<VercelDeploymentCard />
|
|
116
|
+
|
|
117
|
+
<TechInfoCard title="HTTP Information">
|
|
118
|
+
<p className="text-gray-600">Host: {host}</p>
|
|
119
|
+
</TechInfoCard>
|
|
120
|
+
|
|
121
|
+
<TechInfoCard title="Server">
|
|
122
|
+
<pre>{JSON.stringify(await $provideServer(), null, 2)}</pre>
|
|
123
|
+
</TechInfoCard>
|
|
124
|
+
</Section>
|
|
145
125
|
)}
|
|
146
126
|
</div>
|
|
147
127
|
</div>
|
|
@@ -59,21 +59,41 @@ export function Header(props: HeaderProps) {
|
|
|
59
59
|
{/* Desktop Navigation */}
|
|
60
60
|
<nav className="hidden md:flex items-center gap-8">
|
|
61
61
|
{isAdmin && (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
<>
|
|
63
|
+
<Link
|
|
64
|
+
href="/"
|
|
65
|
+
className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
66
|
+
>
|
|
67
|
+
Agents
|
|
68
|
+
</Link>
|
|
69
|
+
<Link
|
|
70
|
+
href="/"
|
|
71
|
+
className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
72
|
+
>
|
|
73
|
+
Models
|
|
74
|
+
</Link>
|
|
75
|
+
<Link
|
|
76
|
+
href="/admin/metadata"
|
|
77
|
+
className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
78
|
+
>
|
|
79
|
+
Metadata
|
|
80
|
+
</Link>
|
|
81
|
+
<Link
|
|
82
|
+
href="https://ptbk.io/"
|
|
83
|
+
className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
84
|
+
>
|
|
85
|
+
About
|
|
86
|
+
</Link>
|
|
87
|
+
</>
|
|
68
88
|
)}
|
|
69
89
|
|
|
70
90
|
{just(false /* TODO: [🧠] Figure out what to do with theese links */) && (
|
|
71
91
|
<Link
|
|
72
|
-
href="https://ptbk.io
|
|
92
|
+
href="https://ptbk.io/"
|
|
73
93
|
target="_blank"
|
|
74
94
|
className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
75
95
|
>
|
|
76
|
-
|
|
96
|
+
Create your server
|
|
77
97
|
</Link>
|
|
78
98
|
)}
|
|
79
99
|
</nav>
|