@promptbook/cli 0.103.0-54 → 0.103.0-56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/apps/agents-server/config.ts +0 -2
  2. package/apps/agents-server/package-lock.json +1163 -0
  3. package/apps/agents-server/package.json +6 -0
  4. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +79 -6
  5. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +171 -69
  6. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +3 -1
  7. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +216 -0
  8. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +78 -0
  9. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileView.tsx +233 -0
  10. package/apps/agents-server/src/app/agents/[agentName]/CloneAgentButton.tsx +4 -4
  11. package/apps/agents-server/src/app/agents/[agentName]/InstallPwaButton.tsx +2 -2
  12. package/apps/agents-server/src/app/agents/[agentName]/QrCodeModal.tsx +90 -0
  13. package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +80 -0
  14. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +3 -1
  15. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +11 -1
  16. package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +203 -0
  17. package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/route.ts +3 -1
  18. package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts +3 -1
  19. package/apps/agents-server/src/app/agents/[agentName]/api/openai/chat/completions/route.ts +3 -169
  20. package/apps/agents-server/src/app/agents/[agentName]/api/openai/models/route.ts +93 -0
  21. package/apps/agents-server/src/app/agents/[agentName]/api/openai/v1/chat/completions/route.ts +10 -0
  22. package/apps/agents-server/src/app/agents/[agentName]/api/openai/v1/models/route.ts +93 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/api/openrouter/chat/completions/route.ts +10 -0
  24. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +4 -0
  25. package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +9 -2
  26. package/apps/agents-server/src/app/agents/[agentName]/integration/SdkCodeTabs.tsx +31 -0
  27. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +271 -30
  28. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +182 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +108 -165
  30. package/apps/agents-server/src/app/agents/[agentName]/website-integration/page.tsx +61 -0
  31. package/apps/agents-server/src/app/api/auth/change-password/route.ts +75 -0
  32. package/apps/agents-server/src/app/api/chat-feedback/export/route.ts +55 -0
  33. package/apps/agents-server/src/app/api/chat-history/export/route.ts +55 -0
  34. package/apps/agents-server/src/app/api/openai/v1/chat/completions/route.ts +6 -0
  35. package/apps/agents-server/src/app/api/openai/v1/models/route.ts +65 -0
  36. package/apps/agents-server/src/app/docs/[docId]/page.tsx +12 -32
  37. package/apps/agents-server/src/app/docs/page.tsx +42 -17
  38. package/apps/agents-server/src/app/globals.css +129 -0
  39. package/apps/agents-server/src/app/layout.tsx +8 -2
  40. package/apps/agents-server/src/app/manifest.ts +1 -1
  41. package/apps/agents-server/src/components/ChangePasswordDialog/ChangePasswordDialog.tsx +41 -0
  42. package/apps/agents-server/src/components/ChangePasswordForm/ChangePasswordForm.tsx +159 -0
  43. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +87 -0
  44. package/apps/agents-server/src/components/Header/Header.tsx +94 -38
  45. package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +20 -0
  46. package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +18 -0
  47. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +18 -0
  48. package/apps/agents-server/src/database/migrations/2025-12-0070-chat-history-source.sql +2 -0
  49. package/apps/agents-server/src/database/schema.ts +6 -0
  50. package/apps/agents-server/src/middleware.ts +1 -1
  51. package/apps/agents-server/src/utils/convertToCsv.ts +31 -0
  52. package/apps/agents-server/src/utils/handleChatCompletion.ts +355 -0
  53. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +100 -0
  54. package/apps/agents-server/src/utils/validateApiKey.ts +128 -0
  55. package/apps/agents-server/tailwind.config.ts +1 -1
  56. package/esm/index.es.js +1188 -175
  57. package/esm/index.es.js.map +1 -1
  58. package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +4 -0
  59. package/esm/typings/src/book-components/Chat/LlmChat/LlmChatProps.d.ts +5 -0
  60. package/esm/typings/src/commitments/CLOSED/CLOSED.d.ts +35 -0
  61. package/esm/typings/src/commitments/COMPONENT/COMPONENT.d.ts +28 -0
  62. package/esm/typings/src/commitments/FROM/FROM.d.ts +34 -0
  63. package/esm/typings/src/commitments/LANGUAGE/LANGUAGE.d.ts +35 -0
  64. package/esm/typings/src/commitments/META_COLOR/META_COLOR.d.ts +6 -0
  65. package/esm/typings/src/commitments/META_FONT/META_FONT.d.ts +42 -0
  66. package/esm/typings/src/commitments/OPEN/OPEN.d.ts +35 -0
  67. package/esm/typings/src/commitments/USE/USE.d.ts +53 -0
  68. package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.d.ts +38 -0
  69. package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.test.d.ts +1 -0
  70. package/esm/typings/src/commitments/USE_MCP/USE_MCP.d.ts +37 -0
  71. package/esm/typings/src/commitments/USE_SEARCH_ENGINE/USE_SEARCH_ENGINE.d.ts +38 -0
  72. package/esm/typings/src/commitments/index.d.ts +12 -1
  73. package/esm/typings/src/playground/playground.d.ts +3 -0
  74. package/esm/typings/src/utils/color/Color.d.ts +8 -0
  75. package/esm/typings/src/utils/color/css-colors.d.ts +1 -0
  76. package/esm/typings/src/version.d.ts +1 -1
  77. package/package.json +2 -2
  78. package/umd/index.umd.js +1180 -167
  79. package/umd/index.umd.js.map +1 -1
  80. package/esm/typings/src/playground/playground1.d.ts +0 -2
@@ -0,0 +1,93 @@
1
+ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
2
+ import { validateApiKey } from '@/src/utils/validateApiKey';
3
+ import { NextRequest, NextResponse } from 'next/server';
4
+
5
+ /**
6
+ * GET /agents/[agentName]/api/openai/v1/models
7
+ *
8
+ * Lists available models for the OpenAI-compatible API.
9
+ * This endpoint is required for OpenAI-compatible clients (like Jan, LM Studio, etc.)
10
+ * to discover available models.
11
+ */
12
+ export async function GET(request: NextRequest, { params }: { params: Promise<{ agentName: string }> }) {
13
+ const { agentName } = await params;
14
+
15
+ // Validate API key explicitly (in addition to middleware)
16
+ const apiKeyValidation = await validateApiKey(request);
17
+ if (!apiKeyValidation.isValid) {
18
+ return NextResponse.json(
19
+ {
20
+ error: {
21
+ message: apiKeyValidation.error || 'Invalid API key',
22
+ type: 'authentication_error',
23
+ },
24
+ },
25
+ { status: 401 },
26
+ );
27
+ }
28
+
29
+ try {
30
+ const collection = await $provideAgentCollectionForServer();
31
+
32
+ let agentSource;
33
+ try {
34
+ agentSource = await collection.getAgentSource(agentName);
35
+ } catch (error) {
36
+ return NextResponse.json(
37
+ { error: { message: `Agent '${agentName}' not found.`, type: 'invalid_request_error' } },
38
+ { status: 404 },
39
+ );
40
+ }
41
+
42
+ if (!agentSource) {
43
+ return NextResponse.json(
44
+ { error: { message: `Agent '${agentName}' not found.`, type: 'invalid_request_error' } },
45
+ { status: 404 },
46
+ );
47
+ }
48
+
49
+ // Return the agent as a single model in OpenAI format
50
+ // The model ID is the agent name, which clients will use when making chat completion requests
51
+ const models = [
52
+ {
53
+ id: agentName,
54
+ object: 'model',
55
+ created: Math.floor(Date.now() / 1000),
56
+ owned_by: 'promptbook',
57
+ permission: [
58
+ {
59
+ id: `modelperm-${agentName}`,
60
+ object: 'model_permission',
61
+ created: Math.floor(Date.now() / 1000),
62
+ allow_create_engine: false,
63
+ allow_sampling: true,
64
+ allow_logprobs: false,
65
+ allow_search_indices: false,
66
+ allow_view: true,
67
+ allow_fine_tuning: false,
68
+ organization: '*',
69
+ group: null,
70
+ is_blocking: false,
71
+ },
72
+ ],
73
+ root: agentName,
74
+ parent: null,
75
+ },
76
+ ];
77
+
78
+ return NextResponse.json({
79
+ object: 'list',
80
+ data: models,
81
+ });
82
+ } catch (error) {
83
+ console.error('Error in models listing handler:', error);
84
+ return NextResponse.json(
85
+ { error: { message: (error as Error).message || 'Internal Server Error', type: 'server_error' } },
86
+ { status: 500 },
87
+ );
88
+ }
89
+ }
90
+
91
+ /**
92
+ * TODO: [🧠] Consider listing all available agents as models when agentName is a wildcard or special value
93
+ */
@@ -0,0 +1,10 @@
1
+ import { handleChatCompletion } from '@/src/utils/handleChatCompletion';
2
+ import { NextRequest } from 'next/server';
3
+
4
+ export async function POST(
5
+ request: NextRequest,
6
+ { params }: { params: Promise<{ agentName: string }> },
7
+ ) {
8
+ const { agentName } = await params;
9
+ return handleChatCompletion(request, { agentName }, 'OpenRouter API Chat Completion');
10
+ }
@@ -98,6 +98,8 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
98
98
  userAgent,
99
99
  language,
100
100
  platform,
101
+ source: 'AGENT_PAGE_CHAT',
102
+ apiKey: null,
101
103
  });
102
104
 
103
105
  // Call Agent
@@ -131,6 +133,8 @@ export async function POST(request: Request, { params }: { params: Promise<{ age
131
133
  userAgent,
132
134
  language,
133
135
  platform,
136
+ source: 'AGENT_PAGE_CHAT',
137
+ apiKey: null,
134
138
  });
135
139
 
136
140
  // Learning
@@ -7,16 +7,23 @@ import { AgentChatWrapper } from '../AgentChatWrapper';
7
7
 
8
8
  export const generateMetadata = generateAgentMetadata;
9
9
 
10
- export default async function AgentChatPage({ params }: { params: Promise<{ agentName: string }> }) {
10
+ export default async function AgentChatPage({
11
+ params,
12
+ searchParams,
13
+ }: {
14
+ params: Promise<{ agentName: string }>;
15
+ searchParams: Promise<{ message?: string }>;
16
+ }) {
11
17
  $sideEffect(headers());
12
18
  let { agentName } = await params;
13
19
  agentName = decodeURIComponent(agentName);
20
+ const { message } = await searchParams;
14
21
 
15
22
  const agentUrl = `/agents/${agentName}`;
16
23
 
17
24
  return (
18
25
  <main className={`w-screen h-screen`}>
19
- <AgentChatWrapper agentUrl={agentUrl} />
26
+ <AgentChatWrapper agentUrl={agentUrl} autoExecuteMessage={message} />
20
27
  </main>
21
28
  );
22
29
  }
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+
3
+ import { CodePreview } from '../../../../../../_common/components/CodePreview/CodePreview';
4
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../../../../../_common/components/Tabs/Tabs';
5
+
6
+ type SdkCodeTabsProps = {
7
+ curlCode: string;
8
+ pythonCode: string;
9
+ jsCode: string;
10
+ };
11
+
12
+ export function SdkCodeTabs({ curlCode, pythonCode, jsCode }: SdkCodeTabsProps) {
13
+ return (
14
+ <Tabs defaultValue="curl" className="w-full">
15
+ <TabsList>
16
+ <TabsTrigger value="curl">cURL</TabsTrigger>
17
+ <TabsTrigger value="python">Python SDK</TabsTrigger>
18
+ <TabsTrigger value="javascript">JavaScript/TypeScript SDK</TabsTrigger>
19
+ </TabsList>
20
+ <TabsContent value="curl">
21
+ <CodePreview code={curlCode} language="bash" />
22
+ </TabsContent>
23
+ <TabsContent value="python">
24
+ <CodePreview code={pythonCode} language="python" />
25
+ </TabsContent>
26
+ <TabsContent value="javascript">
27
+ <CodePreview code={jsCode} language="typescript" />
28
+ </TabsContent>
29
+ </Tabs>
30
+ );
31
+ }
@@ -1,61 +1,302 @@
1
1
  'use server';
2
2
 
3
- import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
3
+ import { $getTableName } from '@/src/database/$getTableName';
4
+ import { $provideSupabase } from '@/src/database/$provideSupabase';
4
5
  import { $provideServer } from '@/src/tools/$provideServer';
5
- import { PromptbookAgent } from '@promptbook-local/components';
6
- import { parseAgentSource } from '@promptbook-local/core';
6
+ import { isUserAdmin } from '@/src/utils/isUserAdmin';
7
+ import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
8
+ import { ArrowLeftIcon, BoxIcon, CodeIcon, GlobeIcon, ServerIcon, TerminalIcon } from 'lucide-react';
7
9
  import { headers } from 'next/headers';
10
+ import Link from 'next/link';
11
+ import { notFound } from 'next/navigation';
8
12
  import spaceTrim from 'spacetrim';
13
+ import { Color } from '../../../../../../../src/utils/color/Color';
14
+ import { withAlpha } from '../../../../../../../src/utils/color/operators/withAlpha';
9
15
  import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
10
16
  import { CodePreview } from '../../../../../../_common/components/CodePreview/CodePreview';
17
+ import { getAgentLinks } from '../agentLinks';
18
+ import { CopyField } from '../CopyField';
19
+ import { getAgentName, getAgentProfile } from '../_utils';
11
20
  import { generateAgentMetadata } from '../generateAgentMetadata';
21
+ import { SdkCodeTabs } from './SdkCodeTabs';
12
22
 
13
23
  export const generateMetadata = generateAgentMetadata;
14
24
 
15
- export default async function AgentChatPage({ params }: { params: Promise<{ agentName: string }> }) {
25
+ export default async function AgentIntegrationPage({ params }: { params: Promise<{ agentName: string }> }) {
16
26
  $sideEffect(headers());
17
- let { agentName } = await params;
18
- agentName = decodeURIComponent(agentName);
27
+ const agentName = await getAgentName(params);
28
+ const isAdmin = await isUserAdmin();
29
+
30
+ let agentProfile;
31
+ try {
32
+ agentProfile = await getAgentProfile(agentName);
33
+ } catch (error) {
34
+ if (
35
+ error instanceof Error &&
36
+ (error.message.includes('Cannot coerce the result to a single JSON object') ||
37
+ error.message.includes('JSON object requested, multiple (or no) results returned'))
38
+ ) {
39
+ notFound();
40
+ }
41
+ throw error;
42
+ }
19
43
 
20
- const collection = await $provideAgentCollectionForServer();
21
- const agentSource = await collection.getAgentSource(agentName);
22
- const { meta } = parseAgentSource(agentSource);
23
- const { fullname, color, image, ...restMeta } = meta;
24
44
  const { publicUrl } = await $provideServer();
25
- const agentUrl = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
45
+ const baseUrl = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
46
+ const agentApiBase = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
47
+ const rootUrl = publicUrl.href.replace(/\/$/, '');
26
48
 
27
- const code = spaceTrim(
28
- (block) => `
49
+ // Get API Key if admin
50
+ let apiKey = 'ptbk_...';
51
+ if (isAdmin) {
52
+ const supabase = $provideSupabase();
53
+ const table = await $getTableName('ApiTokens');
54
+ const { data } = await supabase
55
+ .from(table)
56
+ .select('token')
57
+ .eq('isRevoked', false)
58
+ .order('createdAt', { ascending: false })
59
+ .limit(1);
60
+
61
+ if (data && data.length > 0) {
62
+ apiKey = data[0].token;
63
+ }
64
+ }
29
65
 
66
+ // Extract brand color from meta
67
+ const brandColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
68
+ const backgroundColor = (await brandColor.then(withAlpha(0.05))).toHex();
69
+ const borderColor = (await brandColor.then(withAlpha(0.1))).toHex();
70
+ const primaryColor = (await brandColor).toHex();
71
+
72
+ // Website Integration Code
73
+ const { fullname, color, image, ...restMeta } = agentProfile.meta;
74
+ const websiteIntegrationCode = spaceTrim(
75
+ (block) => `
30
76
  import { PromptbookAgent } from '@promptbook/components';
31
77
 
32
78
  export function YourComponent() {
33
79
  return(
34
80
  <PromptbookAgent
35
- agentUrl="${agentUrl}"
81
+ agentUrl="${baseUrl}"
36
82
  meta={${block(JSON.stringify({ fullname, color, image, ...restMeta }, null, 4))}}
37
83
  />
38
84
  );
39
85
  }
40
-
41
86
  `,
42
87
  );
43
88
 
89
+ // OpenAI Compatible Curl
90
+ const curlCode = spaceTrim(`
91
+ curl ${agentApiBase}/api/openai/v1/chat/completions \\
92
+ -H "Content-Type: application/json" \\
93
+ -H "Authorization: Bearer ${apiKey}" \\
94
+ -d '{
95
+ "model": "agent:${agentName}",
96
+ "messages": [
97
+ {"role": "user", "content": "Hello!"}
98
+ ]
99
+ }'
100
+ `);
101
+
102
+ // OpenAI Compatible Python
103
+ const pythonCode = spaceTrim(`
104
+ from openai import OpenAI
105
+
106
+ client = OpenAI(
107
+ base_url="${agentApiBase}/api/openai/v1",
108
+ api_key="${apiKey}",
109
+ )
110
+
111
+ response = client.chat.completions.create(
112
+ model="agent:${agentName}",
113
+ messages=[
114
+ {"role": "user", "content": "Hello!"}
115
+ ]
116
+ )
117
+
118
+ print(response.choices[0].message.content)
119
+ `);
120
+
121
+ // OpenAI Compatible JS
122
+ const jsCode = spaceTrim(`
123
+ import OpenAI from 'openai';
124
+
125
+ const client = new OpenAI({
126
+ baseURL: '${agentApiBase}/api/openai/v1',
127
+ apiKey: '${apiKey}',
128
+ });
129
+
130
+ async function main() {
131
+ const response = await client.chat.completions.create({
132
+ model: 'agent:${agentName}',
133
+ messages: [{ role: 'user', content: 'Hello!' }],
134
+ });
135
+
136
+ console.log(response.choices[0].message.content);
137
+ }
138
+
139
+ main();
140
+ `);
141
+
142
+ // MCP Config
143
+ const mcpConfigCode = spaceTrim(`
144
+ {
145
+ "mcpServers": {
146
+ "${agentName}": {
147
+ "command": "npx",
148
+ "args": [
149
+ "-y",
150
+ "@promptbook/cli",
151
+ "mcp",
152
+ "${baseUrl}"
153
+ ]
154
+ }
155
+ }
156
+ }
157
+ `);
158
+
159
+ const agentLinks = getAgentLinks(agentName);
160
+ const chatLink = agentLinks.find((l) => l.title === 'Chat with Agent')!;
161
+ const websiteIntegrationLink = agentLinks.find((l) => l.title === 'Website Integration')!;
162
+
44
163
  return (
45
- <main className="w-screen h-screen p-4">
46
- <h1 className="text-2xl font-bold p-4 border-b">{meta.fullname || agentName} Integration Code</h1>
47
- <p className="mt-4 mb-8 text-gray-600">
48
- Use the following code to integrate the <strong>{meta.fullname || agentName}</strong> agent into your
49
- React application using the <code>{'<PromptbookAgent />'}</code> component.
50
- </p>
51
-
52
- <CodePreview code={code} />
53
- <PromptbookAgent agentUrl={agentUrl} meta={meta} />
54
- </main>
164
+ <div
165
+ className="min-h-screen p-6 md:p-12 flex flex-col items-center"
166
+ style={{
167
+ backgroundColor,
168
+ }}
169
+ >
170
+ <div className="w-full max-w-5xl bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
171
+ {/* Header */}
172
+ <div className="p-6 border-b flex items-center gap-4" style={{ borderColor }}>
173
+ {agentProfile.meta.image && (
174
+ // eslint-disable-next-line @next/next/no-img-element
175
+ <img
176
+ src={agentProfile.meta.image as string}
177
+ alt={agentProfile.meta.fullname || agentName}
178
+ className="w-16 h-16 rounded-full object-cover border-2"
179
+ style={{ borderColor: primaryColor }}
180
+ />
181
+ )}
182
+ <div className="flex-1">
183
+ <h1 className="text-2xl font-bold text-gray-900">{agentProfile.meta.fullname || agentName}</h1>
184
+ <p className="text-gray-500 flex items-center gap-2">
185
+ <CodeIcon className="w-4 h-4" />
186
+ Integration Options
187
+ </p>
188
+ </div>
189
+ <Link
190
+ href={chatLink.href}
191
+ className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors"
192
+ title="Back to Agent"
193
+ >
194
+ <ArrowLeftIcon className="w-6 h-6" />
195
+ </Link>
196
+ </div>
197
+
198
+ <div className="p-6 space-y-6">
199
+ {/* Website Integration */}
200
+ <div className="p-6 rounded-xl border-2 border-green-200 bg-green-50/30 shadow-sm">
201
+ <div className="flex items-start gap-4 mb-4">
202
+ <div className="p-3 rounded-xl bg-green-100 text-green-600 shadow-sm">
203
+ <GlobeIcon className="w-6 h-6" />
204
+ </div>
205
+ <div>
206
+ <h2 className="text-xl font-bold text-gray-900">Website Integration</h2>
207
+ <p className="text-gray-600">
208
+ Embed the agent chat widget directly into your React application.
209
+ </p>
210
+ <p className="text-sm text-gray-500 mt-1">
211
+ <Link href={websiteIntegrationLink.href} className="text-blue-600 hover:underline">
212
+ View detailed guide &rarr;
213
+ </Link>
214
+ </p>
215
+ </div>
216
+ </div>
217
+ <CodePreview code={websiteIntegrationCode} language="typescript" />
218
+ </div>
219
+
220
+ {/* OpenAI API Compatible Endpoint */}
221
+ <div className="p-6 rounded-xl border-2 border-blue-200 bg-blue-50/30 shadow-sm">
222
+ <div className="flex items-start gap-4 mb-4">
223
+ <div className="p-3 rounded-xl bg-blue-100 text-blue-600 shadow-sm">
224
+ <TerminalIcon className="w-6 h-6" />
225
+ </div>
226
+ <div className="flex-1">
227
+ <h2 className="text-xl font-bold text-gray-900">OpenAI Compatible API</h2>
228
+ <p className="text-gray-600">
229
+ Use the agent as a drop-in replacement for OpenAI API in your existing applications.
230
+ </p>
231
+ <div className="grid md:grid-cols-3 gap-4 mt-4 mb-2">
232
+ <CopyField label="Endpoint URL" value={`${agentApiBase}/api/openai/v1`} />
233
+ <CopyField label="Model Name" value={`agent:${agentName}`} />
234
+ {isAdmin ? (
235
+ <CopyField label="API Key" value={apiKey} />
236
+ ) : (
237
+ <div className="flex flex-col gap-1">
238
+ <span className="text-xs font-medium text-gray-500 uppercase tracking-wider">
239
+ API Key
240
+ </span>
241
+ <div className="text-sm text-gray-500 italic bg-gray-50 p-2 rounded border border-gray-200">
242
+ Contact admin for API Key
243
+ </div>
244
+ </div>
245
+ )}
246
+ </div>
247
+ {isAdmin && apiKey === 'ptbk_...' && (
248
+ <p className="text-sm text-amber-600 mt-2">
249
+ No API token found.{' '}
250
+ <Link href="/admin/api-tokens" className="underline font-medium">
251
+ Create one in settings
252
+ </Link>
253
+ .
254
+ </p>
255
+ )}
256
+ </div>
257
+ </div>
258
+
259
+ <SdkCodeTabs curlCode={curlCode} pythonCode={pythonCode} jsCode={jsCode} />
260
+ </div>
261
+
262
+ {/* OpenRouter Integration */}
263
+ <div className="p-6 rounded-xl border-2 border-purple-200 bg-purple-50/30 shadow-sm">
264
+ <div className="flex items-start gap-4 mb-4">
265
+ <div className="p-3 rounded-xl bg-purple-100 text-purple-600 shadow-sm">
266
+ <ServerIcon className="w-6 h-6" />
267
+ </div>
268
+ <div>
269
+ <h2 className="text-xl font-bold text-gray-900">OpenRouter Integration</h2>
270
+ <p className="text-gray-600">Connect via OpenRouter compatible endpoint.</p>
271
+ <div className="grid md:grid-cols-2 gap-4 mt-4">
272
+ <CopyField label="Endpoint URL" value={`${agentApiBase}/api/openrouter`} />
273
+ <CopyField label="Model Name" value={`agent:${agentName}`} />
274
+ </div>
275
+ </div>
276
+ </div>
277
+ </div>
278
+
279
+ {/* MCP Integration */}
280
+ <div className="p-6 rounded-xl border-2 border-orange-200 bg-orange-50/30 shadow-sm">
281
+ <div className="flex items-start gap-4 mb-4">
282
+ <div className="p-3 rounded-xl bg-orange-100 text-orange-600 shadow-sm">
283
+ <BoxIcon className="w-6 h-6" />
284
+ </div>
285
+ <div>
286
+ <h2 className="text-xl font-bold text-gray-900">MCP Integration</h2>
287
+ <p className="text-gray-600">
288
+ Use Model Context Protocol to connect this agent with compatible tools and IDEs
289
+ (like Claude Desktop, Cursor, etc.).
290
+ </p>
291
+ </div>
292
+ </div>
293
+ <CodePreview
294
+ code={mcpConfigCode.replace(rootUrl + '/api', agentApiBase + '/api')}
295
+ language="json"
296
+ />
297
+ </div>
298
+ </div>
299
+ </div>
300
+ </div>
55
301
  );
56
302
  }
57
-
58
- /**
59
- * TODO: Make this page better, bring from Promptbook.Studio
60
- * TODO: [🚗] Components and pages here should be just tiny UI wraper around proper agent logic and conponents
61
- */