@promptbook/cli 0.104.0-2 → 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.
Files changed (45) hide show
  1. package/apps/agents-server/package.json +3 -4
  2. package/apps/agents-server/public/swagger.json +115 -0
  3. package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +11 -7
  4. package/apps/agents-server/src/app/AddAgentButton.tsx +1 -2
  5. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
  6. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
  7. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +8 -8
  8. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
  9. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +1 -3
  10. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +29 -16
  11. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +3 -0
  12. package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
  13. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +4 -1
  14. package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +8 -6
  15. package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +33 -30
  16. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +10 -12
  17. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +1 -2
  18. package/apps/agents-server/src/app/api/agents/route.ts +1 -1
  19. package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
  20. package/apps/agents-server/src/app/api/docs/book.md/route.ts +3 -0
  21. package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
  22. package/apps/agents-server/src/app/api/upload/route.ts +9 -0
  23. package/apps/agents-server/src/app/page.tsx +1 -1
  24. package/apps/agents-server/src/app/swagger/page.tsx +14 -0
  25. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +4 -2
  26. package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
  27. package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
  28. package/apps/agents-server/src/components/Header/Header.tsx +27 -5
  29. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +22 -3
  30. package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
  31. package/apps/agents-server/src/generated/reservedPaths.ts +6 -1
  32. package/apps/agents-server/src/middleware.ts +12 -3
  33. package/apps/agents-server/src/utils/auth.ts +117 -17
  34. package/apps/agents-server/src/utils/getUserIdFromRequest.ts +3 -1
  35. package/apps/agents-server/src/utils/handleChatCompletion.ts +9 -5
  36. package/apps/agents-server/src/utils/validateApiKey.ts +5 -10
  37. package/esm/index.es.js +55 -8
  38. package/esm/index.es.js.map +1 -1
  39. package/esm/typings/src/_packages/types.index.d.ts +2 -0
  40. package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
  41. package/esm/typings/src/types/Message.d.ts +49 -0
  42. package/esm/typings/src/version.d.ts +1 -1
  43. package/package.json +1 -1
  44. package/umd/index.umd.js +55 -8
  45. 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
- // If success, it means it exists, so we try next one
25
- counter++;
26
- newAgentName = `${agentName} (Copy ${counter})`;
27
- } catch (error) {
28
- // If error, it likely means it does not exist (NotFoundError), so we can use it
29
- // TODO: [🧠] Check if it is really NotFoundError
30
- break;
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, tablePrefix } = await $provideServer();
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 ? (
@@ -24,7 +24,6 @@ export function QrCodeModal({
24
24
  personaDescription,
25
25
  agentUrl,
26
26
  agentEmail,
27
- brandColorHex,
28
27
  }: QrCodeModalProps) {
29
28
  useEffect(() => {
30
29
  if (isOpen) {
@@ -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, setUser] = useState(initialUser);
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 && <span className="ml-2 bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">Admin</span>}
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 src={server.logoUrl || serverLogoUrl || promptbookLogoBlueTransparent.src} alt={server.title} width={20} height={20} className="w-5 h-5 object-contain rounded-full" />
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 src={server.logoUrl || promptbookLogoBlueTransparent.src} alt={server.title} width={20} height={20} className="w-5 h-5 object-contain rounded-full" />
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({ agent, href, isAdmin, onDelete, onClone, onToggleVisibility, onRestore, visibility }: AgentCardProps) {
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 rounded-xl shadow-lg overflow-hidden flex-shrink-0 bg-black/20"
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={`${visibility === 'PUBLIC' ? 'bg-green-500 hover:bg-green-600' : 'bg-gray-500 hover:bg-gray-600'} ${ACTION_BUTTON_CLASSES}`}
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({ href, children, ...rest }: LinkProps & { children: React.ReactNode } & React.AnchorHTMLAttributes<HTMLAnchorElement>) {
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 "next/navigation";
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) {
@@ -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;
@@ -1,7 +1,7 @@
1
1
  import { TODO_any } from '@promptbook-local/types';
2
2
  import { createClient } from '@supabase/supabase-js';
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { SERVERS, SUPABASE_TABLE_PREFIX } from '../config';
4
+ import { SERVERS } from '../config';
5
5
  import { $getTableName } from './database/$getTableName';
6
6
  import { RESERVED_PATHS } from './generated/reservedPaths';
7
7
  import { isIpAllowed } from './utils/isIpAllowed';
@@ -9,7 +9,7 @@ import { isIpAllowed } from './utils/isIpAllowed';
9
9
  // Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies !!!!
10
10
  function normalizeTo_PascalCase(text: string): string {
11
11
  return text
12
- .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
12
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => {
13
13
  return word.toUpperCase();
14
14
  })
15
15
  .replace(/\s+/g, '');
@@ -35,8 +35,12 @@ export async function middleware(req: NextRequest) {
35
35
  const host = req.headers.get('host');
36
36
 
37
37
  if (host) {
38
+ /*
39
+ Note: [🐔] This code was commented out because results of it are unused
40
+
38
41
  let tablePrefix = SUPABASE_TABLE_PREFIX;
39
42
 
43
+
40
44
  if (SERVERS && SERVERS.length > 0) {
41
45
  // Logic mirrored from src/tools/$provideServer.ts
42
46
  if (SERVERS.some((server) => server === host)) {
@@ -46,6 +50,7 @@ export async function middleware(req: NextRequest) {
46
50
  tablePrefix = `server_${serverName}_`;
47
51
  }
48
52
  }
53
+ */
49
54
 
50
55
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
51
56
  const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
@@ -84,6 +89,9 @@ export async function middleware(req: NextRequest) {
84
89
  const token = authHeader.split(' ')[1];
85
90
 
86
91
  if (token.startsWith('ptbk_')) {
92
+ /*
93
+ Note: [🐔] This code was commented out because results of it are unused
94
+
87
95
  const host = req.headers.get('host');
88
96
  let tablePrefix = SUPABASE_TABLE_PREFIX;
89
97
 
@@ -95,6 +103,7 @@ export async function middleware(req: NextRequest) {
95
103
  tablePrefix = `server_${serverName}_`;
96
104
  }
97
105
  }
106
+ */
98
107
 
99
108
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
100
109
  const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
@@ -230,7 +239,7 @@ export async function middleware(req: NextRequest) {
230
239
  let serverName = serverHost;
231
240
  serverName = serverName.replace(/\.ptbk\.io$/, '');
232
241
  serverName = normalizeTo_PascalCase(serverName);
233
- const prefix = `server_${serverName}_`;
242
+ // const prefix = `server_${serverName}_`;
234
243
 
235
244
  // Search for agent with matching META LINK
236
245
  // agentProfile->links is an array of strings