@promptbook/cli 0.104.0-1 → 0.104.0-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/apps/agents-server/next.config.ts +2 -2
  2. package/apps/agents-server/package.json +7 -3
  3. package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
  4. package/apps/agents-server/public/swagger.json +115 -0
  5. package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
  6. package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
  7. package/apps/agents-server/src/app/AddAgentButton.tsx +3 -3
  8. package/apps/agents-server/src/app/actions.ts +17 -5
  9. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
  10. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
  11. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
  12. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
  13. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
  14. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
  15. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +2 -0
  16. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +18 -0
  17. package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
  18. package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
  19. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
  20. package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
  21. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +1 -1
  22. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
  23. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
  24. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
  25. package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
  26. package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
  27. package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
  28. package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +214 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
  30. package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
  31. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +1 -1
  32. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +2 -2
  33. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +12 -6
  34. package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +87 -0
  35. package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
  36. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +10 -12
  37. package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +19 -0
  38. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +41 -0
  39. package/apps/agents-server/src/app/api/agents/route.ts +28 -3
  40. package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
  41. package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
  42. package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
  43. package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
  44. package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
  45. package/apps/agents-server/src/app/api/upload/route.ts +128 -45
  46. package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
  47. package/apps/agents-server/src/app/docs/page.tsx +12 -12
  48. package/apps/agents-server/src/app/globals.css +140 -33
  49. package/apps/agents-server/src/app/layout.tsx +27 -22
  50. package/apps/agents-server/src/app/page.tsx +50 -4
  51. package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
  52. package/apps/agents-server/src/app/recycle-bin/page.tsx +25 -41
  53. package/apps/agents-server/src/app/sitemap.xml/route.ts +6 -3
  54. package/apps/agents-server/src/app/swagger/page.tsx +14 -0
  55. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +9 -98
  56. package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
  57. package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
  58. package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
  59. package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
  60. package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
  61. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
  62. package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
  63. package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
  64. package/apps/agents-server/src/components/Header/Header.tsx +106 -40
  65. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +104 -20
  66. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +72 -12
  67. package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +50 -0
  68. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
  69. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
  70. package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
  71. package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
  72. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
  73. package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
  74. package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
  75. package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
  76. package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
  77. package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
  78. package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
  79. package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
  80. package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
  81. package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
  82. package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
  83. package/apps/agents-server/src/database/schema.ts +109 -0
  84. package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
  85. package/apps/agents-server/src/middleware.ts +19 -23
  86. package/apps/agents-server/src/tools/$provideCdnForServer.ts +6 -1
  87. package/apps/agents-server/src/utils/auth.ts +117 -17
  88. package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
  89. package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
  90. package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
  91. package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
  92. package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
  93. package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +21 -0
  94. package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
  95. package/esm/index.es.js +194 -34
  96. package/esm/index.es.js.map +1 -1
  97. package/esm/typings/src/_packages/types.index.d.ts +8 -2
  98. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
  99. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
  100. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  101. package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
  102. package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  103. package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
  104. package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
  105. package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
  106. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +13 -7
  107. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +6 -0
  108. package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
  109. package/esm/typings/src/commitments/index.d.ts +2 -1
  110. package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
  111. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
  112. package/esm/typings/src/types/Message.d.ts +49 -0
  113. package/esm/typings/src/types/typeAliases.d.ts +12 -0
  114. package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
  115. package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
  116. package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
  117. package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
  118. package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
  119. package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
  120. package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
  121. package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
  122. package/esm/typings/src/version.d.ts +1 -1
  123. package/package.json +1 -1
  124. package/umd/index.umd.js +200 -40
  125. package/umd/index.umd.js.map +1 -1
  126. package/apps/agents-server/package-lock.json +0 -27
  127. package/apps/agents-server/public/fonts/download-font.js +0 -22
  128. package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
@@ -1,15 +1,20 @@
1
+ import { HomeIcon } from 'lucide-react';
1
2
  import Link from 'next/link';
2
3
  import { ErrorPage } from '../ErrorPage/ErrorPage';
3
4
 
4
5
  export function NotFoundPage() {
5
6
  return (
6
- <ErrorPage title="404 Not Found" message="The page you are looking for does not exist.">
7
+ <ErrorPage
8
+ title="Agent Not Found :("
9
+ message="The agent you are looking for does not exist, but you can create your own!"
10
+ >
7
11
  <div className="flex justify-center">
8
12
  <Link
9
13
  href="/"
10
14
  className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
11
15
  >
12
- Go Home
16
+ <HomeIcon className="inline w-5 h-5 mr-2" />
17
+ Home
13
18
  </Link>
14
19
  </div>
15
20
  </ErrorPage>
@@ -1,19 +1,28 @@
1
+ import { string_char_emoji } from '@promptbook-local/types';
1
2
  import { DetailedHTMLProps, HTMLAttributes } from 'react';
2
3
 
3
4
  type OpenMojiIconProps = DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
4
- icon: string;
5
+ /**
6
+ * The OpenMoji character to display
7
+ */
8
+ icon: string_char_emoji | string;
9
+
10
+ /**
11
+ * @default 'black'
12
+ */
13
+ variant?: 'black' | 'color';
5
14
  };
6
15
 
7
16
  /**
8
17
  * Renders an emoji using the OpenMoji black and white font
9
18
  */
10
- export function OpenMojiIcon({ icon, className, style, ...rest }: OpenMojiIconProps) {
19
+ export function OpenMojiIcon(props: OpenMojiIconProps) {
20
+ const { icon, variant = 'black', className, style, ...rest } = props;
21
+
22
+ const fontFamily = variant === 'black' ? '"OpenMojiBlack", sans-serif' : '"OpenMojiColor", sans-serif';
23
+
11
24
  return (
12
- <span
13
- className={className}
14
- style={{ ...style, fontFamily: '"OpenMojiBlack", sans-serif' }}
15
- {...rest}
16
- >
25
+ <span className={className} style={{ ...style, fontFamily }} {...rest}>
17
26
  {icon}
18
27
  </span>
19
28
  );
@@ -1,16 +1,16 @@
1
1
  export function PrintHeader({ title }: { title?: string }) {
2
2
  return (
3
- <div className="hidden print:block mb-8 border-b-2 border-blue-600 pb-4">
3
+ <div className="hidden print:block mb-6 border-b-2 border-blue-600 pb-2">
4
4
  <div className="flex justify-between items-end">
5
5
  <div>
6
- <h1 className="text-3xl font-bold text-gray-900 font-poppins">Agents Server</h1>
6
+ <h1 className="text-2xl font-bold text-gray-900 font-poppins">Agents Server</h1>
7
7
  <div className="text-sm text-gray-500 mt-1 flex items-center gap-1">
8
8
  Powered by <span className="font-semibold text-blue-600">Promptbook</span>
9
9
  </div>
10
10
  </div>
11
- {title && <h2 className="text-xl font-semibold text-gray-700">{title}</h2>}
11
+ {title && <h2 className="text-lg font-semibold text-gray-700">{title}</h2>}
12
12
  </div>
13
- <div className="text-xs text-gray-400 mt-2 text-right">
13
+ <div className="text-xs text-gray-400 mt-1 text-right">
14
14
  {new Date().toLocaleDateString()}
15
15
  </div>
16
16
  </div>
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import { X } from 'lucide-react';
4
+ import { Portal } from '../Portal/Portal';
5
+
6
+ type RegisterUserDialogProps = {
7
+ isOpen: boolean;
8
+ onClose: () => void;
9
+ adminEmail: string;
10
+ };
11
+
12
+ export function RegisterUserDialog(props: RegisterUserDialogProps) {
13
+ const { isOpen, onClose, adminEmail } = props;
14
+
15
+ if (!isOpen) {
16
+ return null;
17
+ }
18
+
19
+ return (
20
+ <Portal>
21
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 backdrop-blur-sm animate-in fade-in duration-200">
22
+ <div className="relative w-full max-w-md bg-white rounded-lg shadow-lg border border-gray-200 p-6 animate-in zoom-in-95 duration-200">
23
+ <button
24
+ onClick={onClose}
25
+ className="absolute top-4 right-4 text-gray-400 hover:text-gray-500 transition-colors"
26
+ >
27
+ <X className="w-5 h-5" />
28
+ <span className="sr-only">Close</span>
29
+ </button>
30
+
31
+ <div className="mb-6">
32
+ <h2 className="text-xl font-semibold text-gray-900">Register New User</h2>
33
+ <p className="text-sm text-gray-500 mt-1">Create a new user account</p>
34
+ </div>
35
+
36
+ <div className="space-y-4">
37
+ <div className="p-4 bg-blue-50 border border-blue-200 rounded-md">
38
+ <p className="text-sm text-blue-800">
39
+ This Promptbook server has no email capability. Please contact the administrator at{' '}
40
+ <a
41
+ href={`mailto:${adminEmail}`}
42
+ className="font-medium text-blue-900 underline hover:text-blue-800"
43
+ >
44
+ {adminEmail}
45
+ </a>{' '}
46
+ to register new user.
47
+ </p>
48
+ </div>
49
+
50
+ <button
51
+ onClick={onClose}
52
+ className="w-full inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-gray-100 text-gray-900 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors"
53
+ >
54
+ Close
55
+ </button>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </Portal>
60
+ );
61
+ }
@@ -1,6 +1,6 @@
1
1
  // Utility to append ?headless param if present in current URL
2
- import { usePathname, useSearchParams } from 'next/navigation';
3
2
  import Link, { LinkProps } from 'next/link';
3
+ import { useSearchParams } from 'next/navigation';
4
4
  import { useMemo } from 'react';
5
5
 
6
6
  // Returns true if ?headless is present in current search params
@@ -18,7 +18,11 @@ export function appendHeadlessParam(href: string, isHeadless: boolean): string {
18
18
  }
19
19
 
20
20
  // Custom Link that preserves headless param
21
- export function HeadlessLink({ href, children, ...rest }: LinkProps & { children: React.ReactNode } & React.AnchorHTMLAttributes<HTMLAnchorElement>) {
21
+ export function HeadlessLink({
22
+ href,
23
+ children,
24
+ ...rest
25
+ }: LinkProps & { children: React.ReactNode } & React.AnchorHTMLAttributes<HTMLAnchorElement>) {
22
26
  const isHeadless = useIsHeadless();
23
27
  const finalHref = useMemo(() => appendHeadlessParam(String(href), isHeadless), [href, isHeadless]);
24
28
  return (
@@ -28,7 +32,7 @@ export function HeadlessLink({ href, children, ...rest }: LinkProps & { children
28
32
  );
29
33
  }
30
34
 
31
- import { useRouter } from "next/navigation";
35
+ import { useRouter } from 'next/navigation';
32
36
 
33
37
  // Helper for router.push
34
38
  export function pushWithHeadless(router: ReturnType<typeof useRouter>, href: string, isHeadless: boolean) {
@@ -38,7 +38,13 @@ export const metadataDefaults = [
38
38
  type: 'TEXT',
39
39
  },
40
40
  {
41
- key: 'IS_VOICE_CALLING_ENABLED',
41
+ key: 'SHOW_FEDERATED_SERVERS_PUBLICLY',
42
+ value: 'false',
43
+ note: 'Whether to show federated servers and their agents to anonymous users. When false, federated servers are only visible to authenticated users.',
44
+ type: 'BOOLEAN',
45
+ },
46
+ {
47
+ key: 'IS_EXPERIMENTAL_VOICE_CALLING_ENABLED',
42
48
  value: 'false',
43
49
  note: 'Enable or disable voice calling features for agents. When disabled, voice API endpoints will return 403 Forbidden.',
44
50
  type: 'BOOLEAN',
@@ -67,6 +73,18 @@ export const metadataDefaults = [
67
73
  note: 'Language for generating new agent names. Possible values: ENGLISH, CZECH.',
68
74
  type: 'TEXT_SINGLE_LINE',
69
75
  },
76
+ {
77
+ key: 'ADMIN_EMAIL',
78
+ value: 'support@ptbk.io',
79
+ note: 'Administrator email address used for password reset and user registration requests.',
80
+ type: 'TEXT_SINGLE_LINE',
81
+ },
82
+ {
83
+ key: 'DEFAULT_AGENT_VISIBILITY',
84
+ value: 'PRIVATE',
85
+ note: 'Default visibility for new agents. Can be PUBLIC or PRIVATE.',
86
+ type: 'TEXT_SINGLE_LINE',
87
+ },
70
88
  ] as const satisfies ReadonlyArray<{
71
89
  key: string;
72
90
  value: string;
@@ -0,0 +1,3 @@
1
+
2
+ ALTER TABLE "prefix_Agent" ADD COLUMN "permanentId" TEXT;
3
+ CREATE UNIQUE INDEX "prefix_Agent_permanentId_key" ON "prefix_Agent"("permanentId");
@@ -0,0 +1 @@
1
+ ALTER TABLE "prefix_Agent" ADD COLUMN "deletedAt" TEXT;
@@ -0,0 +1,19 @@
1
+ CREATE TABLE IF NOT EXISTS "prefix_Image" (
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
+ "filename" TEXT NOT NULL,
7
+ "prompt" TEXT NOT NULL,
8
+ "cdnUrl" TEXT NOT NULL,
9
+ "cdnKey" TEXT NOT NULL
10
+ );
11
+
12
+ CREATE UNIQUE INDEX IF NOT EXISTS "prefix_Image_filename_idx" ON "prefix_Image" ("filename");
13
+
14
+ ALTER TABLE "prefix_Image" ENABLE ROW LEVEL SECURITY;
15
+
16
+ COMMENT ON COLUMN "prefix_Image"."filename" IS 'The original filename requested (e.g., cat-sitting-on-keyboard.png)';
17
+ COMMENT ON COLUMN "prefix_Image"."prompt" IS 'The normalized prompt used to generate the image';
18
+ COMMENT ON COLUMN "prefix_Image"."cdnUrl" IS 'The full URL of the uploaded image in CDN';
19
+ COMMENT ON COLUMN "prefix_Image"."cdnKey" IS 'The key used to identify the image in CDN storage';
@@ -0,0 +1 @@
1
+ ALTER TABLE "prefix_Agent" ADD COLUMN "visibility" TEXT NOT NULL DEFAULT 'PRIVATE' CHECK ("visibility" IN ('PUBLIC', 'PRIVATE'));
@@ -0,0 +1,20 @@
1
+ CREATE TABLE IF NOT EXISTS "prefix_File" (
2
+ "id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
3
+ "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
4
+
5
+ "userId" BIGINT REFERENCES "prefix_User"("id"),
6
+ "fileName" TEXT NOT NULL,
7
+ "fileSize" BIGINT NOT NULL,
8
+ "fileType" TEXT NOT NULL,
9
+ "cdnUrl" TEXT NOT NULL,
10
+ "purpose" TEXT NOT NULL
11
+ );
12
+
13
+ ALTER TABLE "prefix_File" ENABLE ROW LEVEL SECURITY;
14
+
15
+ COMMENT ON COLUMN "prefix_File"."userId" IS 'Reference to the user who uploaded the file';
16
+ COMMENT ON COLUMN "prefix_File"."fileName" IS 'Original name of the uploaded file';
17
+ COMMENT ON COLUMN "prefix_File"."fileSize" IS 'Size of the file in bytes';
18
+ COMMENT ON COLUMN "prefix_File"."fileType" IS 'MIME type of the file';
19
+ COMMENT ON COLUMN "prefix_File"."cdnUrl" IS 'Public URL of the file in CDN';
20
+ COMMENT ON COLUMN "prefix_File"."purpose" IS 'Purpose of the upload (e.g. KNOWLEDGE, SERVER_FAVICON_URL)';
@@ -0,0 +1,13 @@
1
+ -- Add status column to track file upload progress
2
+ ALTER TABLE "prefix_File" ADD COLUMN IF NOT EXISTS "status" TEXT NOT NULL DEFAULT 'COMPLETED';
3
+
4
+ -- Add check constraint for valid status values
5
+ -- ALTER TABLE "prefix_File" ADD CONSTRAINT "File_status_check" CHECK ("status" IN ('UPLOADING', 'COMPLETED', 'FAILED'));
6
+
7
+ -- Drop the column cdnUrl if it exists
8
+ ALTER TABLE "prefix_File" DROP COLUMN IF EXISTS "cdnUrl";
9
+
10
+
11
+ -- Add nullable columns storageUrl and shortUrl
12
+ ALTER TABLE "prefix_File" ADD COLUMN IF NOT EXISTS "storageUrl" TEXT NULL;
13
+ ALTER TABLE "prefix_File" ADD COLUMN IF NOT EXISTS "shortUrl" TEXT NULL;
@@ -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,32 @@
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 for routes
8
+ * @see /apps/agents-server/public - source directory for static files
9
+ * @see /apps/agents-server/src/middleware.ts - where this is used
10
+ */
11
+ export const RESERVED_PATHS: readonly string[] = [
12
+ "_next",
13
+ "admin",
14
+ "agents",
15
+ "api",
16
+ "docs",
17
+ "embed",
18
+ "favicon.ico",
19
+ "fonts",
20
+ "humans.txt",
21
+ "logo-blue-white-256.png",
22
+ "manifest.webmanifest",
23
+ "recycle-bin",
24
+ "restricted",
25
+ "robots.txt",
26
+ "security.txt",
27
+ "sitemap.xml",
28
+ "sw.js",
29
+ "swagger",
30
+ "swagger.json",
31
+ "test"
32
+ ] as const;
@@ -1,13 +1,15 @@
1
1
  import { TODO_any } from '@promptbook-local/types';
2
2
  import { createClient } from '@supabase/supabase-js';
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { SERVERS, SUPABASE_TABLE_PREFIX } from '../config';
4
+ import { SERVERS } 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
- .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
12
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => {
11
13
  return word.toUpperCase();
12
14
  })
13
15
  .replace(/\s+/g, '');
@@ -33,8 +35,12 @@ export async function middleware(req: NextRequest) {
33
35
  const host = req.headers.get('host');
34
36
 
35
37
  if (host) {
38
+ /*
39
+ Note: [🐔] This code was commented out because results of it are unused
40
+
36
41
  let tablePrefix = SUPABASE_TABLE_PREFIX;
37
42
 
43
+
38
44
  if (SERVERS && SERVERS.length > 0) {
39
45
  // Logic mirrored from src/tools/$provideServer.ts
40
46
  if (SERVERS.some((server) => server === host)) {
@@ -44,6 +50,7 @@ export async function middleware(req: NextRequest) {
44
50
  tablePrefix = `server_${serverName}_`;
45
51
  }
46
52
  }
53
+ */
47
54
 
48
55
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
49
56
  const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
@@ -58,7 +65,7 @@ export async function middleware(req: NextRequest) {
58
65
  });
59
66
 
60
67
  const { data } = await supabase
61
- .from(`${tablePrefix}Metadata`)
68
+ .from(await $getTableName(`Metadata`))
62
69
  .select('value')
63
70
  .eq('key', 'RESTRICT_IP')
64
71
  .single();
@@ -82,6 +89,9 @@ export async function middleware(req: NextRequest) {
82
89
  const token = authHeader.split(' ')[1];
83
90
 
84
91
  if (token.startsWith('ptbk_')) {
92
+ /*
93
+ Note: [🐔] This code was commented out because results of it are unused
94
+
85
95
  const host = req.headers.get('host');
86
96
  let tablePrefix = SUPABASE_TABLE_PREFIX;
87
97
 
@@ -93,6 +103,7 @@ export async function middleware(req: NextRequest) {
93
103
  tablePrefix = `server_${serverName}_`;
94
104
  }
95
105
  }
106
+ */
96
107
 
97
108
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
98
109
  const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
@@ -107,7 +118,7 @@ export async function middleware(req: NextRequest) {
107
118
  });
108
119
 
109
120
  const { data } = await supabase
110
- .from(`${tablePrefix}ApiTokens`)
121
+ .from(await $getTableName(`ApiTokens`))
111
122
  .select('id')
112
123
  .eq('token', token)
113
124
  .eq('isRevoked', false)
@@ -186,22 +197,7 @@ export async function middleware(req: NextRequest) {
186
197
 
187
198
  if (
188
199
  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) &&
200
+ !RESERVED_PATHS.includes(potentialAgentName) &&
205
201
  !potentialAgentName.startsWith('.') &&
206
202
  // Note: Other static files are excluded by the matcher configuration below
207
203
  true
@@ -243,7 +239,7 @@ export async function middleware(req: NextRequest) {
243
239
  let serverName = serverHost;
244
240
  serverName = serverName.replace(/\.ptbk\.io$/, '');
245
241
  serverName = normalizeTo_PascalCase(serverName);
246
- const prefix = `server_${serverName}_`;
242
+ // const prefix = `server_${serverName}_`;
247
243
 
248
244
  // Search for agent with matching META LINK
249
245
  // agentProfile->links is an array of strings
@@ -256,7 +252,7 @@ export async function middleware(req: NextRequest) {
256
252
 
257
253
  try {
258
254
  const { data } = await supabase
259
- .from(`${prefix}Agent`)
255
+ .from(await $getTableName(`Agent`))
260
256
  .select('agentName')
261
257
  .or(orFilter)
262
258
  .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
- cdn = new VercelBlobStorage({
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!,