@promptbook/cli 0.104.0-2 → 0.104.0-4
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 +3 -4
- package/apps/agents-server/public/swagger.json +115 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +11 -7
- package/apps/agents-server/src/app/AddAgentButton.tsx +1 -2
- 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 +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +1 -3
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +29 -16
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +4 -1
- package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +8 -6
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +33 -30
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +10 -12
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +1 -2
- package/apps/agents-server/src/app/api/agents/route.ts +1 -1
- 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 +3 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
- package/apps/agents-server/src/app/api/upload/route.ts +9 -0
- package/apps/agents-server/src/app/page.tsx +1 -1
- package/apps/agents-server/src/app/swagger/page.tsx +14 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +4 -2
- package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
- package/apps/agents-server/src/components/Header/Header.tsx +27 -5
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +22 -3
- package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
- package/apps/agents-server/src/database/migrate.ts +34 -1
- package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
- package/apps/agents-server/src/generated/reservedPaths.ts +6 -1
- package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
- package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +43 -0
- package/apps/agents-server/src/message-providers/index.ts +13 -0
- package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
- package/apps/agents-server/src/middleware.ts +12 -3
- package/apps/agents-server/src/utils/auth.ts +117 -17
- package/apps/agents-server/src/utils/getUserIdFromRequest.ts +3 -1
- package/apps/agents-server/src/utils/handleChatCompletion.ts +9 -5
- package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +6 -2
- package/apps/agents-server/src/utils/validateApiKey.ts +5 -10
- package/esm/index.es.js +86 -9
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/types.index.d.ts +2 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
- package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
- package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
- package/esm/typings/src/types/Message.d.ts +49 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +86 -9
- package/umd/index.umd.js.map +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import Editor from '@monaco-editor/react';
|
|
4
4
|
import { ArrowLeftIcon, ChevronDownIcon, CodeIcon } from 'lucide-react';
|
|
5
5
|
import Link from 'next/link';
|
|
6
|
-
import { useEffect, useState } from 'react';
|
|
6
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
type Transpiler = {
|
|
9
9
|
name: string;
|
|
@@ -61,39 +61,42 @@ export default function AgentCodePage({ params }: { params: Promise<{ agentName:
|
|
|
61
61
|
.catch((err) => console.error('Error fetching transpilers:', err));
|
|
62
62
|
}, [agentName]);
|
|
63
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
|
+
|
|
64
95
|
useEffect(() => {
|
|
65
96
|
if (selectedTranspiler && agentName) {
|
|
66
97
|
transpileCode(selectedTranspiler.name);
|
|
67
98
|
}
|
|
68
|
-
}, [selectedTranspiler, agentName]);
|
|
69
|
-
|
|
70
|
-
const transpileCode = async (transpilerName: string) => {
|
|
71
|
-
setLoading(true);
|
|
72
|
-
setError('');
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const response = await fetch(`/agents/${encodeURIComponent(agentName)}/code/api`, {
|
|
76
|
-
method: 'POST',
|
|
77
|
-
headers: {
|
|
78
|
-
'Content-Type': 'application/json',
|
|
79
|
-
},
|
|
80
|
-
body: JSON.stringify({ transpilerName }),
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
if (!response.ok) {
|
|
84
|
-
const errorData = await response.json();
|
|
85
|
-
throw new Error(errorData.error || 'Failed to transpile code');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const result: TranspilationResult = await response.json();
|
|
89
|
-
setTranspiledCode(result.code);
|
|
90
|
-
} catch (err) {
|
|
91
|
-
setError(err instanceof Error ? err.message : 'Failed to transpile code');
|
|
92
|
-
setTranspiledCode('');
|
|
93
|
-
} finally {
|
|
94
|
-
setLoading(false);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
99
|
+
}, [selectedTranspiler, agentName, transpileCode]);
|
|
97
100
|
|
|
98
101
|
if (!agentProfile) {
|
|
99
102
|
return (
|
|
@@ -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(
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { $getTableName } from '@/src/database/$getTableName';
|
|
5
5
|
import { $provideSupabaseForServer } from '@/src/database/$provideSupabaseForServer';
|
|
6
6
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
7
|
-
import { $provideServer } from '@/src/tools/$provideServer';
|
|
8
7
|
import { TODO_any } from '@promptbook-local/types';
|
|
9
8
|
import { NextResponse } from 'next/server';
|
|
10
9
|
|
|
@@ -23,7 +22,7 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ ag
|
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
const supabase = $provideSupabaseForServer();
|
|
26
|
-
const { tablePrefix } = await $provideServer();
|
|
25
|
+
// const { tablePrefix } = await $provideServer();
|
|
27
26
|
|
|
28
27
|
const updateResult = await supabase
|
|
29
28
|
.from(await $getTableName(`Agent`))
|
|
@@ -12,7 +12,7 @@ export async function GET() {
|
|
|
12
12
|
const collection = await $provideAgentCollectionForServer();
|
|
13
13
|
const allAgents = await collection.listAgents();
|
|
14
14
|
const federatedServers = await getFederatedServersFromMetadata();
|
|
15
|
-
const { publicUrl
|
|
15
|
+
const { publicUrl } = await $provideServer();
|
|
16
16
|
|
|
17
17
|
// Filter to only include PUBLIC agents for federated API
|
|
18
18
|
const supabase = $provideSupabaseForServer();
|
|
@@ -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 });
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import spaceTrim from 'spacetrim';
|
|
3
3
|
import { getGroupedCommitmentDefinitions } from '../../../../../../../src/commitments';
|
|
4
|
+
import { keepUnused } from '../../../../../../../src/utils/organization/keepUnused';
|
|
4
5
|
|
|
5
6
|
export const dynamic = 'force-static';
|
|
6
7
|
|
|
7
8
|
export async function GET(request: NextRequest) {
|
|
9
|
+
keepUnused(request);
|
|
10
|
+
|
|
8
11
|
const groupedCommitments = getGroupedCommitmentDefinitions();
|
|
9
12
|
|
|
10
13
|
const content = spaceTrim(
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { keepUnused } from '../../../../../../src/utils/organization/keepUnused';
|
|
1
3
|
import { $getTableName } from '../../../database/$getTableName';
|
|
2
4
|
import { $provideSupabase } from '../../../database/$provideSupabase';
|
|
3
5
|
import { isUserAdmin } from '../../../utils/isUserAdmin';
|
|
4
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
5
6
|
|
|
6
7
|
export async function GET(request: NextRequest) {
|
|
8
|
+
keepUnused(request);
|
|
9
|
+
|
|
7
10
|
if (!(await isUserAdmin())) {
|
|
8
11
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
9
12
|
}
|
|
@@ -36,11 +39,7 @@ export async function POST(request: NextRequest) {
|
|
|
36
39
|
const supabase = $provideSupabase();
|
|
37
40
|
const table = await $getTableName('Metadata');
|
|
38
41
|
|
|
39
|
-
const { data, error } = await supabase
|
|
40
|
-
.from(table)
|
|
41
|
-
.insert({ key, value, note })
|
|
42
|
-
.select()
|
|
43
|
-
.single();
|
|
42
|
+
const { data, error } = await supabase.from(table).insert({ key, value, note }).select().single();
|
|
44
43
|
|
|
45
44
|
if (error) {
|
|
46
45
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
@@ -155,3 +155,12 @@ export async function POST(request: NextRequest) {
|
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* TODO: !!!! Change uploaded URLs from `storageUrl` to `shortUrl`
|
|
161
|
+
* TODO: !!!! Record both `storageUrl` (actual storage location) and `shortUrl` in `File` table
|
|
162
|
+
* TODO: !!!! Record `purpose` in `File` table
|
|
163
|
+
* TODO: !!!! Record `userId` in `File` table
|
|
164
|
+
* TODO: !!!! Record all things into `File` table
|
|
165
|
+
* TODO: !!!! File type (mime type) of `.book` files should be `application/book` <- [🧠] !!!! Best mime type?!
|
|
166
|
+
*/
|
|
@@ -42,7 +42,7 @@ export default async function HomePage() {
|
|
|
42
42
|
|
|
43
43
|
// Filter agents based on visibility and user authentication
|
|
44
44
|
const supabase = $provideSupabaseForServer();
|
|
45
|
-
const { tablePrefix } = await $provideServer();
|
|
45
|
+
// const { tablePrefix } = await $provideServer();
|
|
46
46
|
|
|
47
47
|
// Get visibility for all agents
|
|
48
48
|
const visibilityResult = await supabase
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import dynamic from 'next/dynamic';
|
|
4
|
+
import 'swagger-ui-react/swagger-ui.css';
|
|
5
|
+
|
|
6
|
+
const SwaggerUI = dynamic(() => import('swagger-ui-react'), { ssr: false });
|
|
7
|
+
|
|
8
|
+
export default function SwaggerPage() {
|
|
9
|
+
return (
|
|
10
|
+
<div className="swagger-container bg-white min-h-screen">
|
|
11
|
+
<SwaggerUI url="/swagger.json" />
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -78,7 +78,7 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
78
78
|
|
|
79
79
|
if (fontString) {
|
|
80
80
|
// [🧠] TODO: Properly parse font string to get family name
|
|
81
|
-
const primaryFont = fontString.split(',')[0].trim().replace(/['"]/g, '');
|
|
81
|
+
// const primaryFont = fontString.split(',')[0].trim().replace(/['"]/g, '');
|
|
82
82
|
fontStyle = {
|
|
83
83
|
fontFamily: fontString,
|
|
84
84
|
};
|
|
@@ -139,8 +139,10 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
139
139
|
backfaceVisibility: 'hidden',
|
|
140
140
|
backgroundColor: brandColorDarkHex,
|
|
141
141
|
boxShadow: `0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px ${brandColorLightHex}40`,
|
|
142
|
+
|
|
143
|
+
// Note: Make it squircle
|
|
142
144
|
// borderRadius: '50%',
|
|
143
|
-
// ['cornerShape' as really_any]: 'squircle ',
|
|
145
|
+
// ['cornerShape' as really_any /* <- Note: `cornerShape` is non standard CSS property */]: 'squircle ',
|
|
144
146
|
}}
|
|
145
147
|
>
|
|
146
148
|
{imageUrl ? (
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
-
import { useRouter } from 'next/navigation';
|
|
5
4
|
|
|
6
5
|
type AuthControlsProps = {
|
|
7
6
|
initialUser: { username: string; isAdmin: boolean } | null;
|
|
8
7
|
};
|
|
9
8
|
|
|
10
9
|
export function AuthControls({ initialUser }: AuthControlsProps) {
|
|
11
|
-
const router = useRouter();
|
|
12
|
-
const [user
|
|
10
|
+
// const router = useRouter();
|
|
11
|
+
const [user] = useState(initialUser);
|
|
13
12
|
const [isLoginOpen, setIsLoginOpen] = useState(false);
|
|
14
13
|
const [username, setUsername] = useState('');
|
|
15
14
|
const [password, setPassword] = useState('');
|
|
@@ -52,7 +51,9 @@ export function AuthControls({ initialUser }: AuthControlsProps) {
|
|
|
52
51
|
<div className="flex items-center space-x-4">
|
|
53
52
|
<span className="text-gray-600">
|
|
54
53
|
Logged in as <strong>{user.username}</strong>
|
|
55
|
-
{user.isAdmin &&
|
|
54
|
+
{user.isAdmin && (
|
|
55
|
+
<span className="ml-2 bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">Admin</span>
|
|
56
|
+
)}
|
|
56
57
|
</span>
|
|
57
58
|
<button
|
|
58
59
|
onClick={handleLogout}
|
|
@@ -4,7 +4,6 @@ import promptbookLogoBlueTransparent from '@/public/logo-blue-white-256.png';
|
|
|
4
4
|
import { $createAgentAction, logoutAction } from '@/src/app/actions';
|
|
5
5
|
import { ArrowRight, ChevronDown, Lock, LogIn, LogOut, User } from 'lucide-react';
|
|
6
6
|
import Image from 'next/image';
|
|
7
|
-
import { HeadlessLink, useIsHeadless, pushWithHeadless } from '../_utils/headlessParam';
|
|
8
7
|
import { useRouter } from 'next/navigation';
|
|
9
8
|
import { ReactNode, useState } from 'react';
|
|
10
9
|
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
@@ -13,6 +12,7 @@ import { useMenuHoisting } from '../../../../../src/book-components/_common/Menu
|
|
|
13
12
|
import { just } from '../../../../../src/utils/organization/just';
|
|
14
13
|
import type { UserInfo } from '../../utils/getCurrentUser';
|
|
15
14
|
import { getVisibleCommitmentDefinitions } from '../../utils/getVisibleCommitmentDefinitions';
|
|
15
|
+
import { HeadlessLink, pushWithHeadless, useIsHeadless } from '../_utils/headlessParam';
|
|
16
16
|
import { ChangePasswordDialog } from '../ChangePasswordDialog/ChangePasswordDialog';
|
|
17
17
|
import { LoginDialog } from '../LoginDialog/LoginDialog';
|
|
18
18
|
import { useUsersAdmin } from '../UsersList/useUsersAdmin';
|
|
@@ -119,16 +119,22 @@ export function Header(props: HeaderProps) {
|
|
|
119
119
|
|
|
120
120
|
// Federated servers dropdown items (respect logo, only current is not clickable)
|
|
121
121
|
const [isFederatedOpen, setIsFederatedOpen] = useState(false);
|
|
122
|
-
const [isMobileFederatedOpen, setIsMobileFederatedOpen] = useState(false);
|
|
122
|
+
// const [isMobileFederatedOpen, setIsMobileFederatedOpen] = useState(false);
|
|
123
123
|
|
|
124
|
-
const federatedDropdownItems: SubMenuItem[] = federatedServers.map(server => {
|
|
124
|
+
const federatedDropdownItems: SubMenuItem[] = federatedServers.map((server) => {
|
|
125
125
|
const isCurrent = server.url === (typeof window !== 'undefined' ? window.location.origin : '');
|
|
126
126
|
return isCurrent
|
|
127
127
|
? {
|
|
128
128
|
label: (
|
|
129
129
|
<span className="flex items-center gap-2">
|
|
130
130
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
131
|
-
<img
|
|
131
|
+
<img
|
|
132
|
+
src={server.logoUrl || serverLogoUrl || promptbookLogoBlueTransparent.src}
|
|
133
|
+
alt={server.title}
|
|
134
|
+
width={20}
|
|
135
|
+
height={20}
|
|
136
|
+
className="w-5 h-5 object-contain rounded-full"
|
|
137
|
+
/>
|
|
132
138
|
<span className="font-semibold">{server.title.replace(/^Federated: /, '')}</span>
|
|
133
139
|
<span className="ml-1 text-xs text-blue-600">(current)</span>
|
|
134
140
|
</span>
|
|
@@ -140,7 +146,13 @@ export function Header(props: HeaderProps) {
|
|
|
140
146
|
label: (
|
|
141
147
|
<span className="flex items-center gap-2">
|
|
142
148
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
143
|
-
<img
|
|
149
|
+
<img
|
|
150
|
+
src={server.logoUrl || promptbookLogoBlueTransparent.src}
|
|
151
|
+
alt={server.title}
|
|
152
|
+
width={20}
|
|
153
|
+
height={20}
|
|
154
|
+
className="w-5 h-5 object-contain rounded-full"
|
|
155
|
+
/>
|
|
144
156
|
<span>{server.title.replace(/^Federated: /, '')}</span>
|
|
145
157
|
</span>
|
|
146
158
|
),
|
|
@@ -169,6 +181,12 @@ export function Header(props: HeaderProps) {
|
|
|
169
181
|
isBold: true,
|
|
170
182
|
isBordered: true,
|
|
171
183
|
} as SubMenuItem,
|
|
184
|
+
{
|
|
185
|
+
label: 'API Reference',
|
|
186
|
+
href: '/swagger',
|
|
187
|
+
isBold: true,
|
|
188
|
+
isBordered: true,
|
|
189
|
+
} as SubMenuItem,
|
|
172
190
|
...getVisibleCommitmentDefinitions().map(
|
|
173
191
|
({ primary, aliases }) =>
|
|
174
192
|
({
|
|
@@ -263,6 +281,10 @@ export function Header(props: HeaderProps) {
|
|
|
263
281
|
isMobileOpen: isMobileSystemOpen,
|
|
264
282
|
setIsMobileOpen: setIsMobileSystemOpen,
|
|
265
283
|
items: [
|
|
284
|
+
{
|
|
285
|
+
label: 'OpenAPI Documentation',
|
|
286
|
+
href: '/swagger',
|
|
287
|
+
},
|
|
266
288
|
{
|
|
267
289
|
label: 'API Tokens',
|
|
268
290
|
href: '/admin/api-tokens',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { really_any } from '@promptbook-local/types';
|
|
3
4
|
import { EyeIcon, EyeOffIcon, RotateCcwIcon } from 'lucide-react';
|
|
4
5
|
import Link from 'next/link';
|
|
5
6
|
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
@@ -19,7 +20,16 @@ type AgentCardProps = {
|
|
|
19
20
|
const ACTION_BUTTON_CLASSES =
|
|
20
21
|
'text-white px-3 py-1 rounded shadow text-xs font-medium transition-colors uppercase tracking-wider opacity-80 hover:opacity-100';
|
|
21
22
|
|
|
22
|
-
export function AgentCard({
|
|
23
|
+
export function AgentCard({
|
|
24
|
+
agent,
|
|
25
|
+
href,
|
|
26
|
+
isAdmin,
|
|
27
|
+
onDelete,
|
|
28
|
+
onClone,
|
|
29
|
+
onToggleVisibility,
|
|
30
|
+
onRestore,
|
|
31
|
+
visibility,
|
|
32
|
+
}: AgentCardProps) {
|
|
23
33
|
const { meta, agentName } = agent;
|
|
24
34
|
const fullname = (meta.fullname as string) || agentName || 'Agent';
|
|
25
35
|
const imageUrl = (meta.image as string) || null;
|
|
@@ -41,9 +51,14 @@ export function AgentCard({ agent, href, isAdmin, onDelete, onClone, onToggleVis
|
|
|
41
51
|
<div className="p-6 flex flex-col items-center flex-grow backdrop-blur-[2px]">
|
|
42
52
|
{/* Image container */}
|
|
43
53
|
<div
|
|
44
|
-
className="w-32 h-32 mb-4
|
|
54
|
+
className="w-32 h-32 mb-4 shadow-lg overflow-hidden flex-shrink-0 bg-black/20"
|
|
45
55
|
style={{
|
|
46
56
|
boxShadow: `0 10px 20px -5px rgba(0, 0, 0, 0.2), 0 0 0 1px ${brandColorLightHex}40`,
|
|
57
|
+
|
|
58
|
+
// Note: Make it squircle
|
|
59
|
+
borderRadius: '50%',
|
|
60
|
+
['cornerShape' as really_any /* <- Note: `cornerShape` is non standard CSS property */]:
|
|
61
|
+
'squircle ',
|
|
47
62
|
}}
|
|
48
63
|
>
|
|
49
64
|
{imageUrl ? (
|
|
@@ -89,7 +104,11 @@ export function AgentCard({ agent, href, isAdmin, onDelete, onClone, onToggleVis
|
|
|
89
104
|
{isAdmin && !onRestore && (
|
|
90
105
|
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
|
91
106
|
<button
|
|
92
|
-
className={`${
|
|
107
|
+
className={`${
|
|
108
|
+
visibility === 'PUBLIC'
|
|
109
|
+
? 'bg-green-500 hover:bg-green-600'
|
|
110
|
+
: 'bg-gray-500 hover:bg-gray-600'
|
|
111
|
+
} ${ACTION_BUTTON_CLASSES}`}
|
|
93
112
|
onClick={(e) => {
|
|
94
113
|
e.preventDefault();
|
|
95
114
|
onToggleVisibility?.(agent.permanentId || agent.agentName);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Utility to append ?headless param if present in current URL
|
|
2
|
-
import { usePathname, useSearchParams } from 'next/navigation';
|
|
3
2
|
import Link, { LinkProps } from 'next/link';
|
|
3
|
+
import { useSearchParams } from 'next/navigation';
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
5
|
|
|
6
6
|
// Returns true if ?headless is present in current search params
|
|
@@ -18,7 +18,11 @@ export function appendHeadlessParam(href: string, isHeadless: boolean): string {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// Custom Link that preserves headless param
|
|
21
|
-
export function HeadlessLink({
|
|
21
|
+
export function HeadlessLink({
|
|
22
|
+
href,
|
|
23
|
+
children,
|
|
24
|
+
...rest
|
|
25
|
+
}: LinkProps & { children: React.ReactNode } & React.AnchorHTMLAttributes<HTMLAnchorElement>) {
|
|
22
26
|
const isHeadless = useIsHeadless();
|
|
23
27
|
const finalHref = useMemo(() => appendHeadlessParam(String(href), isHeadless), [href, isHeadless]);
|
|
24
28
|
return (
|
|
@@ -28,7 +32,7 @@ export function HeadlessLink({ href, children, ...rest }: LinkProps & { children
|
|
|
28
32
|
);
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
import { useRouter } from
|
|
35
|
+
import { useRouter } from 'next/navigation';
|
|
32
36
|
|
|
33
37
|
// Helper for router.push
|
|
34
38
|
export function pushWithHeadless(router: ReturnType<typeof useRouter>, href: string, isHeadless: boolean) {
|
|
@@ -8,13 +8,34 @@ dotenv.config();
|
|
|
8
8
|
async function migrate() {
|
|
9
9
|
console.info('🚀 Starting database migration');
|
|
10
10
|
|
|
11
|
+
// Parse CLI arguments for --only flag
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
let onlyPrefixes: string[] | null = null;
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < args.length; i++) {
|
|
16
|
+
if (args[i] === '--only' && args[i + 1]) {
|
|
17
|
+
onlyPrefixes = args[i + 1]
|
|
18
|
+
.split(',')
|
|
19
|
+
.map((p) => p.trim())
|
|
20
|
+
.filter((p) => p !== '');
|
|
21
|
+
break;
|
|
22
|
+
} else if (args[i]?.startsWith('--only=')) {
|
|
23
|
+
onlyPrefixes = args[i]
|
|
24
|
+
.substring('--only='.length)
|
|
25
|
+
.split(',')
|
|
26
|
+
.map((p) => p.trim())
|
|
27
|
+
.filter((p) => p !== '');
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
// 1. Get configuration
|
|
12
33
|
const prefixesEnv = process.env.SUPABASE_MIGRATION_PREFIXES;
|
|
13
34
|
if (!prefixesEnv) {
|
|
14
35
|
console.warn('⚠️ SUPABASE_MIGRATION_PREFIXES is not defined. Skipping migration.');
|
|
15
36
|
return;
|
|
16
37
|
}
|
|
17
|
-
|
|
38
|
+
let prefixes = prefixesEnv
|
|
18
39
|
.split(',')
|
|
19
40
|
.map((p) => p.trim())
|
|
20
41
|
.filter((p) => p !== '');
|
|
@@ -24,6 +45,18 @@ async function migrate() {
|
|
|
24
45
|
return;
|
|
25
46
|
}
|
|
26
47
|
|
|
48
|
+
// Filter prefixes if --only flag is provided
|
|
49
|
+
if (onlyPrefixes !== null) {
|
|
50
|
+
const invalidPrefixes = onlyPrefixes.filter((p) => !prefixes.includes(p));
|
|
51
|
+
if (invalidPrefixes.length > 0) {
|
|
52
|
+
console.error(`❌ Invalid prefixes specified in --only: ${invalidPrefixes.join(', ')}`);
|
|
53
|
+
console.error(` Available prefixes: ${prefixes.join(', ')}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
prefixes = onlyPrefixes;
|
|
57
|
+
console.info(`🎯 Running migrations only for: ${prefixes.join(', ')}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
27
60
|
const connectionString = process.env.POSTGRES_URL || process.env.DATABASE_URL;
|
|
28
61
|
if (!connectionString) {
|
|
29
62
|
console.error('❌ POSTGRES_URL or DATABASE_URL is not defined.');
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
-- Table: Message
|
|
3
|
+
CREATE TABLE IF NOT EXISTS "prefix_Message" (
|
|
4
|
+
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
5
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
6
|
+
"channel" TEXT NOT NULL,
|
|
7
|
+
"direction" TEXT NOT NULL,
|
|
8
|
+
"sender" JSONB NOT NULL,
|
|
9
|
+
"recipients" JSONB,
|
|
10
|
+
"content" TEXT NOT NULL,
|
|
11
|
+
"threadId" TEXT,
|
|
12
|
+
"metadata" JSONB
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
COMMENT ON TABLE "prefix_Message" IS 'A generic message structure for various communication channels';
|
|
16
|
+
COMMENT ON COLUMN "prefix_Message"."channel" IS 'The communication channel of the message (e.g. EMAIL, PROMPTBOOK_CHAT)';
|
|
17
|
+
COMMENT ON COLUMN "prefix_Message"."direction" IS 'Is the message send from the Promptbook or to the Promptbook';
|
|
18
|
+
COMMENT ON COLUMN "prefix_Message"."sender" IS 'Who sent the message';
|
|
19
|
+
COMMENT ON COLUMN "prefix_Message"."recipients" IS 'Who are the recipients of the message';
|
|
20
|
+
COMMENT ON COLUMN "prefix_Message"."content" IS 'The content of the message as markdown';
|
|
21
|
+
COMMENT ON COLUMN "prefix_Message"."threadId" IS 'The thread identifier the message belongs to';
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
-- Table: MessageSendAttempt
|
|
25
|
+
CREATE TABLE IF NOT EXISTS "prefix_MessageSendAttempt" (
|
|
26
|
+
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
27
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
28
|
+
|
|
29
|
+
"messageId" BIGINT NOT NULL REFERENCES "prefix_Message"("id") ON DELETE CASCADE,
|
|
30
|
+
"providerName" TEXT NOT NULL,
|
|
31
|
+
"isSuccessful" BOOLEAN NOT NULL,
|
|
32
|
+
"raw" JSONB
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
COMMENT ON TABLE "prefix_MessageSendAttempt" IS 'Stores each attempt to send the message';
|
|
36
|
+
COMMENT ON COLUMN "prefix_MessageSendAttempt"."messageId" IS 'The message that was attempted to be sent';
|
|
37
|
+
COMMENT ON COLUMN "prefix_MessageSendAttempt"."providerName" IS 'The name of the provider used for sending';
|
|
38
|
+
COMMENT ON COLUMN "prefix_MessageSendAttempt"."isSuccessful" IS 'Whether the attempt was successful';
|
|
39
|
+
COMMENT ON COLUMN "prefix_MessageSendAttempt"."raw" IS 'Raw response or error from the provider';
|
|
40
|
+
|
|
41
|
+
ALTER TABLE "prefix_Message" ENABLE ROW LEVEL SECURITY;
|
|
42
|
+
ALTER TABLE "prefix_MessageSendAttempt" ENABLE ROW LEVEL SECURITY;
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
|
|
6
6
|
*
|
|
7
|
-
* @see /apps/agents-server/src/app - source directory
|
|
7
|
+
* @see /apps/agents-server/src/app - source directory for routes
|
|
8
|
+
* @see /apps/agents-server/public - source directory for static files
|
|
8
9
|
* @see /apps/agents-server/src/middleware.ts - where this is used
|
|
9
10
|
*/
|
|
10
11
|
export const RESERVED_PATHS: readonly string[] = [
|
|
@@ -15,7 +16,9 @@ export const RESERVED_PATHS: readonly string[] = [
|
|
|
15
16
|
"docs",
|
|
16
17
|
"embed",
|
|
17
18
|
"favicon.ico",
|
|
19
|
+
"fonts",
|
|
18
20
|
"humans.txt",
|
|
21
|
+
"logo-blue-white-256.png",
|
|
19
22
|
"manifest.webmanifest",
|
|
20
23
|
"recycle-bin",
|
|
21
24
|
"restricted",
|
|
@@ -23,5 +26,7 @@ export const RESERVED_PATHS: readonly string[] = [
|
|
|
23
26
|
"security.txt",
|
|
24
27
|
"sitemap.xml",
|
|
25
28
|
"sw.js",
|
|
29
|
+
"swagger",
|
|
30
|
+
"swagger.json",
|
|
26
31
|
"test"
|
|
27
32
|
] as const;
|