@promptbook/cli 0.104.0-1 → 0.104.0-3
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/next.config.ts +2 -2
- package/apps/agents-server/package.json +7 -3
- package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
- package/apps/agents-server/public/swagger.json +115 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
- package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +3 -3
- package/apps/agents-server/src/app/actions.ts +17 -5
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +2 -0
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +18 -0
- package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +1 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +214 -0
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +1 -1
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +12 -6
- package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +87 -0
- package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +10 -12
- package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +19 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +41 -0
- package/apps/agents-server/src/app/api/agents/route.ts +28 -3
- package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
- package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
- package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
- package/apps/agents-server/src/app/api/upload/route.ts +128 -45
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
- package/apps/agents-server/src/app/docs/page.tsx +12 -12
- package/apps/agents-server/src/app/globals.css +140 -33
- package/apps/agents-server/src/app/layout.tsx +27 -22
- package/apps/agents-server/src/app/page.tsx +50 -4
- package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
- package/apps/agents-server/src/app/recycle-bin/page.tsx +25 -41
- package/apps/agents-server/src/app/sitemap.xml/route.ts +6 -3
- package/apps/agents-server/src/app/swagger/page.tsx +14 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +9 -98
- package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
- package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
- package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
- package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
- package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
- package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
- package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
- package/apps/agents-server/src/components/Header/Header.tsx +106 -40
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +104 -20
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +72 -12
- package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +50 -0
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
- package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
- package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
- package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
- package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
- package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
- package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
- package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
- package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
- package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
- package/apps/agents-server/src/database/schema.ts +109 -0
- package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
- package/apps/agents-server/src/middleware.ts +19 -23
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +6 -1
- package/apps/agents-server/src/utils/auth.ts +117 -17
- package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
- package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +21 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
- package/esm/index.es.js +194 -34
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/types.index.d.ts +8 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
- package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
- package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
- package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +13 -7
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +6 -0
- package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
- package/esm/typings/src/types/Message.d.ts +49 -0
- package/esm/typings/src/types/typeAliases.d.ts +12 -0
- package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
- package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
- package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +200 -40
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/package-lock.json +0 -27
- package/apps/agents-server/public/fonts/download-font.js +0 -22
- package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Editor from '@monaco-editor/react';
|
|
4
|
+
import { ArrowLeftIcon, ChevronDownIcon, CodeIcon } from 'lucide-react';
|
|
5
|
+
import Link from 'next/link';
|
|
6
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
type Transpiler = {
|
|
9
|
+
name: string;
|
|
10
|
+
title: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type TranspilationResult = {
|
|
14
|
+
code: string;
|
|
15
|
+
transpiler: Transpiler;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function getLanguageFromTranspiler(transpilerName?: string): string {
|
|
19
|
+
if (!transpilerName) return 'plaintext';
|
|
20
|
+
|
|
21
|
+
// Map transpiler names to Monaco language identifiers
|
|
22
|
+
if (transpilerName.includes('openai-sdk')) return 'javascript';
|
|
23
|
+
if (transpilerName.includes('langchain') || transpilerName.includes('python')) return 'python';
|
|
24
|
+
if (transpilerName.includes('markdown')) return 'markdown';
|
|
25
|
+
|
|
26
|
+
// Default to plaintext for unknown transpilers
|
|
27
|
+
return 'plaintext';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default function AgentCodePage({ params }: { params: Promise<{ agentName: string }> }) {
|
|
31
|
+
const [agentName, setAgentName] = useState<string>('');
|
|
32
|
+
const [agentProfile, setAgentProfile] = useState<unknown>(null);
|
|
33
|
+
const [transpilers, setTranspilers] = useState<Transpiler[]>([]);
|
|
34
|
+
const [selectedTranspiler, setSelectedTranspiler] = useState<Transpiler | null>(null);
|
|
35
|
+
const [transpiledCode, setTranspiledCode] = useState<string>('');
|
|
36
|
+
const [loading, setLoading] = useState(false);
|
|
37
|
+
const [error, setError] = useState<string>('');
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
params.then((p) => setAgentName(p.agentName));
|
|
41
|
+
}, [params]);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!agentName) return;
|
|
45
|
+
|
|
46
|
+
// Fetch agent profile
|
|
47
|
+
fetch(`/api/agents/${encodeURIComponent(agentName)}`)
|
|
48
|
+
.then((res) => res.json())
|
|
49
|
+
.then((data) => setAgentProfile(data))
|
|
50
|
+
.catch((err) => console.error('Error fetching agent profile:', err));
|
|
51
|
+
|
|
52
|
+
// Fetch available transpilers
|
|
53
|
+
fetch(`/agents/${encodeURIComponent(agentName)}/code/api`)
|
|
54
|
+
.then((res) => res.json())
|
|
55
|
+
.then((data) => {
|
|
56
|
+
setTranspilers(data.transpilers || []);
|
|
57
|
+
if (data.transpilers && data.transpilers.length > 0) {
|
|
58
|
+
setSelectedTranspiler(data.transpilers[0]);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.catch((err) => console.error('Error fetching transpilers:', err));
|
|
62
|
+
}, [agentName]);
|
|
63
|
+
|
|
64
|
+
const transpileCode = useCallback(
|
|
65
|
+
async (transpilerName: string) => {
|
|
66
|
+
setLoading(true);
|
|
67
|
+
setError('');
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(`/agents/${encodeURIComponent(agentName)}/code/api`, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify({ transpilerName }),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const errorData = await response.json();
|
|
80
|
+
throw new Error(errorData.error || 'Failed to transpile code');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const result: TranspilationResult = await response.json();
|
|
84
|
+
setTranspiledCode(result.code);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
setError(err instanceof Error ? err.message : 'Failed to transpile code');
|
|
87
|
+
setTranspiledCode('');
|
|
88
|
+
} finally {
|
|
89
|
+
setLoading(false);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
[agentName],
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (selectedTranspiler && agentName) {
|
|
97
|
+
transpileCode(selectedTranspiler.name);
|
|
98
|
+
}
|
|
99
|
+
}, [selectedTranspiler, agentName, transpileCode]);
|
|
100
|
+
|
|
101
|
+
if (!agentProfile) {
|
|
102
|
+
return (
|
|
103
|
+
<div className="min-h-screen p-6 md:p-12 flex flex-col items-center bg-gray-50">
|
|
104
|
+
<div className="w-full max-w-4xl bg-white rounded-xl shadow-sm border border-gray-200 p-12">
|
|
105
|
+
<div className="text-center">Loading...</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div className="min-h-screen p-6 md:p-12 flex flex-col items-center bg-gray-50">
|
|
113
|
+
<div className="w-full max-w-4xl bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
114
|
+
{/* Header */}
|
|
115
|
+
<div className="p-6 border-b border-gray-200 flex items-center gap-4">
|
|
116
|
+
{/* eslint-disable @typescript-eslint/no-explicit-any, @next/next/no-img-element */}
|
|
117
|
+
{(agentProfile as any)?.meta?.image && (
|
|
118
|
+
<img
|
|
119
|
+
src={(agentProfile as any).meta.image as string}
|
|
120
|
+
alt={(agentProfile as any).meta.fullname || agentName}
|
|
121
|
+
className="w-16 h-16 rounded-full object-cover border-2 border-gray-200"
|
|
122
|
+
/>
|
|
123
|
+
)}
|
|
124
|
+
<div className="flex-1">
|
|
125
|
+
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
|
|
126
|
+
<h1 className="text-2xl font-bold text-gray-900">
|
|
127
|
+
{(agentProfile as any)?.meta?.fullname || agentName}
|
|
128
|
+
</h1>
|
|
129
|
+
<p className="text-gray-500 flex items-center gap-2">
|
|
130
|
+
<CodeIcon className="w-4 h-4" />
|
|
131
|
+
Generated Code
|
|
132
|
+
</p>
|
|
133
|
+
</div>
|
|
134
|
+
<Link
|
|
135
|
+
href={`/agents/${encodeURIComponent(agentName)}`}
|
|
136
|
+
className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors"
|
|
137
|
+
title="Back to Agent"
|
|
138
|
+
>
|
|
139
|
+
<ArrowLeftIcon className="w-6 h-6" />
|
|
140
|
+
</Link>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div className="p-6">
|
|
144
|
+
{/* Transpiler Selector */}
|
|
145
|
+
<div className="mb-6">
|
|
146
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">Select Transpiler</label>
|
|
147
|
+
<div className="relative">
|
|
148
|
+
<select
|
|
149
|
+
value={selectedTranspiler?.name || ''}
|
|
150
|
+
onChange={(e) => {
|
|
151
|
+
const transpiler = transpilers.find((t) => t.name === e.target.value);
|
|
152
|
+
if (transpiler) setSelectedTranspiler(transpiler);
|
|
153
|
+
}}
|
|
154
|
+
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
155
|
+
>
|
|
156
|
+
{transpilers.map((transpiler) => (
|
|
157
|
+
<option key={transpiler.name} value={transpiler.name}>
|
|
158
|
+
{transpiler.title}
|
|
159
|
+
</option>
|
|
160
|
+
))}
|
|
161
|
+
</select>
|
|
162
|
+
<ChevronDownIcon className="absolute right-3 top-3 w-4 h-4 text-gray-400 pointer-events-none" />
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
{/* Code Display */}
|
|
167
|
+
<div className="bg-gray-50 rounded-lg border border-gray-200 overflow-hidden">
|
|
168
|
+
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
|
|
169
|
+
<h2 className="text-lg font-semibold text-gray-900">
|
|
170
|
+
{selectedTranspiler?.title || 'Generated Code'}
|
|
171
|
+
</h2>
|
|
172
|
+
{loading && <div className="text-sm text-gray-500">Generating...</div>}
|
|
173
|
+
</div>
|
|
174
|
+
<div className="p-4">
|
|
175
|
+
{error && (
|
|
176
|
+
<div className="text-red-600 text-sm mb-4 p-3 bg-red-50 rounded border border-red-200">
|
|
177
|
+
{error}
|
|
178
|
+
</div>
|
|
179
|
+
)}
|
|
180
|
+
{transpiledCode ? (
|
|
181
|
+
<div className="h-96 border border-gray-200 rounded">
|
|
182
|
+
<Editor
|
|
183
|
+
value={transpiledCode}
|
|
184
|
+
language={getLanguageFromTranspiler(selectedTranspiler?.name)}
|
|
185
|
+
options={{
|
|
186
|
+
readOnly: true,
|
|
187
|
+
minimap: { enabled: false },
|
|
188
|
+
fontSize: 14,
|
|
189
|
+
lineNumbers: 'on',
|
|
190
|
+
scrollBeyondLastLine: false,
|
|
191
|
+
automaticLayout: true,
|
|
192
|
+
wordWrap: 'on',
|
|
193
|
+
}}
|
|
194
|
+
loading={
|
|
195
|
+
<div className="flex items-center justify-center h-full text-gray-500">
|
|
196
|
+
Loading editor...
|
|
197
|
+
</div>
|
|
198
|
+
}
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
) : loading ? (
|
|
202
|
+
<div className="text-gray-500 text-center py-8">Generating code...</div>
|
|
203
|
+
) : (
|
|
204
|
+
<div className="text-gray-500 text-center py-8">
|
|
205
|
+
Select a transpiler to generate code
|
|
206
|
+
</div>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
@@ -15,6 +15,8 @@ export async function generateAgentMetadata({ params }: { params: Promise<{ agen
|
|
|
15
15
|
// Use the agent's icon-256.png as the favicon
|
|
16
16
|
const iconUrl = `/agents/${encodeURIComponent(agentName)}/images/icon-256.png`;
|
|
17
17
|
|
|
18
|
+
const canonicalUrl = `/agents/${encodeURIComponent(agentProfile.permanentId || agentName)}`;
|
|
19
|
+
|
|
18
20
|
const metadata = {
|
|
19
21
|
metadataBase: publicUrl,
|
|
20
22
|
title,
|
|
@@ -24,6 +26,9 @@ export async function generateAgentMetadata({ params }: { params: Promise<{ agen
|
|
|
24
26
|
shortcut: iconUrl,
|
|
25
27
|
apple: iconUrl,
|
|
26
28
|
},
|
|
29
|
+
alternates: {
|
|
30
|
+
canonical: canonicalUrl,
|
|
31
|
+
},
|
|
27
32
|
openGraph: {
|
|
28
33
|
title,
|
|
29
34
|
description,
|
|
@@ -5,8 +5,8 @@ import { revalidatePath } from 'next/cache';
|
|
|
5
5
|
|
|
6
6
|
export async function restoreAgentVersion(agentName: string, historyId: number) {
|
|
7
7
|
const collection = await $provideAgentCollectionForServer();
|
|
8
|
-
await collection.
|
|
9
|
-
|
|
8
|
+
await collection.restoreAgentFromHistory(historyId);
|
|
9
|
+
|
|
10
10
|
revalidatePath(`/agents/${agentName}`);
|
|
11
11
|
revalidatePath(`/agents/${agentName}/history`);
|
|
12
12
|
}
|
|
@@ -170,7 +170,7 @@ export default async function AgentIntegrationPage({ params }: { params: Promise
|
|
|
170
170
|
}
|
|
171
171
|
`);
|
|
172
172
|
|
|
173
|
-
const agentLinks = getAgentLinks(agentName);
|
|
173
|
+
const agentLinks = getAgentLinks(agentProfile.permanentId || agentName);
|
|
174
174
|
const chatLink = agentLinks.find((l) => l.title === 'Chat with Agent')!;
|
|
175
175
|
const websiteIntegrationLink = agentLinks.find((l) => l.title === 'Website Integration')!;
|
|
176
176
|
|
|
@@ -9,9 +9,9 @@ import { notFound } from 'next/navigation';
|
|
|
9
9
|
import { Color } from '../../../../../../../src/utils/color/Color';
|
|
10
10
|
import { withAlpha } from '../../../../../../../src/utils/color/operators/withAlpha';
|
|
11
11
|
import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
|
|
12
|
+
import { getAgentName, getAgentProfile } from '../_utils';
|
|
12
13
|
import { getAgentExternalLinks, getAgentLinks } from '../agentLinks';
|
|
13
14
|
import { CopyField } from '../CopyField';
|
|
14
|
-
import { getAgentName, getAgentProfile } from '../_utils';
|
|
15
15
|
import { generateAgentMetadata } from '../generateAgentMetadata';
|
|
16
16
|
|
|
17
17
|
export const generateMetadata = generateAgentMetadata;
|
|
@@ -129,7 +129,7 @@ export default async function AgentLinksPage({ params }: { params: Promise<{ age
|
|
|
129
129
|
Agent Resources
|
|
130
130
|
</h2>
|
|
131
131
|
<div className="grid md:grid-cols-2 gap-4">
|
|
132
|
-
{getAgentLinks(agentName)
|
|
132
|
+
{getAgentLinks(agentProfile.permanentId || agentName)
|
|
133
133
|
.filter((link) =>
|
|
134
134
|
['Chat with Agent', 'History & Feedback', 'Integration'].includes(link.title),
|
|
135
135
|
)
|
|
@@ -4,14 +4,16 @@ import { $provideServer } from '@/src/tools/$provideServer';
|
|
|
4
4
|
import { isUserAdmin } from '@/src/utils/isUserAdmin';
|
|
5
5
|
import { saturate } from '@promptbook-local/color';
|
|
6
6
|
import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
|
|
7
|
+
import { NotFoundError } from '@promptbook-local/core';
|
|
7
8
|
import { notFound } from 'next/navigation';
|
|
8
9
|
import { Color } from '../../../../../../src/utils/color/Color';
|
|
9
|
-
import { getAgentName, getAgentProfile } from './_utils';
|
|
10
|
+
import { getAgentName, getAgentProfile, isAgentDeleted } from './_utils';
|
|
10
11
|
import { getAgentLinks } from './agentLinks';
|
|
11
12
|
import { AgentProfileChat } from './AgentProfileChat';
|
|
12
13
|
import { AgentProfileWrapper } from './AgentProfileWrapper';
|
|
13
14
|
import { generateAgentMetadata } from './generateAgentMetadata';
|
|
14
15
|
import { ServiceWorkerRegister } from './ServiceWorkerRegister';
|
|
16
|
+
import { DeletedAgentBanner } from '../../../components/DeletedAgentBanner';
|
|
15
17
|
|
|
16
18
|
export const generateMetadata = generateAgentMetadata;
|
|
17
19
|
|
|
@@ -32,10 +34,11 @@ export default async function AgentPage({
|
|
|
32
34
|
agentProfile = await getAgentProfile(agentName);
|
|
33
35
|
} catch (error) {
|
|
34
36
|
if (
|
|
35
|
-
error instanceof
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
error.message.includes('
|
|
37
|
+
error instanceof NotFoundError ||
|
|
38
|
+
(error instanceof Error &&
|
|
39
|
+
// Note: This is a bit hacky, but valid way to check for specific error message
|
|
40
|
+
(error.message.includes('Cannot coerce the result to a single JSON object') ||
|
|
41
|
+
error.message.includes('JSON object requested, multiple (or no) results returned')))
|
|
39
42
|
) {
|
|
40
43
|
notFound();
|
|
41
44
|
}
|
|
@@ -54,6 +57,7 @@ export default async function AgentPage({
|
|
|
54
57
|
const brandColorHex = brandColor.then(saturate(-0.5)).toHex();
|
|
55
58
|
|
|
56
59
|
const fullname = (agentProfile.meta.fullname || agentProfile.agentName || 'Agent') as string;
|
|
60
|
+
const isDeleted = await isAgentDeleted(agentName);
|
|
57
61
|
|
|
58
62
|
return (
|
|
59
63
|
<>
|
|
@@ -68,7 +72,7 @@ export default async function AgentPage({
|
|
|
68
72
|
isHeadless={isHeadless}
|
|
69
73
|
actions={
|
|
70
74
|
<>
|
|
71
|
-
{getAgentLinks(agentName)
|
|
75
|
+
{getAgentLinks(agentProfile.permanentId || agentName)
|
|
72
76
|
.filter((link) => ['Edit Book', 'Integration', 'All Links'].includes(link.title))
|
|
73
77
|
.map((link) => (
|
|
74
78
|
<a
|
|
@@ -86,12 +90,14 @@ export default async function AgentPage({
|
|
|
86
90
|
</>
|
|
87
91
|
}
|
|
88
92
|
>
|
|
93
|
+
{isDeleted && <DeletedAgentBanner />}
|
|
89
94
|
<AgentProfileChat
|
|
90
95
|
agentUrl={agentUrl}
|
|
91
96
|
agentName={agentName}
|
|
92
97
|
fullname={fullname}
|
|
93
98
|
brandColorHex={brandColorHex}
|
|
94
99
|
avatarSrc={agentProfile.meta.image!}
|
|
100
|
+
isDeleted={isDeleted}
|
|
95
101
|
/>
|
|
96
102
|
</AgentProfileWrapper>
|
|
97
103
|
</>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
4
|
+
import { ArrowLeftIcon, FileTextIcon } from 'lucide-react';
|
|
5
|
+
import { headers } from 'next/headers';
|
|
6
|
+
import Link from 'next/link';
|
|
7
|
+
import { notFound } from 'next/navigation';
|
|
8
|
+
import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
|
|
9
|
+
import { getAgentName, getAgentProfile } from '../_utils';
|
|
10
|
+
import { generateAgentMetadata } from '../generateAgentMetadata';
|
|
11
|
+
|
|
12
|
+
export const generateMetadata = generateAgentMetadata;
|
|
13
|
+
|
|
14
|
+
export default async function AgentSystemMessagePage({ params }: { params: Promise<{ agentName: string }> }) {
|
|
15
|
+
$sideEffect(headers());
|
|
16
|
+
const agentName = await getAgentName(params);
|
|
17
|
+
|
|
18
|
+
let agentProfile;
|
|
19
|
+
let agentSource;
|
|
20
|
+
try {
|
|
21
|
+
agentProfile = await getAgentProfile(agentName);
|
|
22
|
+
const collection = await $provideAgentCollectionForServer();
|
|
23
|
+
agentSource = await collection.getAgentSource(agentName);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (
|
|
26
|
+
error instanceof Error &&
|
|
27
|
+
(error.message.includes('Cannot coerce the result to a single JSON object') ||
|
|
28
|
+
error.message.includes('JSON object requested, multiple (or no) results returned'))
|
|
29
|
+
) {
|
|
30
|
+
notFound();
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// For now, we'll display the agent source as the system message
|
|
36
|
+
// TODO: [🧠] This might need to be the actual generated system message from the Agent class
|
|
37
|
+
const systemMessage = agentSource || 'No system message available';
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="min-h-screen p-6 md:p-12 flex flex-col items-center bg-gray-50">
|
|
41
|
+
<div className="w-full max-w-4xl bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
42
|
+
{/* Header */}
|
|
43
|
+
<div className="p-6 border-b border-gray-200 flex items-center gap-4">
|
|
44
|
+
{agentProfile.meta.image && (
|
|
45
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
46
|
+
<img
|
|
47
|
+
src={agentProfile.meta.image as string}
|
|
48
|
+
alt={agentProfile.meta.fullname || agentName}
|
|
49
|
+
className="w-16 h-16 rounded-full object-cover border-2 border-gray-200"
|
|
50
|
+
/>
|
|
51
|
+
)}
|
|
52
|
+
<div className="flex-1">
|
|
53
|
+
<h1 className="text-2xl font-bold text-gray-900">{agentProfile.meta.fullname || agentName}</h1>
|
|
54
|
+
<p className="text-gray-500 flex items-center gap-2">
|
|
55
|
+
<FileTextIcon className="w-4 h-4" />
|
|
56
|
+
System Message
|
|
57
|
+
</p>
|
|
58
|
+
</div>
|
|
59
|
+
<Link
|
|
60
|
+
href={`/agents/${encodeURIComponent(agentName)}`}
|
|
61
|
+
className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors"
|
|
62
|
+
title="Back to Agent"
|
|
63
|
+
>
|
|
64
|
+
<ArrowLeftIcon className="w-6 h-6" />
|
|
65
|
+
</Link>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div className="p-6">
|
|
69
|
+
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
|
70
|
+
<h2 className="text-lg font-semibold text-gray-900 mb-3">Generated System Message</h2>
|
|
71
|
+
<pre className="text-sm text-gray-700 whitespace-pre-wrap font-mono bg-white p-4 rounded border border-gray-200 overflow-x-auto">
|
|
72
|
+
{systemMessage}
|
|
73
|
+
</pre>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div className="mt-6 bg-blue-50 rounded-lg p-4 border border-blue-200">
|
|
77
|
+
<h3 className="text-md font-semibold text-blue-900 mb-2">Model Requirements</h3>
|
|
78
|
+
<div className="text-sm text-blue-800">
|
|
79
|
+
<p><strong>Model Variant:</strong> CHAT</p>
|
|
80
|
+
{/* TODO: [🧠] Add more model requirements if available */}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getMetadata } from '../../../database/getMetadata';
|
|
2
|
+
import { NextResponse } from 'next/server';
|
|
3
|
+
|
|
4
|
+
export async function GET() {
|
|
5
|
+
try {
|
|
6
|
+
const adminEmail = await getMetadata('ADMIN_EMAIL');
|
|
7
|
+
return NextResponse.json({ adminEmail });
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.error('Failed to get admin email:', error);
|
|
10
|
+
return NextResponse.json({ adminEmail: 'support@ptbk.io' }, { status: 500 });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
// POST /api/agents/[agentName]/clone
|
|
2
1
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
3
|
-
import { AgentBasicInformation } from '../../../../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
4
|
-
import { string_book } from '../../../../../../../../src/book-2.0/agent-source/string_book';
|
|
5
2
|
import { TODO_any } from '@promptbook-local/types';
|
|
6
3
|
import { NextResponse } from 'next/server';
|
|
4
|
+
import { string_book } from '../../../../../../../../src/book-2.0/agent-source/string_book';
|
|
7
5
|
|
|
8
6
|
export async function POST(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
9
7
|
const { agentName } = await params;
|
|
@@ -21,14 +19,14 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
|
|
|
21
19
|
while (true) {
|
|
22
20
|
try {
|
|
23
21
|
await collection.getAgentSource(newAgentName);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
// If success, it means it exists, so we try next one
|
|
23
|
+
counter++;
|
|
24
|
+
newAgentName = `${agentName} (Copy ${counter})`;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
// If error, it likely means it does not exist (NotFoundError), so we can use it
|
|
27
|
+
// TODO: [🧠] Check if it is really NotFoundError
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
const lines = source.split('\n');
|
|
@@ -36,7 +34,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
|
|
|
36
34
|
const newSource = lines.join('\n') as string_book;
|
|
37
35
|
|
|
38
36
|
const newAgent = await collection.createAgent(newSource);
|
|
39
|
-
|
|
37
|
+
|
|
40
38
|
return NextResponse.json(newAgent);
|
|
41
39
|
} catch (error) {
|
|
42
40
|
return NextResponse.json(
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// POST /api/agents/[agentName]/restore - restore deleted agent
|
|
2
|
+
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
3
|
+
import { TODO_any } from '@promptbook-local/types';
|
|
4
|
+
import { NextResponse } from 'next/server';
|
|
5
|
+
|
|
6
|
+
export async function POST(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
7
|
+
const { agentName } = await params;
|
|
8
|
+
const collection = await $provideAgentCollectionForServer();
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
await collection.restoreAgent(agentName);
|
|
12
|
+
return NextResponse.json({ success: true });
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{ success: false, error: (error as TODO_any)?.message || 'Failed to restore agent' },
|
|
16
|
+
{ status: 500 },
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,8 +1,49 @@
|
|
|
1
1
|
// DELETE /api/agents/[agentName]
|
|
2
|
+
// PATCH /api/agents/[agentName] - update agent visibility
|
|
3
|
+
// POST /api/agents/[agentName]/restore - restore deleted agent
|
|
4
|
+
import { $getTableName } from '@/src/database/$getTableName';
|
|
5
|
+
import { $provideSupabaseForServer } from '@/src/database/$provideSupabaseForServer';
|
|
2
6
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
3
7
|
import { TODO_any } from '@promptbook-local/types';
|
|
4
8
|
import { NextResponse } from 'next/server';
|
|
5
9
|
|
|
10
|
+
export async function PATCH(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
11
|
+
const { agentName } = await params;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const body = await request.json();
|
|
15
|
+
const { visibility }: { visibility: 'PUBLIC' | 'PRIVATE' } = body;
|
|
16
|
+
|
|
17
|
+
if (!visibility || !['PUBLIC', 'PRIVATE'].includes(visibility)) {
|
|
18
|
+
return NextResponse.json(
|
|
19
|
+
{ success: false, error: 'Invalid visibility value. Must be PUBLIC or PRIVATE.' },
|
|
20
|
+
{ status: 400 },
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const supabase = $provideSupabaseForServer();
|
|
25
|
+
// const { tablePrefix } = await $provideServer();
|
|
26
|
+
|
|
27
|
+
const updateResult = await supabase
|
|
28
|
+
.from(await $getTableName(`Agent`))
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
.update({ visibility } as any)
|
|
31
|
+
.or(`agentName.eq.${agentName},permanentId.eq.${agentName}`)
|
|
32
|
+
.is('deletedAt', null);
|
|
33
|
+
|
|
34
|
+
if (updateResult.error) {
|
|
35
|
+
return NextResponse.json({ success: false, error: updateResult.error.message }, { status: 500 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return NextResponse.json({ success: true });
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return NextResponse.json(
|
|
41
|
+
{ success: false, error: (error as TODO_any)?.message || 'Failed to update agent visibility' },
|
|
42
|
+
{ status: 500 },
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
6
47
|
export async function DELETE(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
7
48
|
const { agentName } = await params;
|
|
8
49
|
const collection = await $provideAgentCollectionForServer();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { $getTableName } from '@/src/database/$getTableName';
|
|
1
2
|
import { $provideServer } from '@/src/tools/$provideServer';
|
|
2
3
|
import { NextResponse } from 'next/server';
|
|
4
|
+
import { $provideSupabaseForServer } from '../../../database/$provideSupabaseForServer';
|
|
3
5
|
import { $provideAgentCollectionForServer } from '../../../tools/$provideAgentCollectionForServer';
|
|
4
6
|
import { getFederatedServersFromMetadata } from '../../../utils/getFederatedServersFromMetadata';
|
|
5
7
|
|
|
@@ -8,13 +10,36 @@ export const dynamic = 'force-dynamic';
|
|
|
8
10
|
export async function GET() {
|
|
9
11
|
try {
|
|
10
12
|
const collection = await $provideAgentCollectionForServer();
|
|
11
|
-
const
|
|
13
|
+
const allAgents = await collection.listAgents();
|
|
12
14
|
const federatedServers = await getFederatedServersFromMetadata();
|
|
13
15
|
const { publicUrl } = await $provideServer();
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
// Filter to only include PUBLIC agents for federated API
|
|
18
|
+
const supabase = $provideSupabaseForServer();
|
|
19
|
+
const visibilityResult = await supabase
|
|
20
|
+
.from(await $getTableName(`Agent`))
|
|
21
|
+
.select('agentName, visibility')
|
|
22
|
+
.is('deletedAt', null);
|
|
23
|
+
|
|
24
|
+
let publicAgents = allAgents;
|
|
25
|
+
if (!visibilityResult.error) {
|
|
26
|
+
const visibilityMap = new Map(
|
|
27
|
+
visibilityResult.data.map((item: { agentName: string; visibility: 'PUBLIC' | 'PRIVATE' }) => [
|
|
28
|
+
item.agentName,
|
|
29
|
+
item.visibility,
|
|
30
|
+
]),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Only include PUBLIC agents in federated API
|
|
34
|
+
publicAgents = allAgents.filter((agent) => {
|
|
35
|
+
const visibility = visibilityMap.get(agent.agentName);
|
|
36
|
+
return visibility === 'PUBLIC';
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const agentsWithUrl = publicAgents.map((agent) => ({
|
|
16
41
|
...agent,
|
|
17
|
-
url: `${publicUrl.href}agents/${encodeURIComponent(agent.agentName)}`,
|
|
42
|
+
url: `${publicUrl.href}agents/${encodeURIComponent(agent.permanentId || agent.agentName)}`,
|
|
18
43
|
}));
|
|
19
44
|
|
|
20
45
|
const response = NextResponse.json({
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
3
|
+
import { keepUnused } from '../../../../../../src/utils/organization/keepUnused';
|
|
1
4
|
import { $getTableName } from '../../../database/$getTableName';
|
|
2
5
|
import { $provideSupabase } from '../../../database/$provideSupabase';
|
|
3
6
|
import { isUserAdmin } from '../../../utils/isUserAdmin';
|
|
4
|
-
import { randomUUID } from 'crypto';
|
|
5
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
6
7
|
|
|
7
8
|
export async function GET(request: NextRequest) {
|
|
9
|
+
keepUnused(request);
|
|
10
|
+
|
|
8
11
|
if (!(await isUserAdmin())) {
|
|
9
12
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
10
13
|
}
|
|
@@ -35,11 +38,7 @@ export async function POST(request: NextRequest) {
|
|
|
35
38
|
const supabase = $provideSupabase();
|
|
36
39
|
const table = await $getTableName('ApiTokens');
|
|
37
40
|
|
|
38
|
-
const { data, error } = await supabase
|
|
39
|
-
.from(table)
|
|
40
|
-
.insert({ token, note })
|
|
41
|
-
.select()
|
|
42
|
-
.single();
|
|
41
|
+
const { data, error } = await supabase.from(table).insert({ token, note }).select().single();
|
|
43
42
|
|
|
44
43
|
if (error) {
|
|
45
44
|
return NextResponse.json({ error: error.message }, { status: 500 });
|