@promptbook/cli 0.104.0-0 → 0.104.0-2

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 (118) hide show
  1. package/apps/agents-server/next.config.ts +2 -2
  2. package/apps/agents-server/package.json +6 -1
  3. package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
  4. package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +50 -0
  5. package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
  6. package/apps/agents-server/src/app/AddAgentButton.tsx +4 -3
  7. package/apps/agents-server/src/app/actions.ts +17 -5
  8. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +15 -11
  9. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -7
  10. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +32 -2
  11. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +2 -0
  12. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +18 -0
  13. package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
  14. package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
  15. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +17 -0
  16. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +1 -1
  17. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +1 -1
  18. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
  19. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
  20. package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
  21. package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
  22. package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +66 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +211 -0
  24. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
  25. package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
  26. package/apps/agents-server/src/app/agents/[agentName]/integration/WebsiteIntegrationTabs.tsx +26 -0
  27. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +23 -6
  28. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +2 -2
  29. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +12 -6
  30. package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +87 -0
  31. package/apps/agents-server/src/app/agents/[agentName]/website-integration/page.tsx +35 -18
  32. package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
  33. package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +19 -0
  34. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +42 -0
  35. package/apps/agents-server/src/app/api/agents/route.ts +29 -4
  36. package/apps/agents-server/src/app/api/docs/book.md/route.ts +58 -0
  37. package/apps/agents-server/src/app/api/embed.js/route.ts +87 -67
  38. package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
  39. package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
  40. package/apps/agents-server/src/app/api/upload/route.ts +119 -45
  41. package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
  42. package/apps/agents-server/src/app/docs/page.tsx +12 -12
  43. package/apps/agents-server/src/app/embed/layout.tsx +31 -0
  44. package/apps/agents-server/src/app/embed/page.tsx +22 -9
  45. package/apps/agents-server/src/app/globals.css +140 -33
  46. package/apps/agents-server/src/app/layout.tsx +27 -22
  47. package/apps/agents-server/src/app/page.tsx +50 -4
  48. package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
  49. package/apps/agents-server/src/app/recycle-bin/page.tsx +25 -41
  50. package/apps/agents-server/src/app/sitemap.xml/route.ts +6 -3
  51. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +6 -97
  52. package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
  53. package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
  54. package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
  55. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
  56. package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
  57. package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
  58. package/apps/agents-server/src/components/Header/Header.tsx +79 -35
  59. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +85 -20
  60. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +72 -12
  61. package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +50 -0
  62. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
  63. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
  64. package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
  65. package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
  66. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
  67. package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
  68. package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
  69. package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
  70. package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
  71. package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
  72. package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
  73. package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
  74. package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
  75. package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
  76. package/apps/agents-server/src/database/schema.ts +109 -0
  77. package/apps/agents-server/src/generated/reservedPaths.ts +27 -0
  78. package/apps/agents-server/src/middleware.ts +7 -20
  79. package/apps/agents-server/src/tools/$provideCdnForServer.ts +6 -1
  80. package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
  81. package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
  82. package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
  83. package/apps/agents-server/src/utils/getUserIdFromRequest.ts +33 -0
  84. package/apps/agents-server/src/utils/handleChatCompletion.ts +60 -4
  85. package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +21 -0
  86. package/apps/agents-server/src/utils/validateApiKey.ts +2 -1
  87. package/esm/index.es.js +140 -27
  88. package/esm/index.es.js.map +1 -1
  89. package/esm/typings/src/_packages/types.index.d.ts +6 -2
  90. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
  91. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
  92. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  93. package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
  94. package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  95. package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
  96. package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
  97. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +13 -7
  98. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +6 -0
  99. package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
  100. package/esm/typings/src/commitments/index.d.ts +2 -1
  101. package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
  102. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
  103. package/esm/typings/src/types/typeAliases.d.ts +12 -0
  104. package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
  105. package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
  106. package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
  107. package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
  108. package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
  109. package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
  110. package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
  111. package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
  112. package/esm/typings/src/version.d.ts +1 -1
  113. package/package.json +1 -1
  114. package/umd/index.umd.js +146 -33
  115. package/umd/index.umd.js.map +1 -1
  116. package/apps/agents-server/package-lock.json +0 -27
  117. package/apps/agents-server/public/fonts/download-font.js +0 -22
  118. package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
@@ -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
+ }
@@ -7,8 +7,9 @@ import { parseAgentSource } from '@promptbook-local/core';
7
7
  import { headers } from 'next/headers';
8
8
  import spaceTrim from 'spacetrim';
9
9
  import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
10
- import { CodePreview } from '../../../../../../_common/components/CodePreview/CodePreview';
10
+ import { just } from '../../../../../../../src/utils/organization/just';
11
11
  import { generateAgentMetadata } from '../generateAgentMetadata';
12
+ import { WebsiteIntegrationTabs } from '../integration/WebsiteIntegrationTabs';
12
13
 
13
14
  export const generateMetadata = generateAgentMetadata;
14
15
 
@@ -24,20 +25,31 @@ export default async function WebsiteIntegrationAgentPage({ params }: { params:
24
25
  const { publicUrl } = await $provideServer();
25
26
  const agentUrl = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
26
27
 
27
- const code = spaceTrim(
28
+ const reactCode = spaceTrim(
28
29
  (block) => `
29
-
30
- import { PromptbookAgent } from '@promptbook/components';
30
+ import { PromptbookAgentIntegration } from '@promptbook/components';
31
31
 
32
32
  export function YourComponent() {
33
33
  return(
34
- <PromptbookAgent
34
+ <PromptbookAgentIntegration
35
35
  agentUrl="${agentUrl}"
36
36
  meta={${block(JSON.stringify({ fullname, color, image, ...restMeta }, null, 4))}}
37
37
  />
38
38
  );
39
39
  }
40
-
40
+ `,
41
+ );
42
+
43
+ // HTML Integration Code - use single quotes for meta attribute to allow JSON with double quotes inside
44
+ const metaJsonString = JSON.stringify({ fullname, color, image, ...restMeta }, null, 4);
45
+ const htmlCode = spaceTrim(
46
+ (block) => `
47
+ <script src="${publicUrl.href}api/embed.js" async defer></script>
48
+
49
+ <promptbook-agent-integration
50
+ agent-url="${agentUrl}"
51
+ meta='${block(metaJsonString)}'
52
+ />
41
53
  `,
42
54
  );
43
55
 
@@ -49,19 +61,24 @@ export default async function WebsiteIntegrationAgentPage({ params }: { params:
49
61
  React application using the <code>{'<PromptbookAgent />'}</code> component.
50
62
  </p>
51
63
 
52
- <CodePreview code={code} />
53
- <PromptbookAgentIntegration
54
- // formfactor="profile"
55
- agentUrl={agentUrl}
56
- meta={meta}
57
- style={
58
- {
59
- // width: '400px',
60
- // height: '600px',
61
- // outline: `2px solid red`
64
+ <WebsiteIntegrationTabs reactCode={reactCode} htmlCode={htmlCode} />
65
+ {just(false) && (
66
+ <PromptbookAgentIntegration
67
+ // formfactor="profile"
68
+ agentUrl={agentUrl}
69
+ meta={meta}
70
+ style={
71
+ {
72
+ // width: '400px',
73
+ // height: '600px',
74
+ // outline: `2px solid red`
75
+ }
62
76
  }
63
- }
64
- />
77
+ />
78
+ )}
79
+ {htmlCode}
80
+ {just(true) && <div dangerouslySetInnerHTML={{ __html: htmlCode }} />}
81
+ {just(true) && <div dangerouslySetInnerHTML={{ __html: `<h1>Test</h1>` }} />}
65
82
  </main>
66
83
  );
67
84
  }
@@ -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
+ }
@@ -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,50 @@
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';
7
+ import { $provideServer } from '@/src/tools/$provideServer';
3
8
  import { TODO_any } from '@promptbook-local/types';
4
9
  import { NextResponse } from 'next/server';
5
10
 
11
+ export async function PATCH(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
12
+ const { agentName } = await params;
13
+
14
+ try {
15
+ const body = await request.json();
16
+ const { visibility }: { visibility: 'PUBLIC' | 'PRIVATE' } = body;
17
+
18
+ if (!visibility || !['PUBLIC', 'PRIVATE'].includes(visibility)) {
19
+ return NextResponse.json(
20
+ { success: false, error: 'Invalid visibility value. Must be PUBLIC or PRIVATE.' },
21
+ { status: 400 },
22
+ );
23
+ }
24
+
25
+ const supabase = $provideSupabaseForServer();
26
+ const { tablePrefix } = await $provideServer();
27
+
28
+ const updateResult = await supabase
29
+ .from(await $getTableName(`Agent`))
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ .update({ visibility } as any)
32
+ .or(`agentName.eq.${agentName},permanentId.eq.${agentName}`)
33
+ .is('deletedAt', null);
34
+
35
+ if (updateResult.error) {
36
+ return NextResponse.json({ success: false, error: updateResult.error.message }, { status: 500 });
37
+ }
38
+
39
+ return NextResponse.json({ success: true });
40
+ } catch (error) {
41
+ return NextResponse.json(
42
+ { success: false, error: (error as TODO_any)?.message || 'Failed to update agent visibility' },
43
+ { status: 500 },
44
+ );
45
+ }
46
+ }
47
+
6
48
  export async function DELETE(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
7
49
  const { agentName } = await params;
8
50
  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 agents = await collection.listAgents();
13
+ const allAgents = await collection.listAgents();
12
14
  const federatedServers = await getFederatedServersFromMetadata();
13
- const { publicUrl } = await $provideServer();
15
+ const { publicUrl, tablePrefix } = await $provideServer();
14
16
 
15
- const agentsWithUrl = agents.map((agent) => ({
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({
@@ -0,0 +1,58 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import spaceTrim from 'spacetrim';
3
+ import { getGroupedCommitmentDefinitions } from '../../../../../../../src/commitments';
4
+
5
+ export const dynamic = 'force-static';
6
+
7
+ export async function GET(request: NextRequest) {
8
+ const groupedCommitments = getGroupedCommitmentDefinitions();
9
+
10
+ const content = spaceTrim(
11
+ (block) => `
12
+ # Promptbook Documentation
13
+
14
+ Promptbook is a language for defining AI agents. It is based on Markdown and uses a set of commitments to define the behavior of the agent.
15
+
16
+ ## Commitments
17
+
18
+ The following commands (commitments) are available in Promptbook:
19
+
20
+ ${block(
21
+ groupedCommitments
22
+ .map(({ primary, aliases }) => {
23
+ const title = primary.type;
24
+ const description = primary.description;
25
+ const documentation = primary.documentation;
26
+ const aliasList = aliases.length > 0 ? `**Aliases:** ${aliases.join(', ')}` : '';
27
+
28
+ return spaceTrim(
29
+ (block) => `
30
+ ### ${title}
31
+
32
+ ${description}
33
+
34
+ ${aliasList}
35
+
36
+ #### Usage
37
+
38
+ ${block(getSafeCodeBlock(documentation))}
39
+ `,
40
+ );
41
+ })
42
+ .join('\n\n'),
43
+ )}
44
+ `,
45
+ );
46
+
47
+ return new NextResponse(content, {
48
+ headers: {
49
+ 'Content-Type': 'text/markdown; charset=utf-8',
50
+ },
51
+ });
52
+ }
53
+
54
+ function getSafeCodeBlock(content: string, lang = 'markdown'): string {
55
+ const maxBackticks = Math.max(0, ...(content.match(/`+/g) || []).map((m) => m.length));
56
+ const fence = '`'.repeat(Math.max(3, maxBackticks + 1));
57
+ return `${fence}${lang}\n${content}\n${fence}`;
58
+ }
@@ -1,3 +1,4 @@
1
+ import { spaceTrim } from '@promptbook-local/utils';
1
2
  import { NextRequest, NextResponse } from 'next/server';
2
3
 
3
4
  export async function GET(request: NextRequest) {
@@ -5,84 +6,103 @@ export async function GET(request: NextRequest) {
5
6
  const host = request.nextUrl.host;
6
7
  const baseUrl = `${protocol}//${host}`;
7
8
 
8
- const script = `
9
- (function() {
10
- if (customElements.get('promptbook-agent')) {
11
- return;
12
- }
9
+ const script = spaceTrim(`
10
+ console.info('[🔌] Promptbook integration script from ${baseUrl}');
13
11
 
14
- class PromptbookAgentElement extends HTMLElement {
15
- constructor() {
16
- super();
17
- this.iframe = null;
18
- }
12
+ (function() {
13
+ if (customElements.get('promptbook-agent-integration')) {
14
+ return;
15
+ }
19
16
 
20
- static get observedAttributes() {
21
- return ['agent-url'];
22
- }
17
+ class PromptbookAgentIntegrationElement extends HTMLElement {
18
+ constructor() {
19
+ super();
20
+ console.info('[🔌] Initializing <promptbook-agent-integration/>',this);
21
+ this.iframe = null;
22
+ }
23
23
 
24
- connectedCallback() {
25
- this.render();
26
- window.addEventListener('message', this.handleMessage.bind(this));
27
- }
24
+ static get observedAttributes() {
25
+ return ['agent-url', 'meta'];
26
+ }
28
27
 
29
- disconnectedCallback() {
30
- window.removeEventListener('message', this.handleMessage.bind(this));
31
- }
28
+ connectedCallback() {
29
+ this.render();
30
+ window.addEventListener('message', this.handleMessage.bind(this));
31
+ }
32
32
 
33
- attributeChangedCallback(name, oldValue, newValue) {
34
- if (name === 'agent-url' && oldValue !== newValue) {
35
- this.render();
36
- }
37
- }
33
+ disconnectedCallback() {
34
+ window.removeEventListener('message', this.handleMessage.bind(this));
35
+ }
38
36
 
39
- handleMessage(event) {
40
- if (event.data && event.data.type === 'PROMPTBOOK_AGENT_RESIZE') {
41
- if (event.data.isOpen) {
42
- this.iframe.style.width = '450px';
43
- this.iframe.style.height = '650px';
44
- this.iframe.style.maxHeight = '90vh';
45
- this.iframe.style.maxWidth = '90vw';
46
- this.iframe.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
47
- this.iframe.style.borderRadius = '12px';
48
- } else {
49
- this.iframe.style.width = '60px';
50
- this.iframe.style.height = '60px';
51
- this.iframe.style.boxShadow = 'none';
52
- this.iframe.style.borderRadius = '0';
37
+ attributeChangedCallback(name, oldValue, newValue) {
38
+ if ((name === 'agent-url' || name === 'meta') && oldValue !== newValue) {
39
+ this.render();
40
+ }
53
41
  }
54
- }
55
- }
56
42
 
57
- render() {
58
- const agentUrl = this.getAttribute('agent-url');
59
- if (!agentUrl) return;
43
+ handleMessage(event) {
44
+ if (event.data && event.data.type === 'PROMPTBOOK_AGENT_RESIZE') {
45
+ if (event.data.isOpen) {
46
+ // Match PromptbookAgentSeamlessIntegration.module.css dimensions
47
+ // Window is 380x600 + 20px padding on each side
48
+ this.iframe.style.width = '420px';
49
+ this.iframe.style.height = '640px';
50
+ this.iframe.style.maxHeight = 'calc(80vh + 40px)';
51
+ this.iframe.style.maxWidth = 'calc(100vw - 20px)';
52
+ } else {
53
+ // Closed state - button area with padding and shadow space
54
+ // Button is ~140px wide + 20px right margin + shadow space
55
+ this.iframe.style.width = '180px';
56
+ this.iframe.style.height = '100px';
57
+ this.iframe.style.maxHeight = 'none';
58
+ this.iframe.style.maxWidth = 'none';
59
+ }
60
+ }
61
+ }
60
62
 
61
- if (!this.iframe) {
62
- this.attachShadow({ mode: 'open' });
63
- this.iframe = document.createElement('iframe');
64
- this.iframe.style.border = 'none';
65
- this.iframe.style.position = 'fixed';
66
- this.iframe.style.bottom = '20px';
67
- this.iframe.style.right = '20px';
68
- this.iframe.style.width = '60px';
69
- this.iframe.style.height = '60px';
70
- this.iframe.style.zIndex = '2147483647'; // Max z-index
71
- this.iframe.style.transition = 'width 0.3s ease, height 0.3s ease';
72
- this.iframe.style.backgroundColor = 'transparent';
73
- this.iframe.setAttribute('allow', 'microphone'); // Allow microphone if needed for voice
74
- this.shadowRoot.appendChild(this.iframe);
75
- }
63
+ render() {
64
+ const agentUrl = this.getAttribute('agent-url');
65
+ if (!agentUrl) return;
76
66
 
77
- // Construct embed URL pointing to the Next.js page we created
78
- const embedUrl = '${baseUrl}/embed?agentUrl=' + encodeURIComponent(agentUrl);
79
- this.iframe.src = embedUrl;
80
- }
81
- }
67
+ if (!this.iframe) {
68
+ this.attachShadow({ mode: 'open' });
69
+ this.iframe = document.createElement('iframe');
70
+ this.iframe.style.border = 'none';
71
+ this.iframe.style.position = 'fixed';
72
+ this.iframe.style.bottom = '0';
73
+ this.iframe.style.right = '0';
74
+ // Initial size for the closed button state (with padding and shadow space)
75
+ this.iframe.style.width = '180px';
76
+ this.iframe.style.height = '100px';
77
+ this.iframe.style.zIndex = '2147483647'; // Max z-index
78
+ this.iframe.style.transition = 'width 0.3s ease, height 0.3s ease';
79
+ this.iframe.style.backgroundColor = 'transparent';
80
+ this.iframe.setAttribute('allow', 'microphone'); // Allow microphone if needed for voice
81
+ this.shadowRoot.appendChild(this.iframe);
82
+ }
83
+
84
+ // Construct embed URL pointing to the Next.js page we created
85
+ let embedUrl = '${baseUrl}/embed?agentUrl=' + encodeURIComponent(agentUrl);
86
+
87
+ // Add meta parameter if provided
88
+ const metaAttr = this.getAttribute('meta');
89
+ if (metaAttr) {
90
+ try {
91
+ // Validate that it's valid JSON
92
+ JSON.parse(metaAttr);
93
+ embedUrl += '&meta=' + encodeURIComponent(metaAttr);
94
+ } catch (e) {
95
+ console.error('[🔌] Invalid meta JSON:', e);
96
+ }
97
+ }
98
+
99
+ this.iframe.src = embedUrl;
100
+ }
101
+ }
82
102
 
83
- customElements.define('promptbook-agent', PromptbookAgentElement);
84
- })();
85
- `;
103
+ customElements.define('promptbook-agent-integration', PromptbookAgentIntegrationElement);
104
+ })();
105
+ `);
86
106
 
87
107
  return new NextResponse(script, {
88
108
  headers: {
@@ -1,10 +1,22 @@
1
1
  import { NextResponse } from 'next/server';
2
2
  import { getFederatedServersFromMetadata } from '../../../utils/getFederatedServersFromMetadata';
3
+ import { getMetadata } from '../../../database/getMetadata';
4
+ import { getCurrentUser } from '../../../utils/getCurrentUser';
3
5
 
4
6
  export const dynamic = 'force-dynamic';
5
7
 
6
8
  export async function GET() {
7
9
  try {
10
+ const currentUser = await getCurrentUser();
11
+ const showFederatedServersPublicly = ((await getMetadata('SHOW_FEDERATED_SERVERS_PUBLICLY')) || 'false') === 'true';
12
+
13
+ // Only show federated servers if user is authenticated or if SHOW_FEDERATED_SERVERS_PUBLICLY is true
14
+ if (!currentUser && !showFederatedServersPublicly) {
15
+ return NextResponse.json({
16
+ federatedServers: [],
17
+ });
18
+ }
19
+
8
20
  const federatedServers = await getFederatedServersFromMetadata();
9
21
 
10
22
  return NextResponse.json({