@promptbook/cli 0.103.0-53 → 0.103.0-55
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/config.ts +0 -2
- package/apps/agents-server/src/app/admin/api-tokens/ApiTokensClient.tsx +186 -0
- package/apps/agents-server/src/app/admin/api-tokens/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +79 -6
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +171 -69
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +10 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +203 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/route.ts +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/openai/chat/completions/route.ts +10 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/openrouter/chat/completions/route.ts +10 -0
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +218 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +24 -3
- package/apps/agents-server/src/app/api/api-tokens/route.ts +76 -0
- package/apps/agents-server/src/app/api/auth/change-password/route.ts +75 -0
- package/apps/agents-server/src/app/api/chat-feedback/export/route.ts +55 -0
- package/apps/agents-server/src/app/api/chat-history/export/route.ts +55 -0
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +1 -0
- package/apps/agents-server/src/app/docs/page.tsx +1 -0
- package/apps/agents-server/src/components/ChangePasswordDialog/ChangePasswordDialog.tsx +41 -0
- package/apps/agents-server/src/components/ChangePasswordForm/ChangePasswordForm.tsx +159 -0
- package/apps/agents-server/src/components/Header/Header.tsx +94 -33
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +2 -1
- package/apps/agents-server/src/database/migrations/2025-12-0010-llm-cache.sql +12 -0
- package/apps/agents-server/src/database/migrations/2025-12-0060-api-tokens.sql +13 -0
- package/apps/agents-server/src/database/schema.ts +51 -0
- package/apps/agents-server/src/middleware.ts +50 -2
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +3 -7
- package/apps/agents-server/src/tools/$provideExecutionToolsForServer.ts +10 -1
- package/apps/agents-server/src/utils/cache/SupabaseCacheStorage.ts +55 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +63 -0
- package/apps/agents-server/src/utils/convertToCsv.ts +31 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +183 -0
- package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +93 -0
- package/esm/index.es.js +846 -131
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/core.index.d.ts +8 -6
- package/esm/typings/src/_packages/types.index.d.ts +1 -1
- package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +4 -0
- package/esm/typings/src/commitments/ACTION/ACTION.d.ts +4 -0
- package/esm/typings/src/commitments/CLOSED/CLOSED.d.ts +35 -0
- package/esm/typings/src/commitments/COMPONENT/COMPONENT.d.ts +28 -0
- package/esm/typings/src/commitments/DELETE/DELETE.d.ts +4 -0
- package/esm/typings/src/commitments/FORMAT/FORMAT.d.ts +4 -0
- package/esm/typings/src/commitments/FROM/FROM.d.ts +34 -0
- package/esm/typings/src/commitments/GOAL/GOAL.d.ts +4 -0
- package/esm/typings/src/commitments/IMPORTANT/IMPORTANT.d.ts +26 -0
- package/esm/typings/src/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +4 -0
- package/esm/typings/src/commitments/LANGUAGE/LANGUAGE.d.ts +35 -0
- package/esm/typings/src/commitments/MEMORY/MEMORY.d.ts +4 -0
- package/esm/typings/src/commitments/MESSAGE/AgentMessageCommitmentDefinition.d.ts +4 -0
- package/esm/typings/src/commitments/MESSAGE/InitialMessageCommitmentDefinition.d.ts +4 -0
- package/esm/typings/src/commitments/MESSAGE/MESSAGE.d.ts +4 -0
- package/esm/typings/src/commitments/MESSAGE/UserMessageCommitmentDefinition.d.ts +4 -0
- package/esm/typings/src/commitments/META/META.d.ts +4 -0
- package/esm/typings/src/commitments/META_COLOR/META_COLOR.d.ts +4 -0
- package/esm/typings/src/commitments/META_IMAGE/META_IMAGE.d.ts +4 -0
- package/esm/typings/src/commitments/META_LINK/META_LINK.d.ts +4 -0
- package/esm/typings/src/commitments/MODEL/MODEL.d.ts +4 -0
- package/esm/typings/src/commitments/NOTE/NOTE.d.ts +4 -0
- package/esm/typings/src/commitments/OPEN/OPEN.d.ts +35 -0
- package/esm/typings/src/commitments/PERSONA/PERSONA.d.ts +4 -0
- package/esm/typings/src/commitments/RULE/RULE.d.ts +4 -0
- package/esm/typings/src/commitments/SAMPLE/SAMPLE.d.ts +4 -0
- package/esm/typings/src/commitments/SCENARIO/SCENARIO.d.ts +4 -0
- package/esm/typings/src/commitments/STYLE/STYLE.d.ts +4 -0
- package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +5 -0
- package/esm/typings/src/commitments/_base/CommitmentDefinition.d.ts +5 -0
- package/esm/typings/src/commitments/_base/NotYetImplementedCommitmentDefinition.d.ts +4 -0
- package/esm/typings/src/commitments/index.d.ts +1 -82
- package/esm/typings/src/commitments/registry.d.ts +68 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +3 -3
- package/umd/index.umd.js +846 -131
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/src/utils/cdn/classes/DigitalOceanSpaces.ts +0 -119
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
2
|
+
import { $provideExecutionToolsForServer } from '@/src/tools/$provideExecutionToolsForServer';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
5
|
+
import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { Agent } from '@promptbook-local/core';
|
|
7
|
+
import { ChatMessage, ChatPromptResult, Prompt, TODO_any } from '@promptbook-local/types';
|
|
8
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
// Global map to store active transports
|
|
12
|
+
// Note: This works in stateful environments or single-instance deployments.
|
|
13
|
+
// In serverless with multiple instances, this will fail if POST lands on a different instance.
|
|
14
|
+
// However, for standard deployments or sticky sessions, it works.
|
|
15
|
+
const sessions = new Map<string, SSENextJsTransport>();
|
|
16
|
+
|
|
17
|
+
class SSENextJsTransport implements Transport {
|
|
18
|
+
public onmessage?: (message: JSONRPCMessage) => void;
|
|
19
|
+
public onclose?: () => void;
|
|
20
|
+
public onError?: (error: Error) => void;
|
|
21
|
+
private controller: ReadableStreamDefaultController<TODO_any>;
|
|
22
|
+
private encoder = new TextEncoder();
|
|
23
|
+
public sessionId: string;
|
|
24
|
+
|
|
25
|
+
constructor(controller: ReadableStreamDefaultController<TODO_any>, sessionId: string) {
|
|
26
|
+
this.controller = controller;
|
|
27
|
+
this.sessionId = sessionId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async start(): Promise<void> {
|
|
31
|
+
// No-op for SSE
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async close(): Promise<void> {
|
|
35
|
+
this.onclose?.();
|
|
36
|
+
sessions.delete(this.sessionId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async send(message: JSONRPCMessage): Promise<void> {
|
|
40
|
+
const event = `event: message\ndata: ${JSON.stringify(message)}\n\n`;
|
|
41
|
+
this.controller.enqueue(this.encoder.encode(event));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async handlePostMessage(message: JSONRPCMessage): Promise<void> {
|
|
45
|
+
this.onmessage?.(message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
sendEndpointEvent(endpoint: string) {
|
|
49
|
+
const event = `event: endpoint\ndata: ${endpoint}\n\n`;
|
|
50
|
+
this.controller.enqueue(this.encoder.encode(event));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function GET(
|
|
55
|
+
request: NextRequest,
|
|
56
|
+
{ params }: { params: Promise<{ agentName: string }> },
|
|
57
|
+
) {
|
|
58
|
+
const { agentName } = await params;
|
|
59
|
+
|
|
60
|
+
// Check if agent exists
|
|
61
|
+
try {
|
|
62
|
+
const collection = await $provideAgentCollectionForServer();
|
|
63
|
+
await collection.getAgentSource(agentName);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return NextResponse.json({ error: 'Agent not found' }, { status: 404 });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const sessionId = crypto.randomUUID();
|
|
69
|
+
|
|
70
|
+
const stream = new ReadableStream({
|
|
71
|
+
start: async (controller) => {
|
|
72
|
+
const transport = new SSENextJsTransport(controller, sessionId);
|
|
73
|
+
sessions.set(sessionId, transport);
|
|
74
|
+
|
|
75
|
+
// Send endpoint event
|
|
76
|
+
// Construct the POST endpoint URL
|
|
77
|
+
// We assume the client can construct it or we send relative/absolute path
|
|
78
|
+
// The user wants: /agents/[agentName]/api/mcp
|
|
79
|
+
// We can send specific query param
|
|
80
|
+
const endpoint = `/agents/${agentName}/api/mcp?sessionId=${sessionId}`;
|
|
81
|
+
transport.sendEndpointEvent(endpoint);
|
|
82
|
+
|
|
83
|
+
// Initialize MCP Server
|
|
84
|
+
const server = new McpServer({
|
|
85
|
+
name: `Agent ${agentName}`,
|
|
86
|
+
version: '1.0.0',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Register Chat Tool
|
|
90
|
+
server.tool(
|
|
91
|
+
'chat',
|
|
92
|
+
{
|
|
93
|
+
messages: z.array(
|
|
94
|
+
z.object({
|
|
95
|
+
role: z.enum(['user', 'assistant', 'system']),
|
|
96
|
+
content: z.string(),
|
|
97
|
+
}),
|
|
98
|
+
),
|
|
99
|
+
model: z.string().optional(),
|
|
100
|
+
},
|
|
101
|
+
async ({ messages, model }) => {
|
|
102
|
+
try {
|
|
103
|
+
const collection = await $provideAgentCollectionForServer();
|
|
104
|
+
const agentSource = await collection.getAgentSource(agentName);
|
|
105
|
+
|
|
106
|
+
const executionTools = await $provideExecutionToolsForServer();
|
|
107
|
+
const agent = new Agent({
|
|
108
|
+
agentSource,
|
|
109
|
+
executionTools,
|
|
110
|
+
isVerbose: true,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Prepare thread and content
|
|
114
|
+
const lastMessage = messages[messages.length - 1];
|
|
115
|
+
const previousMessages = messages.slice(0, -1);
|
|
116
|
+
|
|
117
|
+
const thread: ChatMessage[] = previousMessages.map((msg: TODO_any, index: number) => ({
|
|
118
|
+
id: `msg-${index}`,
|
|
119
|
+
from: msg.role === 'assistant' ? 'agent' : 'user', // Mapping standard roles
|
|
120
|
+
content: msg.content,
|
|
121
|
+
isComplete: true,
|
|
122
|
+
date: new Date(),
|
|
123
|
+
}));
|
|
124
|
+
|
|
125
|
+
const prompt: Prompt = {
|
|
126
|
+
title: 'MCP Chat Completion',
|
|
127
|
+
content: lastMessage.content,
|
|
128
|
+
modelRequirements: {
|
|
129
|
+
modelVariant: 'CHAT',
|
|
130
|
+
},
|
|
131
|
+
parameters: {},
|
|
132
|
+
thread,
|
|
133
|
+
} as Prompt;
|
|
134
|
+
|
|
135
|
+
const result = await agent.callChatModel(prompt);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: 'text',
|
|
141
|
+
text: result.content,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
content: [
|
|
148
|
+
{
|
|
149
|
+
type: 'text',
|
|
150
|
+
text: `Error: ${(error as Error).message}`,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
isError: true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
await server.connect(transport);
|
|
160
|
+
|
|
161
|
+
// Handle connection close
|
|
162
|
+
// In ReadableStream, verify if there is a way to detect close from client side in Next.js?
|
|
163
|
+
// Usually if client disconnects, the stream might be cancelled.
|
|
164
|
+
// But we don't have a direct hook here unless we return logic in 'cancel'.
|
|
165
|
+
},
|
|
166
|
+
cancel() {
|
|
167
|
+
sessions.delete(sessionId);
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return new Response(stream, {
|
|
172
|
+
headers: {
|
|
173
|
+
'Content-Type': 'text/event-stream',
|
|
174
|
+
'Cache-Control': 'no-cache',
|
|
175
|
+
Connection: 'keep-alive',
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function POST(
|
|
181
|
+
request: NextRequest,
|
|
182
|
+
{ params }: { params: Promise<{ agentName: string }> },
|
|
183
|
+
) {
|
|
184
|
+
const { searchParams } = new URL(request.url);
|
|
185
|
+
const sessionId = searchParams.get('sessionId');
|
|
186
|
+
|
|
187
|
+
if (!sessionId) {
|
|
188
|
+
return NextResponse.json({ error: 'Session ID required' }, { status: 400 });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const transport = sessions.get(sessionId);
|
|
192
|
+
if (!transport) {
|
|
193
|
+
return NextResponse.json({ error: 'Session not found' }, { status: 404 });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const body = await request.json();
|
|
198
|
+
await transport.handlePostMessage(body);
|
|
199
|
+
return NextResponse.json({ success: true }); // Accepted
|
|
200
|
+
} catch (error) {
|
|
201
|
+
return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
2
|
+
import { resolveInheritedAgentSource } from '@/src/utils/resolveInheritedAgentSource';
|
|
2
3
|
import { createAgentModelRequirements } from '@promptbook-local/core';
|
|
3
4
|
import { serializeError } from '@promptbook-local/utils';
|
|
4
5
|
import { assertsError } from '../../../../../../../../src/errors/assertsError';
|
|
@@ -12,7 +13,8 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
|
|
|
12
13
|
try {
|
|
13
14
|
const collection = await $provideAgentCollectionForServer();
|
|
14
15
|
const agentSource = await collection.getAgentSource(agentName);
|
|
15
|
-
const
|
|
16
|
+
const effectiveAgentSource = await resolveInheritedAgentSource(agentSource, collection);
|
|
17
|
+
const modelRequirements = await createAgentModelRequirements(effectiveAgentSource);
|
|
16
18
|
|
|
17
19
|
return new Response(
|
|
18
20
|
JSON.stringify(
|
package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
2
|
+
import { resolveInheritedAgentSource } from '@/src/utils/resolveInheritedAgentSource';
|
|
2
3
|
import { createAgentModelRequirements } from '@promptbook-local/core';
|
|
3
4
|
import { serializeError } from '@promptbook-local/utils';
|
|
4
5
|
import { assertsError } from '../../../../../../../../../src/errors/assertsError';
|
|
@@ -12,7 +13,8 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
|
|
|
12
13
|
try {
|
|
13
14
|
const collection = await $provideAgentCollectionForServer();
|
|
14
15
|
const agentSource = await collection.getAgentSource(agentName);
|
|
15
|
-
const
|
|
16
|
+
const effectiveAgentSource = await resolveInheritedAgentSource(agentSource, collection);
|
|
17
|
+
const modelRequirements = await createAgentModelRequirements(effectiveAgentSource);
|
|
16
18
|
const { systemMessage } = modelRequirements;
|
|
17
19
|
|
|
18
20
|
return new Response(systemMessage, {
|
|
@@ -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 }, 'OpenAI API Chat Completion');
|
|
10
|
+
}
|
package/apps/agents-server/src/app/agents/[agentName]/api/openrouter/chat/completions/route.ts
ADDED
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { $provideServer } from '@/src/tools/$provideServer';
|
|
4
|
+
import { Color } from '../../../../../../../src/utils/color/Color';
|
|
5
|
+
import { withAlpha } from '../../../../../../../src/utils/color/operators/withAlpha';
|
|
6
|
+
import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
|
|
7
|
+
import { ArrowLeftIcon, BookOpenIcon, CodeIcon, HistoryIcon, HomeIcon, LinkIcon, MessageSquareIcon, ShareIcon } from 'lucide-react';
|
|
8
|
+
import { headers } from 'next/headers';
|
|
9
|
+
import Link from 'next/link';
|
|
10
|
+
import { notFound } from 'next/navigation';
|
|
11
|
+
import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
|
|
12
|
+
import { CopyField } from '../CopyField';
|
|
13
|
+
import { getAgentName, getAgentProfile } from '../_utils';
|
|
14
|
+
import { generateAgentMetadata } from '../generateAgentMetadata';
|
|
15
|
+
|
|
16
|
+
export const generateMetadata = generateAgentMetadata;
|
|
17
|
+
|
|
18
|
+
export default async function AgentLinksPage({
|
|
19
|
+
params,
|
|
20
|
+
}: {
|
|
21
|
+
params: Promise<{ agentName: string }>;
|
|
22
|
+
}) {
|
|
23
|
+
$sideEffect(headers());
|
|
24
|
+
const agentName = await getAgentName(params);
|
|
25
|
+
|
|
26
|
+
let agentProfile;
|
|
27
|
+
try {
|
|
28
|
+
agentProfile = await getAgentProfile(agentName);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (
|
|
31
|
+
error instanceof Error &&
|
|
32
|
+
(error.message.includes('Cannot coerce the result to a single JSON object') ||
|
|
33
|
+
error.message.includes('JSON object requested, multiple (or no) results returned'))
|
|
34
|
+
) {
|
|
35
|
+
notFound();
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const { publicUrl } = await $provideServer();
|
|
41
|
+
const baseUrl = `${publicUrl.href}agents/${encodeURIComponent(agentName)}`;
|
|
42
|
+
|
|
43
|
+
// Extract brand color from meta
|
|
44
|
+
const brandColor = Color.from(agentProfile.meta.color || PROMPTBOOK_COLOR);
|
|
45
|
+
const backgroundColor = (await brandColor.then(withAlpha(0.05))).toHex();
|
|
46
|
+
const borderColor = (await brandColor.then(withAlpha(0.1))).toHex();
|
|
47
|
+
const primaryColor = (await brandColor).toHex();
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
className="min-h-screen p-6 md:p-12 flex flex-col items-center"
|
|
52
|
+
style={{
|
|
53
|
+
backgroundColor,
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
<div className="w-full max-w-3xl bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
57
|
+
|
|
58
|
+
{/* Header */}
|
|
59
|
+
<div
|
|
60
|
+
className="p-6 border-b flex items-center gap-4"
|
|
61
|
+
style={{ borderColor }}
|
|
62
|
+
>
|
|
63
|
+
{agentProfile.meta.image && (
|
|
64
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
65
|
+
<img
|
|
66
|
+
src={agentProfile.meta.image as string}
|
|
67
|
+
alt={agentProfile.meta.fullname || agentName}
|
|
68
|
+
className="w-16 h-16 rounded-full object-cover border-2"
|
|
69
|
+
style={{ borderColor: primaryColor }}
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
72
|
+
<div className="flex-1">
|
|
73
|
+
<h1 className="text-2xl font-bold text-gray-900">
|
|
74
|
+
{agentProfile.meta.fullname || agentName}
|
|
75
|
+
</h1>
|
|
76
|
+
<p className="text-gray-500 flex items-center gap-2">
|
|
77
|
+
<LinkIcon className="w-4 h-4" />
|
|
78
|
+
Signpost & Links
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
<Link
|
|
82
|
+
href={`/agents/${encodeURIComponent(agentName)}`}
|
|
83
|
+
className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors"
|
|
84
|
+
title="Back to Agent"
|
|
85
|
+
>
|
|
86
|
+
<ArrowLeftIcon className="w-6 h-6" />
|
|
87
|
+
</Link>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div className="divide-y divide-gray-100">
|
|
91
|
+
|
|
92
|
+
{/* API Endpoints */}
|
|
93
|
+
<div className="p-6">
|
|
94
|
+
<h2 className="text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2">
|
|
95
|
+
<CodeIcon className="w-5 h-5 text-gray-500" />
|
|
96
|
+
API Endpoints
|
|
97
|
+
</h2>
|
|
98
|
+
<div className="grid gap-4">
|
|
99
|
+
<div>
|
|
100
|
+
<h3 className="text-sm font-medium text-gray-700 mb-1">OpenAI Compatible Chat Completion</h3>
|
|
101
|
+
<p className="text-xs text-gray-500 mb-2">Standard OpenAI API endpoint for chat completions.</p>
|
|
102
|
+
<CopyField label="Endpoint URL" value={`${baseUrl}/api/openai/chat/completions`} />
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div>
|
|
106
|
+
<h3 className="text-sm font-medium text-gray-700 mb-1">OpenRouter Compatible</h3>
|
|
107
|
+
<p className="text-xs text-gray-500 mb-2">Endpoint compatible with OpenRouter API format.</p>
|
|
108
|
+
<CopyField label="Endpoint URL" value={`${baseUrl}/api/openrouter/chat/completions`} />
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div>
|
|
112
|
+
<h3 className="text-sm font-medium text-gray-700 mb-1">Model Context Protocol (MCP)</h3>
|
|
113
|
+
<p className="text-xs text-gray-500 mb-2">Endpoint for Model Context Protocol integration.</p>
|
|
114
|
+
<CopyField label="Endpoint URL" value={`${baseUrl}/api/mcp`} />
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div>
|
|
118
|
+
<h3 className="text-sm font-medium text-gray-700 mb-1">Model Requirements</h3>
|
|
119
|
+
<p className="text-xs text-gray-500 mb-2">Get requirements and capabilities of the model.</p>
|
|
120
|
+
<CopyField label="Endpoint URL" value={`${baseUrl}/api/modelRequirements`} />
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
{/* Agent Resources */}
|
|
126
|
+
<div className="p-6">
|
|
127
|
+
<h2 className="text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2">
|
|
128
|
+
<HomeIcon className="w-5 h-5 text-gray-500" />
|
|
129
|
+
Agent Resources
|
|
130
|
+
</h2>
|
|
131
|
+
<div className="grid md:grid-cols-2 gap-4">
|
|
132
|
+
<Link
|
|
133
|
+
href={`/agents/${encodeURIComponent(agentName)}`}
|
|
134
|
+
className="block p-4 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-md transition-all group bg-white"
|
|
135
|
+
>
|
|
136
|
+
<div className="flex items-center gap-3 mb-2">
|
|
137
|
+
<div className="p-2 rounded-lg bg-blue-50 text-blue-600 group-hover:bg-blue-100 transition-colors">
|
|
138
|
+
<MessageSquareIcon className="w-5 h-5" />
|
|
139
|
+
</div>
|
|
140
|
+
<span className="font-medium text-gray-900">Chat with Agent</span>
|
|
141
|
+
</div>
|
|
142
|
+
<p className="text-sm text-gray-500">
|
|
143
|
+
Direct interface to converse with {agentProfile.meta.fullname || agentName}.
|
|
144
|
+
</p>
|
|
145
|
+
</Link>
|
|
146
|
+
|
|
147
|
+
<Link
|
|
148
|
+
href={`/agents/${encodeURIComponent(agentName)}/history`}
|
|
149
|
+
className="block p-4 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-md transition-all group bg-white"
|
|
150
|
+
>
|
|
151
|
+
<div className="flex items-center gap-3 mb-2">
|
|
152
|
+
<div className="p-2 rounded-lg bg-purple-50 text-purple-600 group-hover:bg-purple-100 transition-colors">
|
|
153
|
+
<HistoryIcon className="w-5 h-5" />
|
|
154
|
+
</div>
|
|
155
|
+
<span className="font-medium text-gray-900">History & Feedback</span>
|
|
156
|
+
</div>
|
|
157
|
+
<p className="text-sm text-gray-500">
|
|
158
|
+
View past conversations and provide feedback.
|
|
159
|
+
</p>
|
|
160
|
+
</Link>
|
|
161
|
+
|
|
162
|
+
<Link
|
|
163
|
+
href={`/agents/${encodeURIComponent(agentName)}/integration`}
|
|
164
|
+
className="block p-4 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-md transition-all group bg-white"
|
|
165
|
+
>
|
|
166
|
+
<div className="flex items-center gap-3 mb-2">
|
|
167
|
+
<div className="p-2 rounded-lg bg-green-50 text-green-600 group-hover:bg-green-100 transition-colors">
|
|
168
|
+
<CodeIcon className="w-5 h-5" />
|
|
169
|
+
</div>
|
|
170
|
+
<span className="font-medium text-gray-900">Integration Guide</span>
|
|
171
|
+
</div>
|
|
172
|
+
<p className="text-sm text-gray-500">
|
|
173
|
+
Learn how to integrate this agent into your applications.
|
|
174
|
+
</p>
|
|
175
|
+
</Link>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
{/* Ecosystem */}
|
|
180
|
+
<div className="p-6 bg-gray-50">
|
|
181
|
+
<h2 className="text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2">
|
|
182
|
+
<ShareIcon className="w-5 h-5 text-gray-500" />
|
|
183
|
+
Promptbook Ecosystem
|
|
184
|
+
</h2>
|
|
185
|
+
<div className="grid md:grid-cols-2 gap-4">
|
|
186
|
+
<a
|
|
187
|
+
href="https://promptbook.studio"
|
|
188
|
+
target="_blank"
|
|
189
|
+
rel="noopener noreferrer"
|
|
190
|
+
className="flex items-center gap-3 p-3 rounded-lg bg-white border border-gray-200 hover:border-gray-300 transition-colors"
|
|
191
|
+
>
|
|
192
|
+
<BookOpenIcon className="w-5 h-5 text-gray-400" />
|
|
193
|
+
<div>
|
|
194
|
+
<div className="font-medium text-gray-900">Promptbook Studio</div>
|
|
195
|
+
<div className="text-xs text-gray-500">Create and manage your own agents</div>
|
|
196
|
+
</div>
|
|
197
|
+
</a>
|
|
198
|
+
|
|
199
|
+
<a
|
|
200
|
+
href="https://github.com/webgptorg/promptbook"
|
|
201
|
+
target="_blank"
|
|
202
|
+
rel="noopener noreferrer"
|
|
203
|
+
className="flex items-center gap-3 p-3 rounded-lg bg-white border border-gray-200 hover:border-gray-300 transition-colors"
|
|
204
|
+
>
|
|
205
|
+
<CodeIcon className="w-5 h-5 text-gray-400" />
|
|
206
|
+
<div>
|
|
207
|
+
<div className="font-medium text-gray-900">GitHub Repository</div>
|
|
208
|
+
<div className="text-xs text-gray-500">Star us and contribute to the project</div>
|
|
209
|
+
</div>
|
|
210
|
+
</a>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// import { BookEditor } from '@promptbook-local/components';
|
|
4
4
|
import { $provideServer } from '@/src/tools/$provideServer';
|
|
5
5
|
import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
|
|
6
|
-
import { CodeIcon, HistoryIcon, NotebookPenIcon } from 'lucide-react';
|
|
6
|
+
import { CodeIcon, HistoryIcon, MessageCircleQuestionIcon, MessageSquareIcon, NotebookPenIcon } from 'lucide-react';
|
|
7
7
|
import { headers } from 'next/headers';
|
|
8
8
|
import { notFound } from 'next/navigation';
|
|
9
9
|
import { Color } from '../../../../../../src/utils/color/Color';
|
|
@@ -26,13 +26,20 @@ import { isUserAdmin } from '../../../utils/isUserAdmin';
|
|
|
26
26
|
|
|
27
27
|
export const generateMetadata = generateAgentMetadata;
|
|
28
28
|
|
|
29
|
-
export default async function AgentPage({
|
|
29
|
+
export default async function AgentPage({
|
|
30
|
+
params,
|
|
31
|
+
searchParams,
|
|
32
|
+
}: {
|
|
33
|
+
params: Promise<{ agentName: string }>;
|
|
34
|
+
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
|
35
|
+
}) {
|
|
30
36
|
// const [apiKey, setApiKey] = useStateInLocalStorage<string>('openai-apiKey', () => '');
|
|
31
37
|
// const [isApiKeyVisible, setIsApiKeyVisible] = useState(false);
|
|
32
38
|
// const [isApiKeySectionCollapsed, setIsApiKeySectionCollapsed] = useState(!!apiKey);
|
|
33
39
|
|
|
34
40
|
$sideEffect(headers());
|
|
35
41
|
|
|
42
|
+
const { message } = await searchParams;
|
|
36
43
|
const agentName = await getAgentName(params);
|
|
37
44
|
const isAdmin = await isUserAdmin();
|
|
38
45
|
|
|
@@ -155,6 +162,20 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
155
162
|
Maintenance
|
|
156
163
|
</h2>
|
|
157
164
|
<div className="flex flex-col gap-2">
|
|
165
|
+
<a
|
|
166
|
+
href={`/admin/chat-history?agentName=${encodeURIComponent(agentName)}`}
|
|
167
|
+
className="inline-flex items-center justify-center whitespace-nowrap rounded-md border border-gray-300 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 shadow-sm hover:bg-gray-50"
|
|
168
|
+
>
|
|
169
|
+
<MessageSquareIcon className="mr-2 w-3 h-3" />
|
|
170
|
+
Chat history
|
|
171
|
+
</a>
|
|
172
|
+
<a
|
|
173
|
+
href={`/admin/chat-feedback?agentName=${encodeURIComponent(agentName)}`}
|
|
174
|
+
className="inline-flex items-center justify-center whitespace-nowrap rounded-md border border-gray-300 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 shadow-sm hover:bg-gray-50"
|
|
175
|
+
>
|
|
176
|
+
<MessageCircleQuestionIcon className="mr-2 w-3 h-3" />
|
|
177
|
+
Chat feedback
|
|
178
|
+
</a>
|
|
158
179
|
<ClearAgentChatHistoryButton agentName={agentName} />
|
|
159
180
|
<ClearAgentChatFeedbackButton agentName={agentName} />
|
|
160
181
|
</div>
|
|
@@ -181,7 +202,7 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
181
202
|
|
|
182
203
|
{/* Main content: Chat */}
|
|
183
204
|
<div className="flex-1 relative h-full bg-white">
|
|
184
|
-
<AgentChatWrapper agentUrl={agentUrl} />
|
|
205
|
+
<AgentChatWrapper agentUrl={agentUrl} defaultMessage={message as string} />
|
|
185
206
|
</div>
|
|
186
207
|
</div>
|
|
187
208
|
);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { $getTableName } from '../../../database/$getTableName';
|
|
2
|
+
import { $provideSupabase } from '../../../database/$provideSupabase';
|
|
3
|
+
import { isUserAdmin } from '../../../utils/isUserAdmin';
|
|
4
|
+
import { randomUUID } from 'crypto';
|
|
5
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
6
|
+
|
|
7
|
+
export async function GET(request: NextRequest) {
|
|
8
|
+
if (!(await isUserAdmin())) {
|
|
9
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const supabase = $provideSupabase();
|
|
13
|
+
const table = await $getTableName('ApiTokens');
|
|
14
|
+
|
|
15
|
+
const { data, error } = await supabase.from(table).select('*').order('createdAt', { ascending: false });
|
|
16
|
+
|
|
17
|
+
if (error) {
|
|
18
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return NextResponse.json(data);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function POST(request: NextRequest) {
|
|
25
|
+
if (!(await isUserAdmin())) {
|
|
26
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const body = await request.json();
|
|
31
|
+
const { note } = body;
|
|
32
|
+
|
|
33
|
+
const token = `ptbk_${randomUUID().replace(/-/g, '')}`;
|
|
34
|
+
|
|
35
|
+
const supabase = $provideSupabase();
|
|
36
|
+
const table = await $getTableName('ApiTokens');
|
|
37
|
+
|
|
38
|
+
const { data, error } = await supabase
|
|
39
|
+
.from(table)
|
|
40
|
+
.insert({ token, note })
|
|
41
|
+
.select()
|
|
42
|
+
.single();
|
|
43
|
+
|
|
44
|
+
if (error) {
|
|
45
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return NextResponse.json(data);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return NextResponse.json({ error: 'Invalid request body' }, { status: 400 });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function DELETE(request: NextRequest) {
|
|
55
|
+
if (!(await isUserAdmin())) {
|
|
56
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const searchParams = request.nextUrl.searchParams;
|
|
60
|
+
const id = searchParams.get('id');
|
|
61
|
+
|
|
62
|
+
if (!id) {
|
|
63
|
+
return NextResponse.json({ error: 'ID is required' }, { status: 400 });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const supabase = $provideSupabase();
|
|
67
|
+
const table = await $getTableName('ApiTokens');
|
|
68
|
+
|
|
69
|
+
const { error } = await supabase.from(table).delete().eq('id', parseInt(id, 10));
|
|
70
|
+
|
|
71
|
+
if (error) {
|
|
72
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return NextResponse.json({ success: true });
|
|
76
|
+
}
|