@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.
- package/apps/agents-server/next.config.ts +2 -2
- package/apps/agents-server/package.json +6 -1
- package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +50 -0
- package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +4 -3
- package/apps/agents-server/src/app/actions.ts +17 -5
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +15 -11
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -7
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +32 -2
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +2 -0
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +18 -0
- 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/chat/route.ts +17 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +1 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +1 -1
- 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 +66 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +211 -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]/integration/WebsiteIntegrationTabs.tsx +26 -0
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +23 -6
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +12 -6
- package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +87 -0
- package/apps/agents-server/src/app/agents/[agentName]/website-integration/page.tsx +35 -18
- package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +19 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +42 -0
- package/apps/agents-server/src/app/api/agents/route.ts +29 -4
- package/apps/agents-server/src/app/api/docs/book.md/route.ts +58 -0
- package/apps/agents-server/src/app/api/embed.js/route.ts +87 -67
- 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/upload/route.ts +119 -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/embed/layout.tsx +31 -0
- package/apps/agents-server/src/app/embed/page.tsx +22 -9
- package/apps/agents-server/src/app/globals.css +140 -33
- package/apps/agents-server/src/app/layout.tsx +27 -22
- package/apps/agents-server/src/app/page.tsx +50 -4
- package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
- package/apps/agents-server/src/app/recycle-bin/page.tsx +25 -41
- package/apps/agents-server/src/app/sitemap.xml/route.ts +6 -3
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +6 -97
- package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
- 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 +79 -35
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +85 -20
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +72 -12
- package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +50 -0
- 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/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/database/metadataDefaults.ts +19 -1
- 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-0640-openai-assistant-cache.sql +12 -0
- package/apps/agents-server/src/database/schema.ts +109 -0
- package/apps/agents-server/src/generated/reservedPaths.ts +27 -0
- package/apps/agents-server/src/middleware.ts +7 -20
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +6 -1
- 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/getUserIdFromRequest.ts +33 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +60 -4
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +21 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +2 -1
- package/esm/index.es.js +140 -27
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/types.index.d.ts +6 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
- 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/_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 +13 -7
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +6 -0
- 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/ollama/OllamaExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
- package/esm/typings/src/types/typeAliases.d.ts +12 -0
- 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 +146 -33
- 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
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "prefix_OpenAiAssistantCache" (
|
|
2
|
+
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
3
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
4
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
5
|
+
|
|
6
|
+
"agentHash" TEXT NOT NULL,
|
|
7
|
+
"assistantId" TEXT NOT NULL
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "prefix_OpenAiAssistantCache_agentHash_idx" ON "prefix_OpenAiAssistantCache" ("agentHash");
|
|
11
|
+
|
|
12
|
+
ALTER TABLE "prefix_OpenAiAssistantCache" ENABLE ROW LEVEL SECURITY;
|
|
@@ -47,6 +47,7 @@ export type AgentsServerDatabase = {
|
|
|
47
47
|
agentName: string;
|
|
48
48
|
createdAt: string;
|
|
49
49
|
updatedAt: string | null;
|
|
50
|
+
permanentId: string | null;
|
|
50
51
|
agentHash: string;
|
|
51
52
|
agentSource: string;
|
|
52
53
|
agentProfile: Json;
|
|
@@ -54,12 +55,15 @@ export type AgentsServerDatabase = {
|
|
|
54
55
|
usage: Json | null;
|
|
55
56
|
preparedModelRequirements: Json | null;
|
|
56
57
|
preparedExternals: Json | null;
|
|
58
|
+
deletedAt: string | null;
|
|
59
|
+
visibility: 'PUBLIC' | 'PRIVATE';
|
|
57
60
|
};
|
|
58
61
|
Insert: {
|
|
59
62
|
id?: number;
|
|
60
63
|
agentName: string;
|
|
61
64
|
createdAt: string;
|
|
62
65
|
updatedAt?: string | null;
|
|
66
|
+
permanentId?: string | null;
|
|
63
67
|
agentHash: string;
|
|
64
68
|
agentSource: string;
|
|
65
69
|
agentProfile: Json;
|
|
@@ -67,12 +71,15 @@ export type AgentsServerDatabase = {
|
|
|
67
71
|
usage?: Json | null;
|
|
68
72
|
preparedModelRequirements?: Json | null;
|
|
69
73
|
preparedExternals?: Json | null;
|
|
74
|
+
deletedAt?: string | null;
|
|
75
|
+
visibility?: 'PUBLIC' | 'PRIVATE';
|
|
70
76
|
};
|
|
71
77
|
Update: {
|
|
72
78
|
id?: number;
|
|
73
79
|
agentName?: string;
|
|
74
80
|
createdAt?: string;
|
|
75
81
|
updatedAt?: string | null;
|
|
82
|
+
permanentId?: string | null;
|
|
76
83
|
agentHash?: string;
|
|
77
84
|
agentSource?: string;
|
|
78
85
|
agentProfile?: Json;
|
|
@@ -80,6 +87,8 @@ export type AgentsServerDatabase = {
|
|
|
80
87
|
usage?: Json | null;
|
|
81
88
|
preparedModelRequirements?: Json | null;
|
|
82
89
|
preparedExternals?: Json | null;
|
|
90
|
+
deletedAt?: string | null;
|
|
91
|
+
visibility?: 'PUBLIC' | 'PRIVATE';
|
|
83
92
|
};
|
|
84
93
|
Relationships: [];
|
|
85
94
|
};
|
|
@@ -272,6 +281,30 @@ export type AgentsServerDatabase = {
|
|
|
272
281
|
};
|
|
273
282
|
Relationships: [];
|
|
274
283
|
};
|
|
284
|
+
OpenAiAssistantCache: {
|
|
285
|
+
Row: {
|
|
286
|
+
id: number;
|
|
287
|
+
createdAt: string;
|
|
288
|
+
updatedAt: string;
|
|
289
|
+
agentHash: string;
|
|
290
|
+
assistantId: string;
|
|
291
|
+
};
|
|
292
|
+
Insert: {
|
|
293
|
+
id?: number;
|
|
294
|
+
createdAt?: string;
|
|
295
|
+
updatedAt?: string;
|
|
296
|
+
agentHash: string;
|
|
297
|
+
assistantId: string;
|
|
298
|
+
};
|
|
299
|
+
Update: {
|
|
300
|
+
id?: number;
|
|
301
|
+
createdAt?: string;
|
|
302
|
+
updatedAt?: string;
|
|
303
|
+
agentHash?: string;
|
|
304
|
+
assistantId?: string;
|
|
305
|
+
};
|
|
306
|
+
Relationships: [];
|
|
307
|
+
};
|
|
275
308
|
ApiTokens: {
|
|
276
309
|
Row: {
|
|
277
310
|
id: number;
|
|
@@ -299,6 +332,82 @@ export type AgentsServerDatabase = {
|
|
|
299
332
|
};
|
|
300
333
|
Relationships: [];
|
|
301
334
|
};
|
|
335
|
+
Image: {
|
|
336
|
+
Row: {
|
|
337
|
+
id: number;
|
|
338
|
+
createdAt: string;
|
|
339
|
+
updatedAt: string;
|
|
340
|
+
filename: string;
|
|
341
|
+
prompt: string;
|
|
342
|
+
cdnUrl: string;
|
|
343
|
+
cdnKey: string;
|
|
344
|
+
};
|
|
345
|
+
Insert: {
|
|
346
|
+
id?: number;
|
|
347
|
+
createdAt?: string;
|
|
348
|
+
updatedAt?: string;
|
|
349
|
+
filename: string;
|
|
350
|
+
prompt: string;
|
|
351
|
+
cdnUrl: string;
|
|
352
|
+
cdnKey: string;
|
|
353
|
+
};
|
|
354
|
+
Update: {
|
|
355
|
+
id?: number;
|
|
356
|
+
createdAt?: string;
|
|
357
|
+
updatedAt?: string;
|
|
358
|
+
filename?: string;
|
|
359
|
+
prompt?: string;
|
|
360
|
+
cdnUrl?: string;
|
|
361
|
+
cdnKey?: string;
|
|
362
|
+
};
|
|
363
|
+
Relationships: [];
|
|
364
|
+
};
|
|
365
|
+
File: {
|
|
366
|
+
Row: {
|
|
367
|
+
id: number;
|
|
368
|
+
createdAt: string;
|
|
369
|
+
userId: number | null;
|
|
370
|
+
fileName: string;
|
|
371
|
+
fileSize: number;
|
|
372
|
+
fileType: string;
|
|
373
|
+
storageUrl: string | null;
|
|
374
|
+
shortUrl: string | null;
|
|
375
|
+
purpose: string;
|
|
376
|
+
status: 'UPLOADING' | 'COMPLETED' | 'FAILED';
|
|
377
|
+
};
|
|
378
|
+
Insert: {
|
|
379
|
+
id?: number;
|
|
380
|
+
createdAt?: string;
|
|
381
|
+
userId?: number | null;
|
|
382
|
+
fileName: string;
|
|
383
|
+
fileSize: number;
|
|
384
|
+
fileType: string;
|
|
385
|
+
storageUrl?: string | null;
|
|
386
|
+
shortUrl?: string | null;
|
|
387
|
+
purpose: string;
|
|
388
|
+
status?: 'UPLOADING' | 'COMPLETED' | 'FAILED';
|
|
389
|
+
};
|
|
390
|
+
Update: {
|
|
391
|
+
id?: number;
|
|
392
|
+
createdAt?: string;
|
|
393
|
+
userId?: number | null;
|
|
394
|
+
fileName?: string;
|
|
395
|
+
fileSize?: number;
|
|
396
|
+
fileType?: string;
|
|
397
|
+
storageUrl?: string | null;
|
|
398
|
+
shortUrl?: string | null;
|
|
399
|
+
purpose?: string;
|
|
400
|
+
status?: 'UPLOADING' | 'COMPLETED' | 'FAILED';
|
|
401
|
+
};
|
|
402
|
+
Relationships: [
|
|
403
|
+
{
|
|
404
|
+
foreignKeyName: 'File_userId_fkey';
|
|
405
|
+
columns: ['userId'];
|
|
406
|
+
referencedRelation: 'User';
|
|
407
|
+
referencedColumns: ['id'];
|
|
408
|
+
},
|
|
409
|
+
];
|
|
410
|
+
};
|
|
302
411
|
};
|
|
303
412
|
Views: Record<string, never>;
|
|
304
413
|
Functions: Record<string, never>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reserved paths that should not be treated as agent names.
|
|
3
|
+
* This file is auto-generated by scripts/generate-reserved-paths.js
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
|
|
6
|
+
*
|
|
7
|
+
* @see /apps/agents-server/src/app - source directory
|
|
8
|
+
* @see /apps/agents-server/src/middleware.ts - where this is used
|
|
9
|
+
*/
|
|
10
|
+
export const RESERVED_PATHS: readonly string[] = [
|
|
11
|
+
"_next",
|
|
12
|
+
"admin",
|
|
13
|
+
"agents",
|
|
14
|
+
"api",
|
|
15
|
+
"docs",
|
|
16
|
+
"embed",
|
|
17
|
+
"favicon.ico",
|
|
18
|
+
"humans.txt",
|
|
19
|
+
"manifest.webmanifest",
|
|
20
|
+
"recycle-bin",
|
|
21
|
+
"restricted",
|
|
22
|
+
"robots.txt",
|
|
23
|
+
"security.txt",
|
|
24
|
+
"sitemap.xml",
|
|
25
|
+
"sw.js",
|
|
26
|
+
"test"
|
|
27
|
+
] as const;
|
|
@@ -2,9 +2,11 @@ import { TODO_any } from '@promptbook-local/types';
|
|
|
2
2
|
import { createClient } from '@supabase/supabase-js';
|
|
3
3
|
import { NextRequest, NextResponse } from 'next/server';
|
|
4
4
|
import { SERVERS, SUPABASE_TABLE_PREFIX } from '../config';
|
|
5
|
+
import { $getTableName } from './database/$getTableName';
|
|
6
|
+
import { RESERVED_PATHS } from './generated/reservedPaths';
|
|
5
7
|
import { isIpAllowed } from './utils/isIpAllowed';
|
|
6
8
|
|
|
7
|
-
// Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies
|
|
9
|
+
// Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies !!!!
|
|
8
10
|
function normalizeTo_PascalCase(text: string): string {
|
|
9
11
|
return text
|
|
10
12
|
.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
|
|
@@ -58,7 +60,7 @@ export async function middleware(req: NextRequest) {
|
|
|
58
60
|
});
|
|
59
61
|
|
|
60
62
|
const { data } = await supabase
|
|
61
|
-
.from(
|
|
63
|
+
.from(await $getTableName(`Metadata`))
|
|
62
64
|
.select('value')
|
|
63
65
|
.eq('key', 'RESTRICT_IP')
|
|
64
66
|
.single();
|
|
@@ -107,7 +109,7 @@ export async function middleware(req: NextRequest) {
|
|
|
107
109
|
});
|
|
108
110
|
|
|
109
111
|
const { data } = await supabase
|
|
110
|
-
.from(
|
|
112
|
+
.from(await $getTableName(`ApiTokens`))
|
|
111
113
|
.select('id')
|
|
112
114
|
.eq('token', token)
|
|
113
115
|
.eq('isRevoked', false)
|
|
@@ -186,22 +188,7 @@ export async function middleware(req: NextRequest) {
|
|
|
186
188
|
|
|
187
189
|
if (
|
|
188
190
|
potentialAgentName &&
|
|
189
|
-
!
|
|
190
|
-
'agents',
|
|
191
|
-
'api',
|
|
192
|
-
'admin',
|
|
193
|
-
'docs',
|
|
194
|
-
'test',
|
|
195
|
-
'embed',
|
|
196
|
-
'_next',
|
|
197
|
-
'manifest.webmanifest',
|
|
198
|
-
'sw.js',
|
|
199
|
-
'favicon.ico',
|
|
200
|
-
'sitemap.xml',
|
|
201
|
-
'robots.txt',
|
|
202
|
-
'security.txt',
|
|
203
|
-
'humans.txt',
|
|
204
|
-
].includes(potentialAgentName) &&
|
|
191
|
+
!RESERVED_PATHS.includes(potentialAgentName) &&
|
|
205
192
|
!potentialAgentName.startsWith('.') &&
|
|
206
193
|
// Note: Other static files are excluded by the matcher configuration below
|
|
207
194
|
true
|
|
@@ -256,7 +243,7 @@ export async function middleware(req: NextRequest) {
|
|
|
256
243
|
|
|
257
244
|
try {
|
|
258
245
|
const { data } = await supabase
|
|
259
|
-
.from(
|
|
246
|
+
.from(await $getTableName(`Agent`))
|
|
260
247
|
.select('agentName')
|
|
261
248
|
.or(orFilter)
|
|
262
249
|
.limit(1)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { $provideSupabaseForServer } from '../database/$provideSupabaseForServer';
|
|
2
|
+
import { TrackedFilesStorage } from '../utils/cdn/classes/TrackedFilesStorage';
|
|
1
3
|
import { VercelBlobStorage } from '../utils/cdn/classes/VercelBlobStorage';
|
|
2
4
|
import { IIFilesStorageWithCdn } from '../utils/cdn/interfaces/IFilesStorage';
|
|
3
5
|
|
|
@@ -13,12 +15,15 @@ let cdn: IIFilesStorageWithCdn | null = null;
|
|
|
13
15
|
*/
|
|
14
16
|
export function $provideCdnForServer(): IIFilesStorageWithCdn {
|
|
15
17
|
if (!cdn) {
|
|
16
|
-
|
|
18
|
+
const inner = new VercelBlobStorage({
|
|
17
19
|
token: process.env.VERCEL_BLOB_READ_WRITE_TOKEN!,
|
|
18
20
|
pathPrefix: process.env.NEXT_PUBLIC_CDN_PATH_PREFIX!,
|
|
19
21
|
cdnPublicUrl: new URL(process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!),
|
|
20
22
|
});
|
|
21
23
|
|
|
24
|
+
const supabase = $provideSupabaseForServer();
|
|
25
|
+
cdn = new TrackedFilesStorage(inner, supabase);
|
|
26
|
+
|
|
22
27
|
/*
|
|
23
28
|
cdn = new DigitalOceanSpaces({
|
|
24
29
|
bucket: process.env.CDN_BUCKET!,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { $getTableName } from '../../../database/$getTableName';
|
|
3
|
+
import { AgentsServerDatabase } from '../../../database/schema';
|
|
4
|
+
import type { IFile, IIFilesStorageWithCdn } from '../interfaces/IFilesStorage';
|
|
5
|
+
|
|
6
|
+
export class TrackedFilesStorage implements IIFilesStorageWithCdn {
|
|
7
|
+
public constructor(
|
|
8
|
+
private readonly inner: IIFilesStorageWithCdn,
|
|
9
|
+
private readonly supabase: SupabaseClient<AgentsServerDatabase>,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
public get cdnPublicUrl(): URL {
|
|
13
|
+
return this.inner.cdnPublicUrl;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public get pathPrefix(): string | undefined {
|
|
17
|
+
return this.inner.pathPrefix;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public getItemUrl(key: string): URL {
|
|
21
|
+
return this.inner.getItemUrl(key);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async getItem(key: string): Promise<IFile | null> {
|
|
25
|
+
return this.inner.getItem(key);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async removeItem(key: string): Promise<void> {
|
|
29
|
+
return this.inner.removeItem(key);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async setItem(key: string, file: IFile): Promise<void> {
|
|
33
|
+
console.log('!!! 0', { key, file });
|
|
34
|
+
|
|
35
|
+
await this.inner.setItem(key, file);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const { userId, purpose } = file;
|
|
39
|
+
const cdnUrl = this.getItemUrl(key).href;
|
|
40
|
+
|
|
41
|
+
console.log('!!! 1', { userId, purpose, cdnUrl, key, file });
|
|
42
|
+
|
|
43
|
+
await this.supabase.from(await $getTableName('File')).insert({
|
|
44
|
+
userId: userId || null,
|
|
45
|
+
fileName: key,
|
|
46
|
+
fileSize: file.fileSize ?? file.data.length,
|
|
47
|
+
fileType: file.type,
|
|
48
|
+
cdnUrl,
|
|
49
|
+
purpose: purpose || 'UNKNOWN',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
console.log('!!! 2', { userId, purpose, cdnUrl, key, file });
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Failed to track upload:', error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -14,6 +14,10 @@ export class VercelBlobStorage implements IIFilesStorageWithCdn {
|
|
|
14
14
|
return this.config.cdnPublicUrl;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
public get pathPrefix() {
|
|
18
|
+
return this.config.pathPrefix;
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
public constructor(private readonly config: IVercelBlobStorageConfig) {}
|
|
18
22
|
|
|
19
23
|
public getItemUrl(key: string): URL {
|
|
@@ -5,6 +5,23 @@ export type IFile = {
|
|
|
5
5
|
// Maybe TODO name: string_name;
|
|
6
6
|
type: string_mime_type;
|
|
7
7
|
data: Buffer;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* User who uploaded the file
|
|
11
|
+
*/
|
|
12
|
+
userId?: number;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Purpose of the upload (e.g. KNOWLEDGE, SERVER_FAVICON_URL)
|
|
16
|
+
*/
|
|
17
|
+
purpose?: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Size of the file in bytes
|
|
21
|
+
*
|
|
22
|
+
* Note: This is optional, if not provided, the size of the buffer is used
|
|
23
|
+
*/
|
|
24
|
+
fileSize?: number;
|
|
8
25
|
};
|
|
9
26
|
|
|
10
27
|
/**
|
|
@@ -17,6 +34,7 @@ export type IFilesStorage = Omit<IStorage<IFile>, 'length' | 'clear' | 'key'>;
|
|
|
17
34
|
*/
|
|
18
35
|
export type IIFilesStorageWithCdn = IFilesStorage & {
|
|
19
36
|
readonly cdnPublicUrl: URL;
|
|
37
|
+
readonly pathPrefix?: string;
|
|
20
38
|
getItemUrl(key: string): URL;
|
|
21
39
|
};
|
|
22
40
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
import { $getTableName } from '../database/$getTableName';
|
|
3
|
+
import { $provideSupabaseForServer } from '../database/$provideSupabaseForServer';
|
|
4
|
+
import { getSession } from './session';
|
|
5
|
+
|
|
6
|
+
export async function getUserIdFromRequest(request: NextRequest): Promise<number | null> {
|
|
7
|
+
try {
|
|
8
|
+
// 1. Try to get user from session (cookie)
|
|
9
|
+
const session = await getSession();
|
|
10
|
+
if (session && session.username) {
|
|
11
|
+
const supabase = $provideSupabaseForServer();
|
|
12
|
+
const { data } = await supabase
|
|
13
|
+
.from(await $getTableName('User'))
|
|
14
|
+
.select('id')
|
|
15
|
+
.eq('username', session.username)
|
|
16
|
+
.single();
|
|
17
|
+
|
|
18
|
+
if (data) {
|
|
19
|
+
return data.id;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 2. Try to get user from API key (Authorization header)
|
|
24
|
+
// TODO: [🧠] Implement linking API keys to users if needed
|
|
25
|
+
// const authHeader = request.headers.get('authorization');
|
|
26
|
+
// ...
|
|
27
|
+
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('Error getting user ID from request:', error);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
@@ -2,11 +2,13 @@ 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
|
+
import { OpenAiAssistantExecutionTools } from '@promptbook-local/openai';
|
|
6
7
|
import { ChatMessage, ChatPromptResult, Prompt, string_book, TODO_any } from '@promptbook-local/types';
|
|
7
8
|
import { computeHash } from '@promptbook-local/utils';
|
|
8
9
|
import { NextRequest, NextResponse } from 'next/server';
|
|
9
10
|
import { validateApiKey } from './validateApiKey';
|
|
11
|
+
import { isAgentDeleted } from '../app/agents/[agentName]/_utils';
|
|
10
12
|
|
|
11
13
|
export async function handleChatCompletion(
|
|
12
14
|
request: NextRequest,
|
|
@@ -60,6 +62,19 @@ export async function handleChatCompletion(
|
|
|
60
62
|
);
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
// Check if agent is deleted
|
|
66
|
+
if (await isAgentDeleted(agentName)) {
|
|
67
|
+
return NextResponse.json(
|
|
68
|
+
{
|
|
69
|
+
error: {
|
|
70
|
+
message: 'This agent has been deleted. You can restore it from the Recycle Bin.',
|
|
71
|
+
type: 'agent_deleted',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{ status: 410 }, // Gone - indicates the resource is no longer available
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
63
78
|
const collection = await $provideAgentCollectionForServer();
|
|
64
79
|
let agentSource: string_book;
|
|
65
80
|
try {
|
|
@@ -99,7 +114,50 @@ export async function handleChatCompletion(
|
|
|
99
114
|
);
|
|
100
115
|
}
|
|
101
116
|
|
|
102
|
-
const
|
|
117
|
+
const agentHash = computeAgentHash(agentSource);
|
|
118
|
+
const supabase = $provideSupabaseForServer();
|
|
119
|
+
const { data: assistantCache } = await supabase
|
|
120
|
+
.from(await $getTableName('OpenAiAssistantCache'))
|
|
121
|
+
.select('assistantId')
|
|
122
|
+
.eq('agentHash', agentHash)
|
|
123
|
+
.single();
|
|
124
|
+
|
|
125
|
+
let openAiAssistantExecutionTools = await $provideOpenAiAssistantExecutionToolsForServer();
|
|
126
|
+
|
|
127
|
+
if (assistantCache?.assistantId) {
|
|
128
|
+
console.log(`[🐱🚀] Reusing assistant ${assistantCache.assistantId} for agent ${agentName} (hash: ${agentHash})`);
|
|
129
|
+
openAiAssistantExecutionTools = openAiAssistantExecutionTools.getAssistant(assistantCache.assistantId);
|
|
130
|
+
} else {
|
|
131
|
+
console.log(`[🐱🚀] Creating NEW assistant for agent ${agentName} (hash: ${agentHash})`);
|
|
132
|
+
// Parse to get instructions and name
|
|
133
|
+
const parsed = parseAgentSource(agentSource);
|
|
134
|
+
const name = parsed.agentName || agentName;
|
|
135
|
+
// Extract PERSONA
|
|
136
|
+
const baseInstructions = parsed.personaDescription || 'You are a helpful assistant.';
|
|
137
|
+
|
|
138
|
+
// Note: Append context to instructions
|
|
139
|
+
const contextLines = agentSource.split('\n').filter((line) => line.startsWith('CONTEXT '));
|
|
140
|
+
const contextInstructions = contextLines.join('\n');
|
|
141
|
+
const instructions = contextInstructions ? `${baseInstructions}\n\n${contextInstructions}` : baseInstructions;
|
|
142
|
+
|
|
143
|
+
// Create assistant
|
|
144
|
+
const newAssistantTools = await openAiAssistantExecutionTools.createNewAssistant({
|
|
145
|
+
name,
|
|
146
|
+
instructions,
|
|
147
|
+
// knowledgeSources?
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Save to cache
|
|
151
|
+
const newAssistantId = newAssistantTools.assistantId;
|
|
152
|
+
if (newAssistantId) {
|
|
153
|
+
await supabase.from(await $getTableName('OpenAiAssistantCache')).insert({
|
|
154
|
+
agentHash,
|
|
155
|
+
assistantId: newAssistantId,
|
|
156
|
+
});
|
|
157
|
+
openAiAssistantExecutionTools = newAssistantTools;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
103
161
|
const agent = new Agent({
|
|
104
162
|
agentSource,
|
|
105
163
|
executionTools: {
|
|
@@ -108,7 +166,6 @@ export async function handleChatCompletion(
|
|
|
108
166
|
isVerbose: true, // or false
|
|
109
167
|
});
|
|
110
168
|
|
|
111
|
-
const agentHash = computeAgentHash(agentSource);
|
|
112
169
|
const userAgent = request.headers.get('user-agent');
|
|
113
170
|
const ip =
|
|
114
171
|
request.headers.get('x-forwarded-for') ||
|
|
@@ -138,7 +195,6 @@ export async function handleChatCompletion(
|
|
|
138
195
|
content: lastMessage.content,
|
|
139
196
|
};
|
|
140
197
|
|
|
141
|
-
const supabase = $provideSupabaseForServer();
|
|
142
198
|
await supabase.from(await $getTableName('ChatHistory')).insert({
|
|
143
199
|
createdAt: new Date().toISOString(),
|
|
144
200
|
messageHash: computeHash(userMessageContent),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { capitalize } from '../../../../../src/utils/normalization/capitalize';
|
|
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 => capitalize(word));
|
|
19
|
+
|
|
20
|
+
return capitalizedWords.join(' ');
|
|
21
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createClient } from '@supabase/supabase-js';
|
|
2
2
|
import { NextRequest } from 'next/server';
|
|
3
3
|
import { SERVERS, SUPABASE_TABLE_PREFIX } from '../../config';
|
|
4
|
+
import { $getTableName } from '../database/$getTableName';
|
|
4
5
|
|
|
5
6
|
// Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies
|
|
6
7
|
function normalizeTo_PascalCase(text: string): string {
|
|
@@ -95,7 +96,7 @@ export async function validateApiKey(request: NextRequest): Promise<ApiKeyValida
|
|
|
95
96
|
});
|
|
96
97
|
|
|
97
98
|
const { data, error } = await supabase
|
|
98
|
-
.from(
|
|
99
|
+
.from(await $getTableName(`ApiTokens`))
|
|
99
100
|
.select('id, isRevoked')
|
|
100
101
|
.eq('token', token)
|
|
101
102
|
.single();
|