@promptbook/cli 0.103.0-54 → 0.103.0-56
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/config.ts +0 -2
- package/apps/agents-server/package-lock.json +1163 -0
- package/apps/agents-server/package.json +6 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +79 -6
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +171 -69
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +216 -0
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +78 -0
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileView.tsx +233 -0
- package/apps/agents-server/src/app/agents/[agentName]/CloneAgentButton.tsx +4 -4
- package/apps/agents-server/src/app/agents/[agentName]/InstallPwaButton.tsx +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/QrCodeModal.tsx +90 -0
- package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +80 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +11 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +203 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/route.ts +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/openai/chat/completions/route.ts +3 -169
- package/apps/agents-server/src/app/agents/[agentName]/api/openai/models/route.ts +93 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/openai/v1/chat/completions/route.ts +10 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/openai/v1/models/route.ts +93 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/openrouter/chat/completions/route.ts +10 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +4 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +9 -2
- package/apps/agents-server/src/app/agents/[agentName]/integration/SdkCodeTabs.tsx +31 -0
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +271 -30
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +182 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +108 -165
- package/apps/agents-server/src/app/agents/[agentName]/website-integration/page.tsx +61 -0
- package/apps/agents-server/src/app/api/auth/change-password/route.ts +75 -0
- package/apps/agents-server/src/app/api/chat-feedback/export/route.ts +55 -0
- package/apps/agents-server/src/app/api/chat-history/export/route.ts +55 -0
- package/apps/agents-server/src/app/api/openai/v1/chat/completions/route.ts +6 -0
- package/apps/agents-server/src/app/api/openai/v1/models/route.ts +65 -0
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +12 -32
- package/apps/agents-server/src/app/docs/page.tsx +42 -17
- package/apps/agents-server/src/app/globals.css +129 -0
- package/apps/agents-server/src/app/layout.tsx +8 -2
- package/apps/agents-server/src/app/manifest.ts +1 -1
- package/apps/agents-server/src/components/ChangePasswordDialog/ChangePasswordDialog.tsx +41 -0
- package/apps/agents-server/src/components/ChangePasswordForm/ChangePasswordForm.tsx +159 -0
- package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +87 -0
- package/apps/agents-server/src/components/Header/Header.tsx +94 -38
- package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +20 -0
- package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +18 -0
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +18 -0
- package/apps/agents-server/src/database/migrations/2025-12-0070-chat-history-source.sql +2 -0
- package/apps/agents-server/src/database/schema.ts +6 -0
- package/apps/agents-server/src/middleware.ts +1 -1
- package/apps/agents-server/src/utils/convertToCsv.ts +31 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +355 -0
- package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +100 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +128 -0
- package/apps/agents-server/tailwind.config.ts +1 -1
- package/esm/index.es.js +1188 -175
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +4 -0
- package/esm/typings/src/book-components/Chat/LlmChat/LlmChatProps.d.ts +5 -0
- package/esm/typings/src/commitments/CLOSED/CLOSED.d.ts +35 -0
- package/esm/typings/src/commitments/COMPONENT/COMPONENT.d.ts +28 -0
- package/esm/typings/src/commitments/FROM/FROM.d.ts +34 -0
- package/esm/typings/src/commitments/LANGUAGE/LANGUAGE.d.ts +35 -0
- package/esm/typings/src/commitments/META_COLOR/META_COLOR.d.ts +6 -0
- package/esm/typings/src/commitments/META_FONT/META_FONT.d.ts +42 -0
- package/esm/typings/src/commitments/OPEN/OPEN.d.ts +35 -0
- package/esm/typings/src/commitments/USE/USE.d.ts +53 -0
- package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.d.ts +38 -0
- package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.test.d.ts +1 -0
- package/esm/typings/src/commitments/USE_MCP/USE_MCP.d.ts +37 -0
- package/esm/typings/src/commitments/USE_SEARCH_ENGINE/USE_SEARCH_ENGINE.d.ts +38 -0
- package/esm/typings/src/commitments/index.d.ts +12 -1
- package/esm/typings/src/playground/playground.d.ts +3 -0
- package/esm/typings/src/utils/color/Color.d.ts +8 -0
- package/esm/typings/src/utils/color/css-colors.d.ts +1 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +1180 -167
- package/umd/index.umd.js.map +1 -1
- package/esm/typings/src/playground/playground1.d.ts +0 -2
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Loader2, Lock } from 'lucide-react';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
|
|
6
|
+
type ChangePasswordFormProps = {
|
|
7
|
+
onSuccess?: () => void;
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function ChangePasswordForm(props: ChangePasswordFormProps) {
|
|
12
|
+
const { onSuccess, className } = props;
|
|
13
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
14
|
+
const [error, setError] = useState<string | null>(null);
|
|
15
|
+
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
|
16
|
+
|
|
17
|
+
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
setSuccessMessage(null);
|
|
22
|
+
|
|
23
|
+
const formData = new FormData(event.currentTarget);
|
|
24
|
+
const currentPassword = formData.get('currentPassword') as string;
|
|
25
|
+
const newPassword = formData.get('newPassword') as string;
|
|
26
|
+
const confirmNewPassword = formData.get('confirmNewPassword') as string;
|
|
27
|
+
|
|
28
|
+
if (newPassword !== confirmNewPassword) {
|
|
29
|
+
setError('New passwords do not match');
|
|
30
|
+
setIsLoading(false);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch('/api/auth/change-password', {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({ currentPassword, newPassword }),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const result = await response.json();
|
|
44
|
+
|
|
45
|
+
if (response.ok) {
|
|
46
|
+
setSuccessMessage('Password changed successfully');
|
|
47
|
+
// Reset form
|
|
48
|
+
event.currentTarget.reset();
|
|
49
|
+
if (onSuccess) {
|
|
50
|
+
setTimeout(onSuccess, 1500);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
setError(result.error || 'An error occurred');
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
setError('An unexpected error occurred');
|
|
57
|
+
console.error(error);
|
|
58
|
+
} finally {
|
|
59
|
+
setIsLoading(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<form onSubmit={handleSubmit} className={`space-y-4 ${className || ''}`}>
|
|
65
|
+
<div className="space-y-2">
|
|
66
|
+
<label
|
|
67
|
+
htmlFor="currentPassword"
|
|
68
|
+
className="text-sm font-medium text-gray-700 block"
|
|
69
|
+
>
|
|
70
|
+
Current Password
|
|
71
|
+
</label>
|
|
72
|
+
<div className="relative">
|
|
73
|
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400">
|
|
74
|
+
<Lock className="w-4 h-4" />
|
|
75
|
+
</div>
|
|
76
|
+
<input
|
|
77
|
+
id="currentPassword"
|
|
78
|
+
name="currentPassword"
|
|
79
|
+
type="password"
|
|
80
|
+
required
|
|
81
|
+
className="block w-full pl-10 h-10 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-promptbook-blue focus:border-transparent disabled:opacity-50"
|
|
82
|
+
placeholder="Enter current password"
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div className="space-y-2">
|
|
88
|
+
<label
|
|
89
|
+
htmlFor="newPassword"
|
|
90
|
+
className="text-sm font-medium text-gray-700 block"
|
|
91
|
+
>
|
|
92
|
+
New Password
|
|
93
|
+
</label>
|
|
94
|
+
<div className="relative">
|
|
95
|
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400">
|
|
96
|
+
<Lock className="w-4 h-4" />
|
|
97
|
+
</div>
|
|
98
|
+
<input
|
|
99
|
+
id="newPassword"
|
|
100
|
+
name="newPassword"
|
|
101
|
+
type="password"
|
|
102
|
+
required
|
|
103
|
+
className="block w-full pl-10 h-10 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-promptbook-blue focus:border-transparent disabled:opacity-50"
|
|
104
|
+
placeholder="Enter new password"
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div className="space-y-2">
|
|
110
|
+
<label
|
|
111
|
+
htmlFor="confirmNewPassword"
|
|
112
|
+
className="text-sm font-medium text-gray-700 block"
|
|
113
|
+
>
|
|
114
|
+
Confirm New Password
|
|
115
|
+
</label>
|
|
116
|
+
<div className="relative">
|
|
117
|
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400">
|
|
118
|
+
<Lock className="w-4 h-4" />
|
|
119
|
+
</div>
|
|
120
|
+
<input
|
|
121
|
+
id="confirmNewPassword"
|
|
122
|
+
name="confirmNewPassword"
|
|
123
|
+
type="password"
|
|
124
|
+
required
|
|
125
|
+
className="block w-full pl-10 h-10 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-promptbook-blue focus:border-transparent disabled:opacity-50"
|
|
126
|
+
placeholder="Confirm new password"
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{error && (
|
|
132
|
+
<div className="p-3 text-sm text-red-500 bg-red-50 border border-red-200 rounded-md">
|
|
133
|
+
{error}
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{successMessage && (
|
|
138
|
+
<div className="p-3 text-sm text-green-500 bg-green-50 border border-green-200 rounded-md">
|
|
139
|
+
{successMessage}
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
<button
|
|
144
|
+
type="submit"
|
|
145
|
+
disabled={isLoading}
|
|
146
|
+
className="w-full inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-promptbook-blue-dark text-white hover:bg-promptbook-blue-dark/90 focus:outline-none focus:ring-2 focus:ring-promptbook-blue focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none transition-colors"
|
|
147
|
+
>
|
|
148
|
+
{isLoading ? (
|
|
149
|
+
<>
|
|
150
|
+
<Loader2 className="mr-2 w-4 h-4 animate-spin" />
|
|
151
|
+
Changing Password...
|
|
152
|
+
</>
|
|
153
|
+
) : (
|
|
154
|
+
'Change Password'
|
|
155
|
+
)}
|
|
156
|
+
</button>
|
|
157
|
+
</form>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import ReactMarkdown from 'react-markdown';
|
|
2
|
+
import remarkGfm from 'remark-gfm';
|
|
3
|
+
import { string_book } from '../../../../../src/book-2.0/agent-source/string_book';
|
|
4
|
+
import { BookEditor } from '../../../../../src/book-components/BookEditor/BookEditor';
|
|
5
|
+
import { OpenMojiIcon } from '../OpenMojiIcon/OpenMojiIcon';
|
|
6
|
+
|
|
7
|
+
type DocumentationContentProps = {
|
|
8
|
+
primary: {
|
|
9
|
+
type: string;
|
|
10
|
+
icon: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
documentation: string;
|
|
13
|
+
};
|
|
14
|
+
aliases?: string[];
|
|
15
|
+
isPrintOnly?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function DocumentationContent({ primary, aliases = [], isPrintOnly = false }: DocumentationContentProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div className={`bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden ${isPrintOnly ? 'shadow-none border-none' : ''}`}>
|
|
21
|
+
<div className={`p-8 border-b border-gray-100 bg-gray-50/50 ${isPrintOnly ? 'border-none bg-white p-0 mb-4' : ''}`}>
|
|
22
|
+
<div className="flex items-center gap-4 mb-4">
|
|
23
|
+
<h1 className="text-4xl font-bold text-gray-900 tracking-tight">
|
|
24
|
+
<OpenMojiIcon icon={primary.icon} className="mr-3" />
|
|
25
|
+
{primary.type}
|
|
26
|
+
{aliases.length > 0 && (
|
|
27
|
+
<span className="text-gray-400 font-normal ml-4 text-2xl">
|
|
28
|
+
/ {aliases.join(' / ')}
|
|
29
|
+
</span>
|
|
30
|
+
)}
|
|
31
|
+
</h1>
|
|
32
|
+
{!isPrintOnly && (
|
|
33
|
+
<span className="px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-700">
|
|
34
|
+
Commitment
|
|
35
|
+
</span>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
{primary.description && (
|
|
39
|
+
<p className="text-xl text-gray-600 leading-relaxed max-w-3xl">
|
|
40
|
+
{primary.description}
|
|
41
|
+
</p>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div className={`p-8 ${isPrintOnly ? 'p-0' : ''}`}>
|
|
46
|
+
<article className="prose prose-lg prose-slate max-w-none prose-headings:font-bold prose-headings:tracking-tight prose-headings:text-gray-900 prose-h1:text-4xl prose-h1:mb-8 prose-h2:text-2xl prose-h2:mt-12 prose-h2:mb-6 prose-h2:pb-2 prose-h2:border-b prose-h2:border-gray-200 prose-h3:text-xl prose-h3:mt-8 prose-h3:mb-4 prose-h3:text-gray-800 prose-p:text-gray-600 prose-p:leading-relaxed prose-p:mb-6 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:text-blue-700 hover:prose-a:underline prose-a:transition-colors prose-strong:font-bold prose-strong:text-gray-900 prose-code:text-blue-600 prose-code:bg-blue-50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded-md prose-code:before:content-none prose-code:after:content-none prose-code:font-medium prose-pre:bg-gray-900 prose-pre:text-gray-100 prose-pre:shadow-lg prose-pre:rounded-xl prose-pre:p-6 prose-ul:list-disc prose-ul:pl-6 prose-li:marker:text-gray-400 prose-li:mb-2 prose-ol:list-decimal prose-ol:pl-6 prose-li:mb-2 prose-blockquote:border-l-4 prose-blockquote:border-blue-500 prose-blockquote:bg-blue-50/50 prose-blockquote:py-2 prose-blockquote:px-4 prose-blockquote:rounded-r-lg prose-blockquote:not-italic prose-blockquote:text-gray-700 prose-blockquote:my-8 prose-img:rounded-xl prose-img:shadow-md prose-img:my-8 prose-hr:border-gray-200 prose-hr:my-10 prose-table:w-full prose-th:text-left prose-th:py-2 prose-th:px-3 prose-th:bg-gray-100 prose-th:font-semibold prose-th:text-gray-900 prose-td:py-2 prose-td:px-3 prose-td:border-b prose-td:border-gray-200 prose-tr:hover:bg-gray-50">
|
|
47
|
+
<ReactMarkdown
|
|
48
|
+
remarkPlugins={[remarkGfm]}
|
|
49
|
+
components={{
|
|
50
|
+
code(props) {
|
|
51
|
+
const { children, className, node, ...rest } = props;
|
|
52
|
+
const match = /language-(\w+)/.exec(className || '');
|
|
53
|
+
if (match && match[1] === 'book') {
|
|
54
|
+
const value = String(children).replace(/\n$/, '');
|
|
55
|
+
// Estimate height: lines * 30px + padding
|
|
56
|
+
const lineCount = value.split('\n').length;
|
|
57
|
+
const height = lineCount * 30 + 40; // 30px per line + 40px buffer
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="not-prose my-6 rounded-lg overflow-hidden border border-gray-200 shadow-sm print:border-gray-300">
|
|
61
|
+
<BookEditor
|
|
62
|
+
value={value as string_book}
|
|
63
|
+
isReadonly={true}
|
|
64
|
+
isVerbose={false}
|
|
65
|
+
height={`${height}px`}
|
|
66
|
+
isDownloadButtonShown={false}
|
|
67
|
+
isAboutButtonShown={false}
|
|
68
|
+
isFullscreenButtonShown={true}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return (
|
|
74
|
+
<code className={className} {...rest}>
|
|
75
|
+
{children}
|
|
76
|
+
</code>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
{primary.documentation}
|
|
82
|
+
</ReactMarkdown>
|
|
83
|
+
</article>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import promptbookLogoBlueTransparent from '@/public/logo-blue-white-256.png';
|
|
4
4
|
import { $createAgentAction, logoutAction } from '@/src/app/actions';
|
|
5
|
-
import { ArrowRight, ChevronDown, LogIn, LogOut } from 'lucide-react';
|
|
5
|
+
import { ArrowRight, ChevronDown, Lock, LogIn, LogOut, User } from 'lucide-react';
|
|
6
6
|
import Image from 'next/image';
|
|
7
7
|
import Link from 'next/link';
|
|
8
8
|
import { useRouter } from 'next/navigation';
|
|
@@ -12,6 +12,7 @@ import { HamburgerMenu } from '../../../../../src/book-components/_common/Hambur
|
|
|
12
12
|
import { just } from '../../../../../src/utils/organization/just';
|
|
13
13
|
import type { UserInfo } from '../../utils/getCurrentUser';
|
|
14
14
|
import { getVisibleCommitmentDefinitions } from '../../utils/getVisibleCommitmentDefinitions';
|
|
15
|
+
import { ChangePasswordDialog } from '../ChangePasswordDialog/ChangePasswordDialog';
|
|
15
16
|
import { LoginDialog } from '../LoginDialog/LoginDialog';
|
|
16
17
|
import { useUsersAdmin } from '../UsersList/useUsersAdmin';
|
|
17
18
|
|
|
@@ -73,12 +74,16 @@ export function Header(props: HeaderProps) {
|
|
|
73
74
|
|
|
74
75
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
75
76
|
const [isLoginOpen, setIsLoginOpen] = useState(false);
|
|
77
|
+
const [isChangePasswordOpen, setIsChangePasswordOpen] = useState(false);
|
|
76
78
|
const [isAgentsOpen, setIsAgentsOpen] = useState(false);
|
|
77
79
|
const [isDocsOpen, setIsDocsOpen] = useState(false);
|
|
78
80
|
const [isUsersOpen, setIsUsersOpen] = useState(false);
|
|
81
|
+
const [isSystemOpen, setIsSystemOpen] = useState(false);
|
|
82
|
+
const [isProfileOpen, setIsProfileOpen] = useState(false);
|
|
79
83
|
const [isMobileAgentsOpen, setIsMobileAgentsOpen] = useState(false);
|
|
80
84
|
const [isMobileDocsOpen, setIsMobileDocsOpen] = useState(false);
|
|
81
85
|
const [isMobileUsersOpen, setIsMobileUsersOpen] = useState(false);
|
|
86
|
+
const [isMobileSystemOpen, setIsMobileSystemOpen] = useState(false);
|
|
82
87
|
const [isCreatingAgent, setIsCreatingAgent] = useState(false);
|
|
83
88
|
const router = useRouter();
|
|
84
89
|
|
|
@@ -131,7 +136,9 @@ export function Header(props: HeaderProps) {
|
|
|
131
136
|
label: (
|
|
132
137
|
<>
|
|
133
138
|
{primary.type}
|
|
134
|
-
{aliases.length > 0 &&
|
|
139
|
+
{aliases.length > 0 && (
|
|
140
|
+
<span className="text-gray-400 font-normal"> / {aliases.join(' / ')}</span>
|
|
141
|
+
)}
|
|
135
142
|
</>
|
|
136
143
|
),
|
|
137
144
|
href: `/docs/${primary.type}`,
|
|
@@ -210,24 +217,30 @@ export function Header(props: HeaderProps) {
|
|
|
210
217
|
],
|
|
211
218
|
},
|
|
212
219
|
{
|
|
213
|
-
type: '
|
|
214
|
-
label: '
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
220
|
+
type: 'dropdown' as const,
|
|
221
|
+
label: 'System',
|
|
222
|
+
isOpen: isSystemOpen,
|
|
223
|
+
setIsOpen: setIsSystemOpen,
|
|
224
|
+
isMobileOpen: isMobileSystemOpen,
|
|
225
|
+
setIsMobileOpen: setIsMobileSystemOpen,
|
|
226
|
+
items: [
|
|
227
|
+
{
|
|
228
|
+
label: 'API Tokens',
|
|
229
|
+
href: '/admin/api-tokens',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
label: 'Metadata',
|
|
233
|
+
href: '/admin/metadata',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
label: 'Chat history',
|
|
237
|
+
href: '/admin/chat-history',
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
label: 'Chat feedback',
|
|
241
|
+
href: '/admin/chat-feedback',
|
|
242
|
+
},
|
|
243
|
+
],
|
|
231
244
|
},
|
|
232
245
|
{
|
|
233
246
|
type: 'link' as const,
|
|
@@ -241,6 +254,7 @@ export function Header(props: HeaderProps) {
|
|
|
241
254
|
return (
|
|
242
255
|
<header className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200 h-16">
|
|
243
256
|
<LoginDialog isOpen={isLoginOpen} onClose={() => setIsLoginOpen(false)} />
|
|
257
|
+
<ChangePasswordDialog isOpen={isChangePasswordOpen} onClose={() => setIsChangePasswordOpen(false)} />
|
|
244
258
|
<div className="container mx-auto px-4 h-full">
|
|
245
259
|
<div className="flex items-center justify-between h-full">
|
|
246
260
|
{/* Logo */}
|
|
@@ -370,24 +384,56 @@ export function Header(props: HeaderProps) {
|
|
|
370
384
|
|
|
371
385
|
{(currentUser || isAdmin) && (
|
|
372
386
|
<div className="hidden lg:flex items-center gap-3">
|
|
373
|
-
<
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
387
|
+
<div className="relative">
|
|
388
|
+
<button
|
|
389
|
+
onClick={() => setIsProfileOpen(!isProfileOpen)}
|
|
390
|
+
onBlur={() => setTimeout(() => setIsProfileOpen(false), 200)}
|
|
391
|
+
className="flex items-center gap-2 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors px-3 py-2 rounded-md hover:bg-gray-50"
|
|
392
|
+
>
|
|
393
|
+
<div className="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center text-blue-600">
|
|
394
|
+
<User className="w-4 h-4" />
|
|
395
|
+
</div>
|
|
396
|
+
<div className="flex flex-col items-start">
|
|
397
|
+
<span className="leading-none">{currentUser?.username || 'Admin'}</span>
|
|
398
|
+
{(currentUser?.isAdmin || isAdmin) && (
|
|
399
|
+
<span className="text-xs text-blue-600">Admin</span>
|
|
400
|
+
)}
|
|
401
|
+
</div>
|
|
402
|
+
<ChevronDown className="w-4 h-4 ml-1 opacity-50" />
|
|
403
|
+
</button>
|
|
404
|
+
|
|
405
|
+
{isProfileOpen && (
|
|
406
|
+
<div className="absolute top-full right-0 mt-2 w-56 bg-white rounded-md shadow-lg border border-gray-100 py-1 z-50 animate-in fade-in zoom-in-95 duration-200">
|
|
407
|
+
<div className="px-4 py-3 border-b border-gray-100">
|
|
408
|
+
<p className="text-sm font-medium text-gray-900">
|
|
409
|
+
{currentUser?.username || 'Admin'}
|
|
410
|
+
</p>
|
|
411
|
+
{(currentUser?.isAdmin || isAdmin) && (
|
|
412
|
+
<p className="text-xs text-blue-600 mt-1">Administrator</p>
|
|
413
|
+
)}
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
<button
|
|
417
|
+
onClick={() => setIsChangePasswordOpen(true)}
|
|
418
|
+
className="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900 flex items-center gap-2"
|
|
419
|
+
>
|
|
420
|
+
<Lock className="w-4 h-4" />
|
|
421
|
+
Change Password
|
|
422
|
+
</button>
|
|
423
|
+
|
|
424
|
+
<button
|
|
425
|
+
onClick={() => {
|
|
426
|
+
handleLogout();
|
|
427
|
+
setIsMenuOpen(false);
|
|
428
|
+
}}
|
|
429
|
+
className="w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-50 hover:text-red-700 flex items-center gap-2"
|
|
430
|
+
>
|
|
431
|
+
<LogOut className="w-4 h-4" />
|
|
432
|
+
Log out
|
|
433
|
+
</button>
|
|
434
|
+
</div>
|
|
379
435
|
)}
|
|
380
|
-
</
|
|
381
|
-
<button
|
|
382
|
-
onClick={() => {
|
|
383
|
-
handleLogout();
|
|
384
|
-
setIsMenuOpen(false);
|
|
385
|
-
}}
|
|
386
|
-
className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 transition-colors"
|
|
387
|
-
>
|
|
388
|
-
Log out
|
|
389
|
-
<LogOut className="ml-2 w-4 h-4" />
|
|
390
|
-
</button>
|
|
436
|
+
</div>
|
|
391
437
|
</div>
|
|
392
438
|
)}
|
|
393
439
|
|
|
@@ -403,7 +449,7 @@ export function Header(props: HeaderProps) {
|
|
|
403
449
|
{/* Mobile Navigation */}
|
|
404
450
|
{isMenuOpen && (
|
|
405
451
|
<div
|
|
406
|
-
className="lg:hidden absolute top-16 left-0 right-0 z-50 bg-white
|
|
452
|
+
className="lg:hidden absolute top-16 left-0 right-0 z-50 bg-white shadow-xl py-4 border-t border-gray-100 animate-in slide-in-from-top-2 h-[calc(100vh-4rem)] overflow-y-auto"
|
|
407
453
|
style={{
|
|
408
454
|
backdropFilter: 'blur(20px)',
|
|
409
455
|
WebkitBackdropFilter: 'blur(20px)',
|
|
@@ -435,6 +481,16 @@ export function Header(props: HeaderProps) {
|
|
|
435
481
|
</span>
|
|
436
482
|
)}
|
|
437
483
|
</div>
|
|
484
|
+
<button
|
|
485
|
+
onClick={() => {
|
|
486
|
+
setIsChangePasswordOpen(true);
|
|
487
|
+
setIsMenuOpen(false);
|
|
488
|
+
}}
|
|
489
|
+
className="flex items-center gap-2 text-base font-medium text-gray-600 hover:text-gray-900 w-full"
|
|
490
|
+
>
|
|
491
|
+
<Lock className="w-4 h-4" />
|
|
492
|
+
Change Password
|
|
493
|
+
</button>
|
|
438
494
|
<button
|
|
439
495
|
onClick={() => {
|
|
440
496
|
handleLogout();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
type OpenMojiIconProps = DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
|
4
|
+
icon: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Renders an emoji using the OpenMoji black and white font
|
|
9
|
+
*/
|
|
10
|
+
export function OpenMojiIcon({ icon, className, style, ...rest }: OpenMojiIconProps) {
|
|
11
|
+
return (
|
|
12
|
+
<span
|
|
13
|
+
className={className}
|
|
14
|
+
style={{ ...style, fontFamily: '"OpenMojiBlack", sans-serif' }}
|
|
15
|
+
{...rest}
|
|
16
|
+
>
|
|
17
|
+
{icon}
|
|
18
|
+
</span>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { OpenMojiIcon } from '../OpenMojiIcon/OpenMojiIcon';
|
|
4
|
+
|
|
5
|
+
export function PrintButton() {
|
|
6
|
+
return (
|
|
7
|
+
<button
|
|
8
|
+
onClick={() => window.print()}
|
|
9
|
+
className="fixed bottom-8 right-8 bg-blue-600 hover:bg-blue-700 text-white rounded-full p-4 shadow-lg transition-all hover:scale-105 print:hidden z-50 flex items-center justify-center gap-2 group"
|
|
10
|
+
title="Print documentation"
|
|
11
|
+
>
|
|
12
|
+
<OpenMojiIcon icon="🖨️" className="text-2xl" />
|
|
13
|
+
<span className="max-w-0 overflow-hidden group-hover:max-w-xs transition-all duration-300 ease-in-out whitespace-nowrap">
|
|
14
|
+
Print
|
|
15
|
+
</span>
|
|
16
|
+
</button>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function PrintHeader({ title }: { title?: string }) {
|
|
2
|
+
return (
|
|
3
|
+
<div className="hidden print:block mb-8 border-b-2 border-blue-600 pb-4">
|
|
4
|
+
<div className="flex justify-between items-end">
|
|
5
|
+
<div>
|
|
6
|
+
<h1 className="text-3xl font-bold text-gray-900 font-poppins">Agents Server</h1>
|
|
7
|
+
<div className="text-sm text-gray-500 mt-1 flex items-center gap-1">
|
|
8
|
+
Powered by <span className="font-semibold text-blue-600">Promptbook</span>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
{title && <h2 className="text-xl font-semibold text-gray-700">{title}</h2>}
|
|
12
|
+
</div>
|
|
13
|
+
<div className="text-xs text-gray-400 mt-2 text-right">
|
|
14
|
+
{new Date().toLocaleDateString()}
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -128,6 +128,8 @@ export type AgentsServerDatabase = {
|
|
|
128
128
|
userAgent: string | null;
|
|
129
129
|
language: string | null;
|
|
130
130
|
platform: string | null;
|
|
131
|
+
source: 'AGENT_PAGE_CHAT' | 'OPENAI_API_COMPATIBILITY' | null;
|
|
132
|
+
apiKey: string | null;
|
|
131
133
|
};
|
|
132
134
|
Insert: {
|
|
133
135
|
id?: number;
|
|
@@ -143,6 +145,8 @@ export type AgentsServerDatabase = {
|
|
|
143
145
|
userAgent?: string | null;
|
|
144
146
|
language?: string | null;
|
|
145
147
|
platform?: string | null;
|
|
148
|
+
source?: 'AGENT_PAGE_CHAT' | 'OPENAI_API_COMPATIBILITY' | null;
|
|
149
|
+
apiKey?: string | null;
|
|
146
150
|
};
|
|
147
151
|
Update: {
|
|
148
152
|
id?: number;
|
|
@@ -158,6 +162,8 @@ export type AgentsServerDatabase = {
|
|
|
158
162
|
userAgent?: string | null;
|
|
159
163
|
language?: string | null;
|
|
160
164
|
platform?: string | null;
|
|
165
|
+
source?: 'AGENT_PAGE_CHAT' | 'OPENAI_API_COMPATIBILITY' | null;
|
|
166
|
+
apiKey?: string | null;
|
|
161
167
|
};
|
|
162
168
|
Relationships: [];
|
|
163
169
|
};
|
|
@@ -134,7 +134,7 @@ export async function middleware(req: NextRequest) {
|
|
|
134
134
|
headers: {
|
|
135
135
|
'Access-Control-Allow-Origin': '*',
|
|
136
136
|
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
137
|
-
'Access-Control-Allow-Headers': 'Content-Type',
|
|
137
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
138
138
|
},
|
|
139
139
|
});
|
|
140
140
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts an array of objects to a CSV string
|
|
3
|
+
*/
|
|
4
|
+
export function convertToCsv(data: Array<Record<string, unknown>>): string {
|
|
5
|
+
if (data.length === 0) {
|
|
6
|
+
return '';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const headers = Object.keys(data[0]);
|
|
10
|
+
const csvRows = [headers.join(',')];
|
|
11
|
+
|
|
12
|
+
for (const row of data) {
|
|
13
|
+
const values = headers.map((header) => {
|
|
14
|
+
let value = row[header];
|
|
15
|
+
|
|
16
|
+
if (value === null || value === undefined) {
|
|
17
|
+
value = '';
|
|
18
|
+
} else if (typeof value === 'object') {
|
|
19
|
+
value = JSON.stringify(value);
|
|
20
|
+
} else {
|
|
21
|
+
value = String(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const escaped = (value as string).replace(/"/g, '""');
|
|
25
|
+
return `"${escaped}"`;
|
|
26
|
+
});
|
|
27
|
+
csvRows.push(values.join(','));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return csvRows.join('\n');
|
|
31
|
+
}
|