@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.
- package/apps/agents-server/README.md +1 -1
- package/apps/agents-server/config.ts +3 -3
- package/apps/agents-server/next.config.ts +1 -1
- package/apps/agents-server/public/sw.js +16 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +24 -4
- package/apps/agents-server/src/app/actions.ts +15 -13
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +541 -0
- package/apps/agents-server/src/app/admin/chat-feedback/page.tsx +22 -0
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +532 -0
- package/apps/agents-server/src/app/admin/chat-history/page.tsx +21 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +241 -27
- package/apps/agents-server/src/app/admin/models/page.tsx +22 -0
- package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +131 -0
- package/apps/agents-server/src/app/admin/users/[userId]/page.tsx +21 -0
- package/apps/agents-server/src/app/admin/users/page.tsx +18 -0
- package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatFeedbackButton.tsx +63 -0
- package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatHistoryButton.tsx +63 -0
- package/apps/agents-server/src/app/agents/[agentName]/CloneAgentButton.tsx +41 -0
- package/apps/agents-server/src/app/agents/[agentName]/InstallPwaButton.tsx +74 -0
- package/apps/agents-server/src/app/agents/[agentName]/ServiceWorkerRegister.tsx +24 -0
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +19 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +67 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +177 -0
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +53 -1
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +11 -11
- package/apps/agents-server/src/app/agents/[agentName]/history/RestoreVersionButton.tsx +46 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +62 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +80 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +92 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +92 -0
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +61 -0
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +102 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +41 -22
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +47 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +19 -0
- package/apps/agents-server/src/app/api/agents/route.ts +22 -13
- package/apps/agents-server/src/app/api/auth/login/route.ts +6 -44
- package/apps/agents-server/src/app/api/chat-feedback/[id]/route.ts +38 -0
- package/apps/agents-server/src/app/api/chat-feedback/route.ts +157 -0
- package/apps/agents-server/src/app/api/chat-history/[id]/route.ts +37 -0
- package/apps/agents-server/src/app/api/chat-history/route.ts +147 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +17 -0
- package/apps/agents-server/src/app/api/upload/route.ts +9 -1
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +62 -0
- package/apps/agents-server/src/app/docs/page.tsx +33 -0
- package/apps/agents-server/src/app/layout.tsx +29 -3
- package/apps/agents-server/src/app/manifest.ts +109 -0
- package/apps/agents-server/src/app/page.tsx +8 -45
- package/apps/agents-server/src/app/recycle-bin/RestoreAgentButton.tsx +40 -0
- package/apps/agents-server/src/app/recycle-bin/actions.ts +27 -0
- package/apps/agents-server/src/app/recycle-bin/page.tsx +58 -0
- package/apps/agents-server/src/app/restricted/page.tsx +33 -0
- package/apps/agents-server/src/app/test/og-image/README.md +1 -0
- package/apps/agents-server/src/app/test/og-image/opengraph-image.tsx +37 -0
- package/apps/agents-server/src/app/test/og-image/page.tsx +22 -0
- package/apps/agents-server/src/components/Footer/Footer.tsx +175 -0
- package/apps/agents-server/src/components/Header/Header.tsx +445 -79
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +46 -14
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +58 -0
- package/apps/agents-server/src/components/Homepage/Card.tsx +1 -1
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +21 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +183 -0
- package/apps/agents-server/src/components/Homepage/ModelsSection.tsx +75 -0
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +28 -3
- package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +18 -17
- package/apps/agents-server/src/components/Portal/Portal.tsx +38 -0
- package/apps/agents-server/src/components/UsersList/UsersList.tsx +82 -131
- package/apps/agents-server/src/components/UsersList/useUsersAdmin.ts +139 -0
- package/apps/agents-server/src/database/metadataDefaults.ts +38 -6
- package/apps/agents-server/src/middleware.ts +146 -93
- package/apps/agents-server/src/tools/$provideServer.ts +2 -2
- package/apps/agents-server/src/utils/authenticateUser.ts +42 -0
- package/apps/agents-server/src/utils/chatFeedbackAdmin.ts +96 -0
- package/apps/agents-server/src/utils/chatHistoryAdmin.ts +96 -0
- package/apps/agents-server/src/utils/getEffectiveFederatedServers.ts +22 -0
- package/apps/agents-server/src/utils/getFederatedAgents.ts +31 -8
- package/apps/agents-server/src/utils/getFederatedServersFromMetadata.ts +10 -0
- package/apps/agents-server/src/utils/getVisibleCommitmentDefinitions.ts +12 -0
- package/apps/agents-server/src/utils/isUserAdmin.ts +2 -2
- package/apps/agents-server/vercel.json +7 -0
- package/esm/index.es.js +153 -2
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -1
- package/esm/typings/src/_packages/components.index.d.ts +2 -0
- package/esm/typings/src/_packages/core.index.d.ts +6 -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/AgentModelRequirements.d.ts +7 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +4 -0
- package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +12 -0
- package/esm/typings/src/book-components/icons/MicIcon.d.ts +8 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +17 -0
- package/esm/typings/src/commitments/MESSAGE/AgentMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/MESSAGE/UserMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/index.d.ts +20 -1
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +9 -0
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +2 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +10 -1
- package/esm/typings/src/utils/normalization/normalizeMessageText.d.ts +9 -0
- package/esm/typings/src/utils/normalization/normalizeMessageText.test.d.ts +1 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +153 -2
- package/umd/index.umd.js.map +1 -1
|
@@ -1,112 +1,51 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { FormEvent, useState } from 'react';
|
|
4
4
|
import { Card } from '../Homepage/Card';
|
|
5
5
|
import { Section } from '../Homepage/Section';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
import { useUsersAdmin } from './useUsersAdmin';
|
|
7
|
+
|
|
8
|
+
type UsersListProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Whether the UI should allow creating new users.
|
|
11
|
+
*
|
|
12
|
+
* On the main `/` page this should be `false` so that users
|
|
13
|
+
* can only be created from the `/admin/users` page.
|
|
14
|
+
*/
|
|
15
|
+
allowCreate?: boolean;
|
|
13
16
|
};
|
|
14
17
|
|
|
15
|
-
export function UsersList() {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const [error, setError] = useState<string | null>(null);
|
|
18
|
+
export function UsersList({ allowCreate = true }: UsersListProps) {
|
|
19
|
+
const { users, loading, error, createUser, deleteUser, toggleAdmin } = useUsersAdmin();
|
|
20
|
+
|
|
19
21
|
const [newUsername, setNewUsername] = useState('');
|
|
20
22
|
const [newPassword, setNewPassword] = useState('');
|
|
21
23
|
const [newIsAdmin, setNewIsAdmin] = useState(false);
|
|
22
24
|
|
|
23
|
-
const
|
|
24
|
-
try {
|
|
25
|
-
setLoading(true);
|
|
26
|
-
const response = await fetch('/api/users');
|
|
27
|
-
if (!response.ok) {
|
|
28
|
-
if (response.status === 401) {
|
|
29
|
-
// Not authorized, maybe session expired
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
throw new Error('Failed to fetch users');
|
|
33
|
-
}
|
|
34
|
-
const data = await response.json();
|
|
35
|
-
setUsers(data);
|
|
36
|
-
} catch (err) {
|
|
37
|
-
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
38
|
-
} finally {
|
|
39
|
-
setLoading(false);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
fetchUsers();
|
|
45
|
-
}, []);
|
|
46
|
-
|
|
47
|
-
const handleCreateUser = async (e: React.FormEvent) => {
|
|
25
|
+
const handleCreateUser = async (e: FormEvent) => {
|
|
48
26
|
e.preventDefault();
|
|
49
|
-
setError(null);
|
|
50
27
|
|
|
51
28
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
username: newUsername,
|
|
57
|
-
password: newPassword,
|
|
58
|
-
isAdmin: newIsAdmin,
|
|
59
|
-
}),
|
|
29
|
+
await createUser({
|
|
30
|
+
username: newUsername,
|
|
31
|
+
password: newPassword,
|
|
32
|
+
isAdmin: newIsAdmin,
|
|
60
33
|
});
|
|
61
34
|
|
|
62
|
-
if (!response.ok) {
|
|
63
|
-
const data = await response.json();
|
|
64
|
-
throw new Error(data.error || 'Failed to create user');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
35
|
setNewUsername('');
|
|
68
36
|
setNewPassword('');
|
|
69
37
|
setNewIsAdmin(false);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
38
|
+
} catch {
|
|
39
|
+
// Error is already handled and exposed via `error` state from the hook
|
|
73
40
|
}
|
|
74
41
|
};
|
|
75
42
|
|
|
76
43
|
const handleDeleteUser = async (username: string) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const response = await fetch(`/api/users/${username}`, {
|
|
81
|
-
method: 'DELETE',
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (!response.ok) {
|
|
85
|
-
const data = await response.json();
|
|
86
|
-
throw new Error(data.error || 'Failed to delete user');
|
|
87
|
-
}
|
|
88
|
-
fetchUsers();
|
|
89
|
-
} catch (err) {
|
|
90
|
-
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
91
|
-
}
|
|
44
|
+
await deleteUser(username);
|
|
92
45
|
};
|
|
93
46
|
|
|
94
47
|
const handleToggleAdmin = async (username: string, currentIsAdmin: boolean) => {
|
|
95
|
-
|
|
96
|
-
const response = await fetch(`/api/users/${username}`, {
|
|
97
|
-
method: 'PATCH',
|
|
98
|
-
headers: { 'Content-Type': 'application/json' },
|
|
99
|
-
body: JSON.stringify({ isAdmin: !currentIsAdmin }),
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
if (!response.ok) {
|
|
103
|
-
const data = await response.json();
|
|
104
|
-
throw new Error(data.error || 'Failed to update user');
|
|
105
|
-
}
|
|
106
|
-
fetchUsers();
|
|
107
|
-
} catch (err) {
|
|
108
|
-
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
109
|
-
}
|
|
48
|
+
await toggleAdmin(username, currentIsAdmin);
|
|
110
49
|
};
|
|
111
50
|
|
|
112
51
|
if (loading) return <div>Loading users...</div>;
|
|
@@ -121,17 +60,23 @@ export function UsersList() {
|
|
|
121
60
|
<div className="flex justify-between items-start">
|
|
122
61
|
<div>
|
|
123
62
|
<h3 className="text-xl font-semibold text-gray-900">{user.username}</h3>
|
|
124
|
-
{user.isAdmin &&
|
|
125
|
-
|
|
63
|
+
{user.isAdmin && (
|
|
64
|
+
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded mt-1">
|
|
65
|
+
Admin
|
|
66
|
+
</span>
|
|
67
|
+
)}
|
|
68
|
+
<p className="text-gray-500 text-sm mt-2">
|
|
69
|
+
Created: {new Date(user.createdAt).toLocaleDateString()}
|
|
70
|
+
</p>
|
|
126
71
|
</div>
|
|
127
72
|
<div className="space-x-2">
|
|
128
|
-
<button
|
|
73
|
+
<button
|
|
129
74
|
onClick={() => handleToggleAdmin(user.username, user.isAdmin)}
|
|
130
75
|
className="text-sm text-blue-600 hover:text-blue-800"
|
|
131
76
|
>
|
|
132
77
|
{user.isAdmin ? 'Remove Admin' : 'Make Admin'}
|
|
133
78
|
</button>
|
|
134
|
-
<button
|
|
79
|
+
<button
|
|
135
80
|
onClick={() => handleDeleteUser(user.username)}
|
|
136
81
|
className="text-sm text-red-600 hover:text-red-800"
|
|
137
82
|
>
|
|
@@ -141,49 +86,55 @@ export function UsersList() {
|
|
|
141
86
|
</div>
|
|
142
87
|
</Card>
|
|
143
88
|
))}
|
|
144
|
-
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
89
|
+
|
|
90
|
+
{allowCreate && (
|
|
91
|
+
<div
|
|
92
|
+
id="create-user"
|
|
93
|
+
className="block p-6 bg-gray-50 rounded-lg border border-dashed border-gray-300"
|
|
94
|
+
>
|
|
95
|
+
<h3 className="text-lg font-semibold text-gray-700 mb-4">Add New User</h3>
|
|
96
|
+
<form onSubmit={handleCreateUser} className="space-y-3">
|
|
97
|
+
<div>
|
|
98
|
+
<input
|
|
99
|
+
type="text"
|
|
100
|
+
placeholder="Username"
|
|
101
|
+
value={newUsername}
|
|
102
|
+
onChange={(e) => setNewUsername(e.target.value)}
|
|
103
|
+
className="w-full p-2 border border-gray-300 rounded"
|
|
104
|
+
required
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
<div>
|
|
108
|
+
<input
|
|
109
|
+
type="password"
|
|
110
|
+
placeholder="Password"
|
|
111
|
+
value={newPassword}
|
|
112
|
+
onChange={(e) => setNewPassword(e.target.value)}
|
|
113
|
+
className="w-full p-2 border border-gray-300 rounded"
|
|
114
|
+
required
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
<div className="flex items-center">
|
|
118
|
+
<input
|
|
119
|
+
type="checkbox"
|
|
120
|
+
id="newIsAdmin"
|
|
121
|
+
checked={newIsAdmin}
|
|
122
|
+
onChange={(e) => setNewIsAdmin(e.target.checked)}
|
|
123
|
+
className="mr-2"
|
|
124
|
+
/>
|
|
125
|
+
<label htmlFor="newIsAdmin" className="text-gray-700">
|
|
126
|
+
Is Admin
|
|
127
|
+
</label>
|
|
128
|
+
</div>
|
|
129
|
+
<button
|
|
130
|
+
type="submit"
|
|
131
|
+
className="w-full bg-blue-600 text-white p-2 rounded hover:bg-blue-700 transition-colors"
|
|
132
|
+
>
|
|
133
|
+
Create User
|
|
134
|
+
</button>
|
|
135
|
+
</form>
|
|
136
|
+
</div>
|
|
137
|
+
)}
|
|
187
138
|
</Section>
|
|
188
139
|
</div>
|
|
189
140
|
);
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export type AdminUser = {
|
|
4
|
+
id: number;
|
|
5
|
+
username: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
updatedAt: string;
|
|
8
|
+
isAdmin: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type CreateUserPayload = {
|
|
12
|
+
username: string;
|
|
13
|
+
password: string;
|
|
14
|
+
isAdmin: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Admin users management hook
|
|
19
|
+
*
|
|
20
|
+
* Centralizes fetching and mutating users so it can be reused
|
|
21
|
+
* from multiple places (homepage, admin pages, header menu, etc.).
|
|
22
|
+
*/
|
|
23
|
+
export function useUsersAdmin() {
|
|
24
|
+
const [users, setUsers] = useState<AdminUser[]>([]);
|
|
25
|
+
const [loading, setLoading] = useState(true);
|
|
26
|
+
const [error, setError] = useState<string | null>(null);
|
|
27
|
+
|
|
28
|
+
const fetchUsers = useCallback(async () => {
|
|
29
|
+
try {
|
|
30
|
+
setLoading(true);
|
|
31
|
+
setError(null);
|
|
32
|
+
|
|
33
|
+
const response = await fetch('/api/users');
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
if (response.status === 401) {
|
|
36
|
+
// Not authorized, maybe session expired or non-admin user
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw new Error('Failed to fetch users');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
setUsers(data);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
46
|
+
} finally {
|
|
47
|
+
setLoading(false);
|
|
48
|
+
}
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
fetchUsers();
|
|
53
|
+
}, [fetchUsers]);
|
|
54
|
+
|
|
55
|
+
const createUser = useCallback(
|
|
56
|
+
async ({ username, password, isAdmin }: CreateUserPayload) => {
|
|
57
|
+
setError(null);
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch('/api/users', {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: { 'Content-Type': 'application/json' },
|
|
63
|
+
body: JSON.stringify({
|
|
64
|
+
username,
|
|
65
|
+
password,
|
|
66
|
+
isAdmin,
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
const data = await response.json().catch(() => ({}));
|
|
72
|
+
throw new Error(data.error || 'Failed to create user');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await fetchUsers();
|
|
76
|
+
} catch (err) {
|
|
77
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
[fetchUsers],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const deleteUser = useCallback(
|
|
85
|
+
async (username: string) => {
|
|
86
|
+
if (!confirm(`Are you sure you want to delete user ${username}?`)) return;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(`/api/users/${username}`, {
|
|
90
|
+
method: 'DELETE',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
const data = await response.json().catch(() => ({}));
|
|
95
|
+
throw new Error(data.error || 'Failed to delete user');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await fetchUsers();
|
|
99
|
+
} catch (err) {
|
|
100
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
[fetchUsers],
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const toggleAdmin = useCallback(
|
|
108
|
+
async (username: string, currentIsAdmin: boolean) => {
|
|
109
|
+
try {
|
|
110
|
+
const response = await fetch(`/api/users/${username}`, {
|
|
111
|
+
method: 'PATCH',
|
|
112
|
+
headers: { 'Content-Type': 'application/json' },
|
|
113
|
+
body: JSON.stringify({ isAdmin: !currentIsAdmin }),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const data = await response.json().catch(() => ({}));
|
|
118
|
+
throw new Error(data.error || 'Failed to update user');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await fetchUsers();
|
|
122
|
+
} catch (err) {
|
|
123
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
[fetchUsers],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
users,
|
|
132
|
+
loading,
|
|
133
|
+
error,
|
|
134
|
+
fetchUsers,
|
|
135
|
+
createUser,
|
|
136
|
+
deleteUser,
|
|
137
|
+
toggleAdmin,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -1,37 +1,69 @@
|
|
|
1
|
+
export type MetadataType = 'TEXT_SINGLE_LINE' | 'TEXT' | 'NUMBER' | 'BOOLEAN' | 'IMAGE_URL' | 'IP_RANGE';
|
|
2
|
+
|
|
1
3
|
export const metadataDefaults = [
|
|
2
4
|
{
|
|
3
5
|
key: 'SERVER_NAME',
|
|
4
6
|
value: 'Promptbook Agents Server',
|
|
5
7
|
note: 'The name of the server displayed in the heading bar',
|
|
8
|
+
type: 'TEXT_SINGLE_LINE',
|
|
6
9
|
},
|
|
7
10
|
{
|
|
8
11
|
key: 'SERVER_DESCRIPTION',
|
|
9
12
|
value: 'Agents server powered by Promptbook',
|
|
10
13
|
note: 'The description of the server displayed in the search engine results',
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
key: 'SERVER_URL',
|
|
14
|
-
value: 'https://ptbk.io',
|
|
15
|
-
note: 'The URL of the server',
|
|
14
|
+
type: 'TEXT',
|
|
16
15
|
},
|
|
17
16
|
{
|
|
18
17
|
key: 'SERVER_LOGO_URL',
|
|
19
18
|
value: '',
|
|
20
19
|
note: 'The URL of the logo displayed in the heading bar',
|
|
20
|
+
type: 'IMAGE_URL',
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
key: 'SERVER_FAVICON_URL',
|
|
24
24
|
value: '',
|
|
25
25
|
note: 'The URL of the favicon',
|
|
26
|
+
type: 'IMAGE_URL',
|
|
26
27
|
},
|
|
27
28
|
{
|
|
28
29
|
key: 'RESTRICT_IP',
|
|
29
30
|
value: '',
|
|
30
31
|
note: 'Comma separated list of allowed IPs or CIDR ranges. If set, only clients from these IPs are allowed to access the server.',
|
|
32
|
+
type: 'IP_RANGE',
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
key: 'FEDERATED_SERVERS',
|
|
34
36
|
value: '',
|
|
35
37
|
note: 'Comma separated list of federated servers URLs. The server will look to all federated servers and list their agents.',
|
|
38
|
+
type: 'TEXT',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: 'IS_VOICE_CALLING_ENABLED',
|
|
42
|
+
value: 'false',
|
|
43
|
+
note: 'Enable or disable voice calling features for agents. When disabled, voice API endpoints will return 403 Forbidden.',
|
|
44
|
+
type: 'BOOLEAN',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: 'IS_FOOTER_SHOWN',
|
|
48
|
+
value: 'true',
|
|
49
|
+
note: 'Show or hide the footer.',
|
|
50
|
+
type: 'BOOLEAN',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
key: 'FOOTER_LINKS',
|
|
54
|
+
value: '[]',
|
|
55
|
+
note: 'Extra links to display in the footer, as a JSON array of objects with title and url properties.',
|
|
56
|
+
type: 'TEXT',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: 'MAX_FILE_UPLOAD_SIZE_MB',
|
|
60
|
+
value: '50', // <- TODO: [🌲] To /config.ts
|
|
61
|
+
note: 'Maximum size of file that can be uploaded in MB.',
|
|
62
|
+
type: 'NUMBER',
|
|
36
63
|
},
|
|
37
|
-
] as const
|
|
64
|
+
] as const satisfies ReadonlyArray<{
|
|
65
|
+
key: string;
|
|
66
|
+
value: string;
|
|
67
|
+
note: string;
|
|
68
|
+
type: MetadataType;
|
|
69
|
+
}>;
|