@promptbook/cli 0.104.0-1 → 0.104.0-10
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 +1 -3
- package/apps/agents-server/next.config.ts +2 -2
- package/apps/agents-server/package.json +7 -3
- package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
- package/apps/agents-server/public/swagger.json +115 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
- package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +47 -21
- package/apps/agents-server/src/app/actions.ts +22 -5
- package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +211 -0
- package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
- package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +294 -0
- package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
- package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
- package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
- package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
- package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
- package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
- package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
- package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
- package/apps/agents-server/src/app/api/agents/route.ts +28 -3
- package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
- package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
- package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
- package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
- package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
- package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
- package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
- package/apps/agents-server/src/app/api/messages/route.ts +102 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
- package/apps/agents-server/src/app/api/upload/route.ts +128 -45
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
- package/apps/agents-server/src/app/docs/page.tsx +12 -12
- package/apps/agents-server/src/app/globals.css +140 -33
- package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
- package/apps/agents-server/src/app/layout.tsx +27 -22
- package/apps/agents-server/src/app/page.tsx +54 -6
- package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
- package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
- package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
- package/apps/agents-server/src/app/security.txt/route.ts +1 -1
- package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
- package/apps/agents-server/src/app/swagger/page.tsx +14 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +41 -116
- package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
- package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
- package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
- package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
- package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
- package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
- package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
- package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
- package/apps/agents-server/src/components/Header/Header.tsx +114 -40
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +145 -23
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
- package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
- package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
- package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
- package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
- package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
- package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
- package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
- package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
- package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
- package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
- package/apps/agents-server/src/database/migrate.ts +34 -1
- package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
- package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
- package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
- package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
- package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
- package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
- package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
- package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
- package/apps/agents-server/src/database/schema.ts +231 -4
- package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
- package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
- package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
- package/apps/agents-server/src/message-providers/index.ts +13 -0
- package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
- package/apps/agents-server/src/middleware.ts +19 -23
- package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
- package/apps/agents-server/src/utils/auth.ts +117 -17
- package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
- package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
- package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
- package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
- package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
- package/esm/index.es.js +2890 -2737
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -0
- package/esm/typings/src/_packages/core.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +10 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
- package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
- package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
- package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +21 -11
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +80 -14
- package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
- package/esm/typings/src/types/Message.d.ts +49 -0
- package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
- package/esm/typings/src/types/typeAliases.d.ts +23 -1
- package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
- package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
- package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
- package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +4018 -3865
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/package-lock.json +0 -27
- package/apps/agents-server/public/fonts/download-font.js +0 -22
- package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
- package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
|
@@ -2,10 +2,11 @@ import { $getTableName } from '@/src/database/$getTableName';
|
|
|
2
2
|
import { $provideSupabaseForServer } from '@/src/database/$provideSupabaseForServer';
|
|
3
3
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
4
4
|
import { $provideOpenAiAssistantExecutionToolsForServer } from '@/src/tools/$provideOpenAiAssistantExecutionToolsForServer';
|
|
5
|
-
import { Agent, computeAgentHash, PROMPTBOOK_ENGINE_VERSION } from '@promptbook-local/core';
|
|
5
|
+
import { Agent, computeAgentHash, parseAgentSource, PROMPTBOOK_ENGINE_VERSION } from '@promptbook-local/core';
|
|
6
6
|
import { ChatMessage, ChatPromptResult, Prompt, string_book, TODO_any } from '@promptbook-local/types';
|
|
7
7
|
import { computeHash } from '@promptbook-local/utils';
|
|
8
8
|
import { NextRequest, NextResponse } from 'next/server';
|
|
9
|
+
import { isAgentDeleted } from '../app/agents/[agentName]/_utils';
|
|
9
10
|
import { validateApiKey } from './validateApiKey';
|
|
10
11
|
|
|
11
12
|
export async function handleChatCompletion(
|
|
@@ -60,6 +61,19 @@ export async function handleChatCompletion(
|
|
|
60
61
|
);
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
// Check if agent is deleted
|
|
65
|
+
if (await isAgentDeleted(agentName)) {
|
|
66
|
+
return NextResponse.json(
|
|
67
|
+
{
|
|
68
|
+
error: {
|
|
69
|
+
message: 'This agent has been deleted. You can restore it from the Recycle Bin.',
|
|
70
|
+
type: 'agent_deleted',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{ status: 410 }, // Gone - indicates the resource is no longer available
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
63
77
|
const collection = await $provideAgentCollectionForServer();
|
|
64
78
|
let agentSource: string_book;
|
|
65
79
|
try {
|
|
@@ -99,7 +113,54 @@ export async function handleChatCompletion(
|
|
|
99
113
|
);
|
|
100
114
|
}
|
|
101
115
|
|
|
102
|
-
const
|
|
116
|
+
const agentHash = computeAgentHash(agentSource);
|
|
117
|
+
const supabase = $provideSupabaseForServer();
|
|
118
|
+
const { data: assistantCache } = await supabase
|
|
119
|
+
.from(await $getTableName('OpenAiAssistantCache'))
|
|
120
|
+
.select('assistantId')
|
|
121
|
+
.eq('agentHash', agentHash)
|
|
122
|
+
.single();
|
|
123
|
+
|
|
124
|
+
let openAiAssistantExecutionTools = await $provideOpenAiAssistantExecutionToolsForServer();
|
|
125
|
+
|
|
126
|
+
if (assistantCache?.assistantId) {
|
|
127
|
+
console.log(
|
|
128
|
+
`[🐱🚀] Reusing assistant ${assistantCache.assistantId} for agent ${agentName} (hash: ${agentHash})`,
|
|
129
|
+
);
|
|
130
|
+
openAiAssistantExecutionTools = openAiAssistantExecutionTools.getAssistant(assistantCache.assistantId);
|
|
131
|
+
} else {
|
|
132
|
+
console.log(`[🐱🚀] Creating NEW assistant for agent ${agentName} (hash: ${agentHash})`);
|
|
133
|
+
// Parse to get instructions and name
|
|
134
|
+
const parsed = parseAgentSource(agentSource);
|
|
135
|
+
const name = parsed.agentName || agentName;
|
|
136
|
+
// Extract PERSONA
|
|
137
|
+
const baseInstructions = parsed.personaDescription || 'You are a helpful assistant.';
|
|
138
|
+
|
|
139
|
+
// Note: Append context to instructions
|
|
140
|
+
const contextLines = agentSource.split('\n').filter((line) => line.startsWith('CONTEXT '));
|
|
141
|
+
const contextInstructions = contextLines.join('\n');
|
|
142
|
+
const instructions = contextInstructions
|
|
143
|
+
? `${baseInstructions}\n\n${contextInstructions}`
|
|
144
|
+
: baseInstructions;
|
|
145
|
+
|
|
146
|
+
// Create assistant
|
|
147
|
+
const newAssistantTools = await openAiAssistantExecutionTools.createNewAssistant({
|
|
148
|
+
name,
|
|
149
|
+
instructions,
|
|
150
|
+
// knowledgeSources?
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Save to cache
|
|
154
|
+
const newAssistantId = newAssistantTools.assistantId;
|
|
155
|
+
if (newAssistantId) {
|
|
156
|
+
await supabase.from(await $getTableName('OpenAiAssistantCache')).insert({
|
|
157
|
+
agentHash,
|
|
158
|
+
assistantId: newAssistantId,
|
|
159
|
+
});
|
|
160
|
+
openAiAssistantExecutionTools = newAssistantTools;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
103
164
|
const agent = new Agent({
|
|
104
165
|
agentSource,
|
|
105
166
|
executionTools: {
|
|
@@ -108,7 +169,6 @@ export async function handleChatCompletion(
|
|
|
108
169
|
isVerbose: true, // or false
|
|
109
170
|
});
|
|
110
171
|
|
|
111
|
-
const agentHash = computeAgentHash(agentSource);
|
|
112
172
|
const userAgent = request.headers.get('user-agent');
|
|
113
173
|
const ip =
|
|
114
174
|
request.headers.get('x-forwarded-for') ||
|
|
@@ -125,8 +185,9 @@ export async function handleChatCompletion(
|
|
|
125
185
|
const previousMessages = threadMessages.slice(0, -1);
|
|
126
186
|
|
|
127
187
|
const thread: ChatMessage[] = previousMessages.map((msg: TODO_any, index: number) => ({
|
|
188
|
+
// channel: 'PROMPTBOOK_CHAT',
|
|
128
189
|
id: `msg-${index}`, // Placeholder ID
|
|
129
|
-
|
|
190
|
+
sender: msg.role === 'assistant' ? 'agent' : 'user', // Mapping standard OpenAI roles
|
|
130
191
|
content: msg.content,
|
|
131
192
|
isComplete: true,
|
|
132
193
|
date: new Date(), // We don't have the real date, using current
|
|
@@ -138,7 +199,6 @@ export async function handleChatCompletion(
|
|
|
138
199
|
content: lastMessage.content,
|
|
139
200
|
};
|
|
140
201
|
|
|
141
|
-
const supabase = $provideSupabaseForServer();
|
|
142
202
|
await supabase.from(await $getTableName('ChatHistory')).insert({
|
|
143
203
|
createdAt: new Date().toISOString(),
|
|
144
204
|
messageHash: computeHash(userMessageContent),
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { really_any } from '@promptbook-local/types';
|
|
2
|
+
import { serializeError } from '@promptbook-local/utils';
|
|
3
|
+
import { assertsError } from '../../../../../src/errors/assertsError';
|
|
4
|
+
import { $getTableName } from '../../database/$getTableName';
|
|
5
|
+
import { $provideSupabaseForServer } from '../../database/$provideSupabaseForServer';
|
|
6
|
+
import { EMAIL_PROVIDERS } from '../../message-providers';
|
|
7
|
+
import { OutboundEmail } from '../../message-providers/email/_common/Email';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sends a message
|
|
11
|
+
*/
|
|
12
|
+
export async function sendMessage(message: OutboundEmail): Promise<void> {
|
|
13
|
+
const supabase = await $provideSupabaseForServer();
|
|
14
|
+
|
|
15
|
+
// 1. Insert message
|
|
16
|
+
const { data: insertedMessage, error: insertError } = await supabase
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
.from(await $getTableName('Message'))
|
|
19
|
+
.insert({
|
|
20
|
+
channel: message.channel || 'UNKNOWN',
|
|
21
|
+
direction: message.direction || 'OUTBOUND',
|
|
22
|
+
sender: message.sender,
|
|
23
|
+
recipients: message.recipients,
|
|
24
|
+
content: message.content,
|
|
25
|
+
threadId: message.threadId,
|
|
26
|
+
metadata: message.metadata,
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
} as any)
|
|
29
|
+
.select()
|
|
30
|
+
.single();
|
|
31
|
+
|
|
32
|
+
if (insertError) {
|
|
33
|
+
throw new Error(`Failed to insert message: ${insertError.message}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!insertedMessage) {
|
|
37
|
+
throw new Error('Failed to insert message: No data returned');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. If outbound and email, try to send
|
|
41
|
+
if (message.direction === 'OUTBOUND' && message.channel === 'EMAIL') {
|
|
42
|
+
const providers = Object.keys(EMAIL_PROVIDERS);
|
|
43
|
+
|
|
44
|
+
if (providers.length === 0) {
|
|
45
|
+
console.warn('No email providers configured');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let isSent = false;
|
|
50
|
+
|
|
51
|
+
for (const providerName of providers) {
|
|
52
|
+
const provider = EMAIL_PROVIDERS[providerName];
|
|
53
|
+
let isSuccessful = false;
|
|
54
|
+
let raw: really_any = null;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
console.log(`📤 Sending email via ${providerName}`);
|
|
58
|
+
raw = await provider.send(message);
|
|
59
|
+
isSuccessful = true;
|
|
60
|
+
isSent = true;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
assertsError(error);
|
|
63
|
+
console.error(`Failed to send email via ${providerName}`, error);
|
|
64
|
+
raw = { error: serializeError(error) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 3. Log attempt
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
|
+
await supabase.from(await $getTableName('MessageSendAttempt')).insert({
|
|
70
|
+
// @ts-expect-error: insertedMessage is any
|
|
71
|
+
messageId: insertedMessage.id,
|
|
72
|
+
providerName,
|
|
73
|
+
isSuccessful,
|
|
74
|
+
raw,
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
} as any);
|
|
77
|
+
|
|
78
|
+
if (isSuccessful) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!isSent) {
|
|
84
|
+
throw new Error('Failed to send email via any provider');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* TODO: !!!! Move to `message-providers` and rename `message-providers` -> `messages`
|
|
91
|
+
*/
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Json } from '../database/schema';
|
|
2
|
+
|
|
3
|
+
export type MessageRow = {
|
|
4
|
+
id: number;
|
|
5
|
+
createdAt: string;
|
|
6
|
+
channel: string;
|
|
7
|
+
direction: string;
|
|
8
|
+
sender: Json;
|
|
9
|
+
recipients: Json;
|
|
10
|
+
content: string;
|
|
11
|
+
threadId: string | null;
|
|
12
|
+
metadata: Json;
|
|
13
|
+
// Joined fields
|
|
14
|
+
sendAttempts?: MessageSendAttemptRow[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type MessageSendAttemptRow = {
|
|
18
|
+
id: number;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
messageId: number;
|
|
21
|
+
providerName: string;
|
|
22
|
+
isSuccessful: boolean;
|
|
23
|
+
raw: Json;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type MessagesListResponse = {
|
|
27
|
+
items: MessageRow[];
|
|
28
|
+
total: number;
|
|
29
|
+
page: number;
|
|
30
|
+
pageSize: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type MessagesListParams = {
|
|
34
|
+
page?: number;
|
|
35
|
+
pageSize?: number;
|
|
36
|
+
search?: string;
|
|
37
|
+
channel?: string;
|
|
38
|
+
direction?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Build query string for messages listing.
|
|
43
|
+
*/
|
|
44
|
+
function buildQuery(params: MessagesListParams): string {
|
|
45
|
+
const searchParams = new URLSearchParams();
|
|
46
|
+
|
|
47
|
+
if (params.page && params.page > 0) searchParams.set('page', String(params.page));
|
|
48
|
+
if (params.pageSize && params.pageSize > 0) searchParams.set('pageSize', String(params.pageSize));
|
|
49
|
+
if (params.search) searchParams.set('search', params.search);
|
|
50
|
+
if (params.channel) searchParams.set('channel', params.channel);
|
|
51
|
+
if (params.direction) searchParams.set('direction', params.direction);
|
|
52
|
+
|
|
53
|
+
const qs = searchParams.toString();
|
|
54
|
+
return qs ? `?${qs}` : '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fetch messages from the admin API.
|
|
59
|
+
*/
|
|
60
|
+
export async function $fetchMessages(params: MessagesListParams = {}): Promise<MessagesListResponse> {
|
|
61
|
+
const qs = buildQuery(params);
|
|
62
|
+
const response = await fetch(`/api/messages${qs}`, {
|
|
63
|
+
method: 'GET',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const data = await response.json().catch(() => ({}));
|
|
68
|
+
throw new Error(data.error || 'Failed to load messages');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (await response.json()) as MessagesListResponse;
|
|
72
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { filenameToPrompt } from './filenameToPrompt';
|
|
3
|
+
|
|
4
|
+
describe('how filenameToPrompt works', () => {
|
|
5
|
+
it('will convert filename with dashes', () => {
|
|
6
|
+
expect(filenameToPrompt('cat-sitting-on-keyboard.png')).toEqual('Cat sitting on keyboard');
|
|
7
|
+
expect(filenameToPrompt('hello-world.jpg')).toEqual('Hello world');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('will convert filename with underscores', () => {
|
|
11
|
+
expect(filenameToPrompt('cat_sitting_on_keyboard.png')).toEqual('Cat sitting on keyboard');
|
|
12
|
+
expect(filenameToPrompt('hello_world.jpg')).toEqual('Hello world');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('will convert filename with mixed separators', () => {
|
|
16
|
+
expect(filenameToPrompt('cat-sitting_on-keyboard.png')).toEqual('Cat sitting on keyboard');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('will handle single word filename', () => {
|
|
20
|
+
expect(filenameToPrompt('cat.png')).toEqual('Cat');
|
|
21
|
+
expect(filenameToPrompt('HELLO.jpg')).toEqual('HELLO');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('will handle filename without extension', () => {
|
|
25
|
+
expect(filenameToPrompt('cat-sitting-on-keyboard')).toEqual('Cat sitting on keyboard');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('will handle filename with multiple dots', () => {
|
|
29
|
+
expect(filenameToPrompt('cat.sitting.on.keyboard.png')).toEqual('Cat.sitting.on.keyboard');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('will handle capitalized words after first word', () => {
|
|
33
|
+
expect(filenameToPrompt('Cat-Sitting-On-Keyboard.png')).toEqual('Cat sitting on keyboard');
|
|
34
|
+
expect(filenameToPrompt('HELLO-WORLD.png')).toEqual('HELLO world');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { capitalize } from '@promptbook/utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts a filename like "cat-sitting-on-keyboard.png" to a prompt like "Cat sitting on keyboard"
|
|
5
|
+
*
|
|
6
|
+
* @param filename - The filename to convert
|
|
7
|
+
* @returns The normalized prompt
|
|
8
|
+
*/
|
|
9
|
+
export function filenameToPrompt(filename: string): string {
|
|
10
|
+
// Remove file extension
|
|
11
|
+
const withoutExtension = filename.replace(/\.[^/.]+$/, '');
|
|
12
|
+
|
|
13
|
+
// Replace dashes and underscores with spaces
|
|
14
|
+
const withSpaces = withoutExtension.replace(/[-_]/g, ' ');
|
|
15
|
+
|
|
16
|
+
// Capitalize each word
|
|
17
|
+
const words = withSpaces.split(' ');
|
|
18
|
+
const capitalizedWords = words.map((word, index) => (index === 0 ? capitalize(word) : word.toLowerCase()));
|
|
19
|
+
|
|
20
|
+
return capitalizedWords.join(' ');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* TODO: [🧠][🏰] Make standard normalization function exported from `@promptbook/utils`
|
|
25
|
+
*/
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import { createClient } from '@supabase/supabase-js';
|
|
2
2
|
import { NextRequest } from 'next/server';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
// Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies
|
|
6
|
-
function normalizeTo_PascalCase(text: string): string {
|
|
7
|
-
return text
|
|
8
|
-
.replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => {
|
|
9
|
-
return word.toUpperCase();
|
|
10
|
-
})
|
|
11
|
-
.replace(/\s+/g, '');
|
|
12
|
-
}
|
|
3
|
+
import { $getTableName } from '../database/$getTableName';
|
|
13
4
|
|
|
14
5
|
export type ApiKeyValidationResult = {
|
|
15
6
|
isValid: boolean;
|
|
@@ -62,10 +53,14 @@ export async function validateApiKey(request: NextRequest): Promise<ApiKeyValida
|
|
|
62
53
|
};
|
|
63
54
|
}
|
|
64
55
|
|
|
56
|
+
/*
|
|
57
|
+
Note: [🐔] This code was commented out because results of it are unused
|
|
58
|
+
|
|
65
59
|
// Determine the table prefix based on the host
|
|
66
60
|
const host = request.headers.get('host');
|
|
67
61
|
let tablePrefix = SUPABASE_TABLE_PREFIX;
|
|
68
62
|
|
|
63
|
+
|
|
69
64
|
if (host && SERVERS && SERVERS.length > 0) {
|
|
70
65
|
if (SERVERS.some((server) => server === host)) {
|
|
71
66
|
let serverName = host;
|
|
@@ -74,6 +69,7 @@ export async function validateApiKey(request: NextRequest): Promise<ApiKeyValida
|
|
|
74
69
|
tablePrefix = `server_${serverName}_`;
|
|
75
70
|
}
|
|
76
71
|
}
|
|
72
|
+
*/
|
|
77
73
|
|
|
78
74
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
79
75
|
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
@@ -95,7 +91,7 @@ export async function validateApiKey(request: NextRequest): Promise<ApiKeyValida
|
|
|
95
91
|
});
|
|
96
92
|
|
|
97
93
|
const { data, error } = await supabase
|
|
98
|
-
.from(
|
|
94
|
+
.from(await $getTableName(`ApiTokens`))
|
|
99
95
|
.select('id, isRevoked')
|
|
100
96
|
.eq('token', token)
|
|
101
97
|
.single();
|