@promptbook/cli 0.103.0-47 → 0.103.0-49

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 (133) hide show
  1. package/apps/agents-server/README.md +1 -1
  2. package/apps/agents-server/TODO.txt +6 -5
  3. package/apps/agents-server/config.ts +130 -0
  4. package/apps/agents-server/next.config.ts +1 -1
  5. package/apps/agents-server/public/fonts/OpenMoji-black-glyf.woff2 +0 -0
  6. package/apps/agents-server/public/fonts/download-font.js +22 -0
  7. package/apps/agents-server/src/app/[agentName]/[...rest]/page.tsx +6 -0
  8. package/apps/agents-server/src/app/[agentName]/page.tsx +1 -0
  9. package/apps/agents-server/src/app/actions.ts +37 -2
  10. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +68 -0
  11. package/apps/agents-server/src/app/agents/[agentName]/AgentQrCode.tsx +55 -0
  12. package/apps/agents-server/src/app/agents/[agentName]/AgentUrlCopy.tsx +4 -5
  13. package/apps/agents-server/src/app/agents/[agentName]/CopyField.tsx +44 -0
  14. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +8 -8
  15. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +100 -18
  16. package/apps/agents-server/src/app/agents/[agentName]/api/feedback/route.ts +54 -0
  17. package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/route.ts +6 -6
  18. package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts +3 -3
  19. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +6 -7
  20. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +6 -7
  21. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +9 -2
  22. package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +23 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/book+chat/{AgentBookAndChatComponent.tsx → AgentBookAndChatComponent.tsx.todo} +6 -8
  24. package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +28 -17
  25. package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx.todo +21 -0
  26. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatWrapper.tsx +34 -4
  27. package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +4 -1
  28. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +42 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +111 -108
  30. package/apps/agents-server/src/app/agents/page.tsx +1 -1
  31. package/apps/agents-server/src/app/api/auth/login/route.ts +65 -0
  32. package/apps/agents-server/src/app/api/auth/logout/route.ts +7 -0
  33. package/apps/agents-server/src/app/api/metadata/route.ts +116 -0
  34. package/apps/agents-server/src/app/api/upload/route.ts +7 -7
  35. package/apps/agents-server/src/app/api/users/[username]/route.ts +75 -0
  36. package/apps/agents-server/src/app/api/users/route.ts +71 -0
  37. package/apps/agents-server/src/app/globals.css +35 -1
  38. package/apps/agents-server/src/app/layout.tsx +43 -23
  39. package/apps/agents-server/src/app/metadata/MetadataClient.tsx +271 -0
  40. package/apps/agents-server/src/app/metadata/page.tsx +13 -0
  41. package/apps/agents-server/src/app/not-found.tsx +5 -0
  42. package/apps/agents-server/src/app/page.tsx +84 -46
  43. package/apps/agents-server/src/components/Auth/AuthControls.tsx +123 -0
  44. package/apps/agents-server/src/components/ErrorPage/ErrorPage.tsx +33 -0
  45. package/apps/agents-server/src/components/ForbiddenPage/ForbiddenPage.tsx +15 -0
  46. package/apps/agents-server/src/components/Header/Header.tsx +146 -0
  47. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +27 -0
  48. package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +40 -0
  49. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +109 -0
  50. package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +17 -0
  51. package/apps/agents-server/src/components/UsersList/UsersList.tsx +190 -0
  52. package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +60 -0
  53. package/apps/agents-server/src/database/$getTableName.ts +18 -0
  54. package/apps/agents-server/src/database/$provideSupabase.ts +29 -0
  55. package/apps/agents-server/src/{supabase/getSupabaseForBrowser.ts → database/$provideSupabaseForBrowser.ts} +9 -5
  56. package/apps/agents-server/src/{supabase/getSupabaseForServer.ts → database/$provideSupabaseForServer.ts} +7 -7
  57. package/apps/agents-server/src/{supabase/getSupabaseForWorker.ts → database/$provideSupabaseForWorker.ts} +5 -4
  58. package/apps/agents-server/src/database/getMetadata.ts +31 -0
  59. package/apps/agents-server/src/database/metadataDefaults.ts +32 -0
  60. package/apps/agents-server/src/database/schema.sql +179 -0
  61. package/apps/agents-server/src/database/schema.ts +251 -0
  62. package/apps/agents-server/src/middleware.ts +162 -0
  63. package/apps/agents-server/src/tools/$provideAgentCollectionForServer.ts +14 -10
  64. package/apps/agents-server/src/tools/$provideCdnForServer.ts +1 -1
  65. package/apps/agents-server/src/tools/$provideExecutionToolsForServer.ts +11 -13
  66. package/apps/agents-server/src/tools/$provideOpenAiAssistantExecutionToolsForServer.ts +7 -7
  67. package/apps/agents-server/src/tools/$provideServer.ts +39 -0
  68. package/apps/agents-server/src/utils/auth.ts +33 -0
  69. package/apps/agents-server/src/utils/cdn/utils/getUserFileCdnKey.ts +2 -1
  70. package/apps/agents-server/src/utils/cdn/utils/nameToSubfolderPath.ts +1 -1
  71. package/apps/agents-server/src/utils/getCurrentUser.ts +32 -0
  72. package/apps/agents-server/src/utils/isIpAllowed.ts +101 -0
  73. package/apps/agents-server/src/utils/isUserAdmin.ts +31 -0
  74. package/apps/agents-server/src/utils/session.ts +50 -0
  75. package/apps/agents-server/tailwind.config.ts +2 -0
  76. package/esm/index.es.js +310 -49
  77. package/esm/index.es.js.map +1 -1
  78. package/esm/typings/servers.d.ts +1 -0
  79. package/esm/typings/src/_packages/core.index.d.ts +6 -0
  80. package/esm/typings/src/_packages/types.index.d.ts +4 -0
  81. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  82. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +17 -3
  83. package/esm/typings/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +2 -1
  84. package/esm/typings/src/book-2.0/agent-source/computeAgentHash.d.ts +8 -0
  85. package/esm/typings/src/book-2.0/agent-source/computeAgentHash.test.d.ts +1 -0
  86. package/esm/typings/src/book-2.0/agent-source/createDefaultAgentName.d.ts +8 -0
  87. package/esm/typings/src/book-2.0/agent-source/normalizeAgentName.d.ts +9 -0
  88. package/esm/typings/src/book-2.0/agent-source/normalizeAgentName.test.d.ts +1 -0
  89. package/esm/typings/src/book-2.0/agent-source/parseAgentSourceWithCommitments.d.ts +1 -1
  90. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +14 -8
  91. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabaseOptions.d.ts +10 -0
  92. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +57 -32
  93. package/esm/typings/src/commitments/MESSAGE/InitialMessageCommitmentDefinition.d.ts +28 -0
  94. package/esm/typings/src/commitments/index.d.ts +2 -1
  95. package/esm/typings/src/config.d.ts +1 -0
  96. package/esm/typings/src/errors/DatabaseError.d.ts +2 -2
  97. package/esm/typings/src/errors/WrappedError.d.ts +2 -2
  98. package/esm/typings/src/execution/ExecutionTask.d.ts +2 -2
  99. package/esm/typings/src/execution/LlmExecutionTools.d.ts +6 -1
  100. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizardOrCli.d.ts +2 -2
  101. package/esm/typings/src/llm-providers/_common/utils/assertUniqueModels.d.ts +12 -0
  102. package/esm/typings/src/llm-providers/agent/Agent.d.ts +17 -4
  103. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +10 -1
  104. package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +6 -2
  105. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +30 -4
  106. package/esm/typings/src/llm-providers/openai/openai-models.test.d.ts +4 -0
  107. package/esm/typings/src/remote-server/startAgentServer.d.ts +2 -2
  108. package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -2
  109. package/esm/typings/src/transpilers/openai-sdk/register.d.ts +1 -1
  110. package/esm/typings/src/types/typeAliases.d.ts +6 -0
  111. package/esm/typings/src/utils/color/Color.d.ts +7 -0
  112. package/esm/typings/src/utils/color/Color.test.d.ts +1 -0
  113. package/esm/typings/src/utils/environment/$getGlobalScope.d.ts +2 -2
  114. package/esm/typings/src/utils/misc/computeHash.d.ts +11 -0
  115. package/esm/typings/src/utils/misc/computeHash.test.d.ts +1 -0
  116. package/esm/typings/src/utils/normalization/normalize-to-kebab-case.d.ts +2 -0
  117. package/esm/typings/src/utils/normalization/normalizeTo_PascalCase.d.ts +3 -0
  118. package/esm/typings/src/utils/normalization/normalizeTo_camelCase.d.ts +2 -0
  119. package/esm/typings/src/utils/normalization/titleToName.d.ts +2 -0
  120. package/esm/typings/src/utils/organization/$sideEffect.d.ts +2 -2
  121. package/esm/typings/src/utils/organization/$side_effect.d.ts +2 -2
  122. package/esm/typings/src/utils/organization/TODO_USE.d.ts +2 -2
  123. package/esm/typings/src/utils/organization/keepUnused.d.ts +2 -2
  124. package/esm/typings/src/utils/organization/preserve.d.ts +3 -3
  125. package/esm/typings/src/utils/organization/really_any.d.ts +7 -0
  126. package/esm/typings/src/utils/serialization/asSerializable.d.ts +2 -2
  127. package/esm/typings/src/version.d.ts +1 -1
  128. package/package.json +1 -1
  129. package/umd/index.umd.js +311 -50
  130. package/umd/index.umd.js.map +1 -1
  131. package/apps/agents-server/config.ts.todo +0 -312
  132. package/apps/agents-server/src/supabase/TODO.txt +0 -1
  133. package/apps/agents-server/src/supabase/getSupabase.ts +0 -25
@@ -0,0 +1,251 @@
1
+ /**
2
+ * AUTO-GENERATED TYPES FROM `schema.sql`
3
+ * Source of truth: `schema.sql` *(do not edit table structure here manually)*
4
+ *
5
+ * [💽] Prompt:
6
+ * Re-generate supabase typescript schema from the `./schema.sql`
7
+ */
8
+
9
+ // Json helper (Supabase style)
10
+ export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[];
11
+
12
+ // Public schema database interface (Supabase convention)
13
+ export type AgentsServerDatabase = {
14
+ // <- TODO: [🧠][🕜] Better naming
15
+ public: {
16
+ Tables: {
17
+ Metadata: {
18
+ Row: {
19
+ id: number;
20
+ createdAt: string;
21
+ updatedAt: string;
22
+ key: string;
23
+ value: string;
24
+ note: string | null;
25
+ };
26
+ Insert: {
27
+ id?: number;
28
+ createdAt?: string;
29
+ updatedAt?: string;
30
+ key: string;
31
+ value: string;
32
+ note?: string | null;
33
+ };
34
+ Update: {
35
+ id?: number;
36
+ createdAt?: string;
37
+ updatedAt?: string;
38
+ key?: string;
39
+ value?: string;
40
+ note?: string | null;
41
+ };
42
+ Relationships: [];
43
+ };
44
+ Agent: {
45
+ Row: {
46
+ id: number;
47
+ agentName: string;
48
+ createdAt: string;
49
+ updatedAt: string | null;
50
+ agentHash: string;
51
+ agentSource: string;
52
+ agentProfile: Json;
53
+ promptbookEngineVersion: string;
54
+ usage: Json | null;
55
+ preparedModelRequirements: Json | null;
56
+ preparedExternals: Json | null;
57
+ };
58
+ Insert: {
59
+ id?: number;
60
+ agentName: string;
61
+ createdAt: string;
62
+ updatedAt?: string | null;
63
+ agentHash: string;
64
+ agentSource: string;
65
+ agentProfile: Json;
66
+ promptbookEngineVersion: string;
67
+ usage?: Json | null;
68
+ preparedModelRequirements?: Json | null;
69
+ preparedExternals?: Json | null;
70
+ };
71
+ Update: {
72
+ id?: number;
73
+ agentName?: string;
74
+ createdAt?: string;
75
+ updatedAt?: string | null;
76
+ agentHash?: string;
77
+ agentSource?: string;
78
+ agentProfile?: Json;
79
+ promptbookEngineVersion?: string;
80
+ usage?: Json | null;
81
+ preparedModelRequirements?: Json | null;
82
+ preparedExternals?: Json | null;
83
+ };
84
+ Relationships: [];
85
+ };
86
+ AgentHistory: {
87
+ Row: {
88
+ id: number;
89
+ createdAt: string;
90
+ agentName: string;
91
+ agentHash: string;
92
+ previousAgentHash: string | null;
93
+ agentSource: string;
94
+ promptbookEngineVersion: string;
95
+ };
96
+ Insert: {
97
+ id?: number;
98
+ createdAt: string;
99
+ agentName: string;
100
+ agentHash: string;
101
+ previousAgentHash?: string | null;
102
+ agentSource: string;
103
+ promptbookEngineVersion: string;
104
+ };
105
+ Update: {
106
+ id?: number;
107
+ createdAt?: string;
108
+ agentName?: string;
109
+ agentHash?: string;
110
+ previousAgentHash?: string | null;
111
+ agentSource?: string;
112
+ promptbookEngineVersion?: string;
113
+ };
114
+ Relationships: [];
115
+ };
116
+ ChatHistory: {
117
+ Row: {
118
+ id: number;
119
+ createdAt: string;
120
+ messageHash: string;
121
+ previousMessageHash: string | null;
122
+ agentName: string;
123
+ agentHash: string;
124
+ message: Json;
125
+ promptbookEngineVersion: string | null;
126
+ url: string | null;
127
+ ip: string | null;
128
+ userAgent: string | null;
129
+ language: string | null;
130
+ platform: string | null;
131
+ };
132
+ Insert: {
133
+ id?: number;
134
+ createdAt: string;
135
+ messageHash: string;
136
+ previousMessageHash?: string | null;
137
+ agentName: string;
138
+ agentHash: string;
139
+ message: Json;
140
+ promptbookEngineVersion?: string | null;
141
+ url?: string | null;
142
+ ip?: string | null;
143
+ userAgent?: string | null;
144
+ language?: string | null;
145
+ platform?: string | null;
146
+ };
147
+ Update: {
148
+ id?: number;
149
+ createdAt?: string;
150
+ messageHash?: string;
151
+ previousMessageHash?: string | null;
152
+ agentName?: string;
153
+ agentHash?: string;
154
+ message?: Json;
155
+ promptbookEngineVersion?: string | null;
156
+ url?: string | null;
157
+ ip?: string | null;
158
+ userAgent?: string | null;
159
+ language?: string | null;
160
+ platform?: string | null;
161
+ };
162
+ Relationships: [];
163
+ };
164
+ ChatFeedback: {
165
+ Row: {
166
+ id: number;
167
+ createdAt: string;
168
+ agentName: string;
169
+ agentHash: string;
170
+ rating: string | null;
171
+ textRating: string | null;
172
+ chatThread: string | null;
173
+ userNote: string | null;
174
+ expectedAnswer: string | null;
175
+ promptbookEngineVersion: string | null;
176
+ url: string | null;
177
+ ip: string | null;
178
+ userAgent: string | null;
179
+ language: string | null;
180
+ platform: string | null;
181
+ };
182
+ Insert: {
183
+ id?: number;
184
+ createdAt: string;
185
+ agentName: string;
186
+ agentHash: string;
187
+ rating?: string | null;
188
+ textRating?: string | null;
189
+ chatThread?: string | null;
190
+ userNote?: string | null;
191
+ expectedAnswer?: string | null;
192
+ promptbookEngineVersion?: string | null;
193
+ url?: string | null;
194
+ ip?: string | null;
195
+ userAgent?: string | null;
196
+ language?: string | null;
197
+ platform?: string | null;
198
+ };
199
+ Update: {
200
+ id?: number;
201
+ createdAt?: string;
202
+ agentName?: string;
203
+ agentHash?: string;
204
+ rating?: string | null;
205
+ textRating?: string | null;
206
+ chatThread?: string | null;
207
+ userNote?: string | null;
208
+ expectedAnswer?: string | null;
209
+ promptbookEngineVersion?: string | null;
210
+ url?: string | null;
211
+ ip?: string | null;
212
+ userAgent?: string | null;
213
+ language?: string | null;
214
+ platform?: string | null;
215
+ };
216
+ Relationships: [];
217
+ };
218
+ User: {
219
+ Row: {
220
+ id: number;
221
+ createdAt: string;
222
+ updatedAt: string;
223
+ username: string;
224
+ passwordHash: string;
225
+ isAdmin: boolean;
226
+ };
227
+ Insert: {
228
+ id?: number;
229
+ createdAt?: string;
230
+ updatedAt?: string;
231
+ username: string;
232
+ passwordHash: string;
233
+ isAdmin?: boolean;
234
+ };
235
+ Update: {
236
+ id?: number;
237
+ createdAt?: string;
238
+ updatedAt?: string;
239
+ username?: string;
240
+ passwordHash?: string;
241
+ isAdmin?: boolean;
242
+ };
243
+ Relationships: [];
244
+ };
245
+ };
246
+ Views: Record<string, never>;
247
+ Functions: Record<string, never>;
248
+ Enums: Record<string, never>;
249
+ CompositeTypes: Record<string, never>;
250
+ };
251
+ };
@@ -0,0 +1,162 @@
1
+ import { TODO_any } from '@promptbook-local/types';
2
+ import { createClient } from '@supabase/supabase-js';
3
+ import { NextRequest, NextResponse } from 'next/server';
4
+ import { SERVERS, SUPABASE_TABLE_PREFIX } from '../config';
5
+ import { isIpAllowed } from './utils/isIpAllowed';
6
+
7
+ // Note: Re-implementing normalizeTo_PascalCase to avoid importing from @promptbook-local/utils which might have Node.js dependencies
8
+ function normalizeTo_PascalCase(text: string): string {
9
+ return text
10
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
11
+ return word.toUpperCase();
12
+ })
13
+ .replace(/\s+/g, '');
14
+ }
15
+
16
+ export async function middleware(req: NextRequest) {
17
+ // 1. Get client IP
18
+ let ip = (req as TODO_any).ip;
19
+ const xForwardedFor = req.headers.get('x-forwarded-for');
20
+ if (!ip && xForwardedFor) {
21
+ ip = xForwardedFor.split(',')[0].trim();
22
+ }
23
+ // Fallback for local development if needed, though req.ip is usually ::1 or 127.0.0.1
24
+ ip = ip || '127.0.0.1';
25
+
26
+ // 2. Determine allowed IPs
27
+ // Priority: Metadata > Environment Variable
28
+
29
+ const allowedIpsEnv = process.env.RESTRICT_IP;
30
+ let allowedIpsMetadata: string | null = null;
31
+
32
+ // To fetch metadata, we need to know the table name, which depends on the host
33
+ const host = req.headers.get('host');
34
+
35
+ if (host) {
36
+ let tablePrefix = SUPABASE_TABLE_PREFIX;
37
+
38
+ if (SERVERS && SERVERS.length > 0) {
39
+ // Logic mirrored from src/tools/$provideServer.ts
40
+ if (SERVERS.some((server) => server === host)) {
41
+ let serverName = host;
42
+ serverName = serverName.replace(/\.ptbk\.io$/, '');
43
+ serverName = normalizeTo_PascalCase(serverName);
44
+ tablePrefix = `server_${serverName}_`;
45
+ }
46
+ }
47
+
48
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
49
+ const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
50
+
51
+ if (supabaseUrl && supabaseKey) {
52
+ try {
53
+ const supabase = createClient(supabaseUrl, supabaseKey, {
54
+ auth: {
55
+ persistSession: false,
56
+ autoRefreshToken: false,
57
+ },
58
+ });
59
+
60
+ const { data } = await supabase
61
+ .from(`${tablePrefix}Metadata`)
62
+ .select('value')
63
+ .eq('key', 'RESTRICT_IP')
64
+ .single();
65
+
66
+ if (data && data.value) {
67
+ allowedIpsMetadata = data.value;
68
+ }
69
+ } catch (error) {
70
+ console.error('Error fetching metadata in middleware:', error);
71
+ }
72
+ }
73
+ }
74
+
75
+ const allowedIps = allowedIpsMetadata !== null && allowedIpsMetadata !== undefined ? allowedIpsMetadata : allowedIpsEnv;
76
+
77
+ if (isIpAllowed(ip, allowedIps)) {
78
+ // 3. Custom Domain Routing
79
+ // If the host is not one of the configured SERVERS, try to find an agent with a matching META LINK
80
+
81
+ if (host && SERVERS && !SERVERS.some((server) => server === host)) {
82
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
83
+ const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
84
+
85
+ if (supabaseUrl && supabaseKey) {
86
+ const supabase = createClient(supabaseUrl, supabaseKey, {
87
+ auth: {
88
+ persistSession: false,
89
+ autoRefreshToken: false,
90
+ },
91
+ });
92
+
93
+ // Determine prefixes to check
94
+ // We check all configured servers because the custom domain could point to any of them
95
+ // (or if they share the database, we need to check the relevant tables)
96
+ const serversToCheck = SERVERS;
97
+
98
+ // TODO: [🧠] If there are many servers, this loop might be slow. Optimize if needed.
99
+ for (const serverHost of serversToCheck) {
100
+ let serverName = serverHost;
101
+ serverName = serverName.replace(/\.ptbk\.io$/, '');
102
+ serverName = normalizeTo_PascalCase(serverName);
103
+ const prefix = `server_${serverName}_`;
104
+
105
+ // Search for agent with matching META LINK
106
+ // agentProfile->links is an array of strings
107
+ // We check if it contains the host, or https://host, or http://host
108
+
109
+ const searchLinks = [host, `https://${host}`, `http://${host}`];
110
+
111
+ // Construct OR filter: agentProfile.cs.{"links":["link1"]},agentProfile.cs.{"links":["link2"]},...
112
+ const orFilter = searchLinks.map(link => `agentProfile.cs.{"links":["${link}"]}`).join(',');
113
+
114
+ try {
115
+ const { data } = await supabase
116
+ .from(`${prefix}Agent`)
117
+ .select('agentName')
118
+ .or(orFilter)
119
+ .limit(1)
120
+ .single();
121
+
122
+ if (data && data.agentName) {
123
+ // Found the agent!
124
+ const url = req.nextUrl.clone();
125
+ url.pathname = `/${data.agentName}`;
126
+
127
+ // Pass the server context to the app via header
128
+ const requestHeaders = new Headers(req.headers);
129
+ requestHeaders.set('x-promptbook-server', serverHost);
130
+
131
+ return NextResponse.rewrite(url, {
132
+ request: {
133
+ headers: requestHeaders,
134
+ },
135
+ });
136
+ }
137
+ } catch (error) {
138
+ // Ignore error (e.g. table not found, or agent not found) and continue to next server
139
+ // console.error(`Error checking server ${serverHost} for custom domain ${host}:`, error);
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ return NextResponse.next();
146
+ }
147
+
148
+ return new NextResponse('Forbidden', { status: 403 });
149
+ }
150
+
151
+ export const config = {
152
+ matcher: [
153
+ /*
154
+ * Match all request paths except for the ones starting with:
155
+ * - _next/static (static files)
156
+ * - _next/image (image optimization files)
157
+ * - favicon.ico (favicon file)
158
+ * - public folder
159
+ */
160
+ '/((?!_next/static|_next/image|favicon.ico|logo-|fonts/).*)',
161
+ ],
162
+ };
@@ -2,32 +2,34 @@
2
2
 
3
3
  import { AgentCollectionInSupabase } from '@promptbook-local/core';
4
4
  import { AgentCollection } from '@promptbook-local/types';
5
- import { getSupabaseForServer } from '../supabase/getSupabaseForServer';
5
+ import { just } from '../../../../src/utils/organization/just';
6
+ import { $provideSupabaseForServer } from '../database/$provideSupabaseForServer';
7
+ import { $provideServer } from './$provideServer';
6
8
 
7
9
  /**
8
10
  * Cache of provided agent collection
9
- *
11
+ *
10
12
  * @private internal cache for `$provideAgentCollectionForServer`
11
13
  */
12
14
  let agentCollection: null | AgentCollection = null;
13
15
 
14
16
  /**
15
- * !!!!
17
+ * [🐱‍🚀]
16
18
  */
17
19
  export async function $provideAgentCollectionForServer(): Promise<AgentCollection> {
18
20
  // <- Note: This function is potentially async
19
21
 
20
- // TODO: !!!! [🌕] DRY
22
+ // TODO: [🐱‍🚀] [🌕] DRY
21
23
 
22
- const isVerbose = true; // <- TODO: !!!! Pass
24
+ const isVerbose = true; // <- TODO: [🐱‍🚀] Pass
23
25
 
24
- if (agentCollection !== null) {
25
- console.log('!!! Returning cached agent collection');
26
+ if (agentCollection !== null && just(false /* <- TODO: [🐱‍🚀] Fix caching */)) {
27
+ console.log('[🐱‍🚀] Returning cached agent collection');
26
28
  return agentCollection;
27
- // TODO: !!!! Be aware of options changes
29
+ // TODO: [🐱‍🚀] Be aware of options changes
28
30
  }
29
31
 
30
- console.log('!!! Creating NEW agent collection');
32
+ console.log('[🐱‍🚀] Creating NEW agent collection');
31
33
 
32
34
  /*
33
35
  // TODO: [🧟‍♂️][◽] DRY:
@@ -40,10 +42,12 @@ export async function $provideAgentCollectionForServer(): Promise<AgentCollectio
40
42
  });
41
43
  */
42
44
 
43
- const supabase = getSupabaseForServer();
45
+ const supabase = $provideSupabaseForServer();
46
+ const { tablePrefix } = await $provideServer();
44
47
 
45
48
  agentCollection = new AgentCollectionInSupabase(supabase, {
46
49
  isVerbose,
50
+ tablePrefix,
47
51
  });
48
52
 
49
53
  return agentCollection;
@@ -9,7 +9,7 @@ import { IIFilesStorageWithCdn } from '../utils/cdn/interfaces/IFilesStorage';
9
9
  let cdn: IIFilesStorageWithCdn | null = null;
10
10
 
11
11
  /**
12
- * !!!
12
+ * [🐱‍🚀]
13
13
  */
14
14
  export function $provideCdnForServer(): IIFilesStorageWithCdn {
15
15
  if (!cdn) {
@@ -43,11 +43,11 @@ $sideEffect(
43
43
  _MarkitdownScraperMetadataRegistration,
44
44
  _PdfScraperMetadataRegistration,
45
45
  _WebsiteScraperMetadataRegistration,
46
- // <- TODO: !!! Export all registrations from one variabile in `@promptbook/core`
46
+ // <- TODO: [🐱‍🚀] Export all registrations from one variabile in `@promptbook/core`
47
47
  );
48
48
  $sideEffect(/* [㊗] */ _OpenAiRegistration);
49
49
  $sideEffect(/* [㊗] */ _GoogleRegistration);
50
- // <- TODO: !!!! Allow to dynamically install required metadata
50
+ // <- TODO: [🐱‍🚀] Allow to dynamically install required metadata
51
51
 
52
52
  /**
53
53
  * Cache of provided execution tools
@@ -56,8 +56,6 @@ $sideEffect(/* [㊗] */ _GoogleRegistration);
56
56
  */
57
57
  let executionTools: null | ExecutionTools = null;
58
58
 
59
-
60
-
61
59
  /*
62
60
  TODO: [▶️]
63
61
  type ProvideExecutionToolsForServerOptions = {
@@ -66,25 +64,25 @@ type ProvideExecutionToolsForServerOptions = {
66
64
  */
67
65
 
68
66
  /**
69
- * !!!!
67
+ * [🐱‍🚀]
70
68
  */
71
69
  export async function $provideExecutionToolsForServer(): Promise<ExecutionTools> {
72
- // TODO: !!!! [🌕] DRY
70
+ // TODO: [🐱‍🚀] [🌕] DRY
73
71
 
74
- // const path = '../../agents'; // <- TODO: !!!! Pass
75
- const isVerbose = true; // <- TODO: !!!! Pass
76
- const isCacheReloaded = false; // <- TODO: !!!! Pass
72
+ // const path = '../../agents'; // <- TODO: [🐱‍🚀] Pass
73
+ const isVerbose = true; // <- TODO: [🐱‍🚀] Pass
74
+ const isCacheReloaded = false; // <- TODO: [🐱‍🚀] Pass
77
75
  const cliOptions = {
78
76
  provider: 'BRING_YOUR_OWN_KEYS',
79
- } as TODO_any; // <- TODO: !!!! Pass
77
+ } as TODO_any; // <- TODO: [🐱‍🚀] Pass
80
78
 
81
79
  if (executionTools !== null) {
82
- console.log('!!! Returning cached execution tools');
80
+ console.log('[🐱‍🚀] Returning cached execution tools');
83
81
  return executionTools;
84
- // TODO: !!!! Be aware of options changes
82
+ // TODO: [🐱‍🚀] Be aware of options changes
85
83
  }
86
84
 
87
- console.log('!!! Creating NEW execution tools');
85
+ console.log('[🐱‍🚀] Creating NEW execution tools');
88
86
 
89
87
  // TODO: DRY [◽]
90
88
  const prepareAndScrapeOptions = {
@@ -10,23 +10,23 @@ import { OpenAiAssistantExecutionTools } from '@promptbook-local/openai';
10
10
  let executionTools: null | OpenAiAssistantExecutionTools = null;
11
11
 
12
12
  /**
13
- * !!!!
13
+ * [🐱‍🚀]
14
14
  */
15
15
  export async function $provideOpenAiAssistantExecutionToolsForServer(): Promise<OpenAiAssistantExecutionTools> {
16
- // TODO: !!!! [🌕] DRY
17
- const isVerbose = true; // <- TODO: !!!! Pass
16
+ // TODO: [🐱‍🚀] [🌕] DRY
17
+ const isVerbose = true; // <- TODO: [🐱‍🚀] Pass
18
18
 
19
19
  if (executionTools !== null) {
20
- console.log('!!! Returning cached OpenAiAssistantExecutionTools');
20
+ console.log('[🐱‍🚀] Returning cached OpenAiAssistantExecutionTools');
21
21
  return executionTools;
22
- // TODO: !!!! Be aware of options changes
22
+ // TODO: [🐱‍🚀] Be aware of options changes
23
23
  }
24
24
 
25
- console.log('!!! Creating NEW OpenAiAssistantExecutionTools');
25
+ console.log('[🐱‍🚀] Creating NEW OpenAiAssistantExecutionTools');
26
26
 
27
27
  executionTools = new OpenAiAssistantExecutionTools({
28
28
  apiKey: process.env.OPENAI_API_KEY,
29
- assistantId: '!!!! null',
29
+ assistantId: 'abstract_assistant', // <- TODO: [🐱‍🚀] In `OpenAiAssistantExecutionTools` Allow to create abstract assistants with `isCreatingNewAssistantsAllowed`
30
30
  isCreatingNewAssistantsAllowed: true,
31
31
  isVerbose,
32
32
  });
@@ -0,0 +1,39 @@
1
+ import { NEXT_PUBLIC_URL, SERVERS, SUPABASE_TABLE_PREFIX } from '@/config';
2
+ import { normalizeTo_PascalCase } from '@promptbook-local/utils';
3
+ import { headers } from 'next/headers';
4
+
5
+ export async function $provideServer() {
6
+ if (!SERVERS) {
7
+ return {
8
+ publicUrl: NEXT_PUBLIC_URL || new URL(`https://${(await headers()).get('host') || 'localhost:4440'}`),
9
+ tablePrefix: SUPABASE_TABLE_PREFIX,
10
+ };
11
+ }
12
+
13
+ const headersList = await headers();
14
+ let host = headersList.get('host');
15
+ const xPromptbookServer = headersList.get('x-promptbook-server');
16
+
17
+ if (host === null) {
18
+ throw new Error('Host header is missing');
19
+ }
20
+
21
+ // If host is not in known servers, check if we have a context header from middleware
22
+ if (!SERVERS.some((server) => server === host)) {
23
+ if (xPromptbookServer && SERVERS.some((server) => server === xPromptbookServer)) {
24
+ host = xPromptbookServer;
25
+ } else {
26
+ throw new Error(`Server with host "${host}" is not configured in SERVERS`);
27
+ }
28
+ }
29
+
30
+ let serverName = host;
31
+
32
+ serverName = serverName.replace(/\.ptbk\.io$/, '');
33
+ serverName = normalizeTo_PascalCase(serverName);
34
+
35
+ return {
36
+ publicUrl: new URL(`https://${host}`),
37
+ tablePrefix: `server_${serverName}_`,
38
+ };
39
+ }
@@ -0,0 +1,33 @@
1
+ import { randomBytes, scrypt, timingSafeEqual } from 'crypto';
2
+ import { promisify } from 'util';
3
+
4
+ const scryptAsync = promisify(scrypt);
5
+
6
+ /**
7
+ * Hashes a password using scrypt
8
+ *
9
+ * @param password The plain text password
10
+ * @returns The salt and hash formatted as "salt:hash"
11
+ */
12
+ export async function hashPassword(password: string): Promise<string> {
13
+ const salt = randomBytes(16).toString('hex');
14
+ const derivedKey = (await scryptAsync(password, salt, 64)) as Buffer;
15
+ return `${salt}:${derivedKey.toString('hex')}`;
16
+ }
17
+
18
+ /**
19
+ * Verifies a password against a stored hash
20
+ *
21
+ * @param password The plain text password
22
+ * @param storedHash The stored hash in format "salt:hash"
23
+ * @returns True if the password matches
24
+ */
25
+ export async function verifyPassword(password: string, storedHash: string): Promise<boolean> {
26
+ const [salt, key] = storedHash.split(':');
27
+ if (!salt || !key) return false;
28
+
29
+ const derivedKey = (await scryptAsync(password, salt, 64)) as Buffer;
30
+ const keyBuffer = Buffer.from(key, 'hex');
31
+
32
+ return timingSafeEqual(derivedKey, keyBuffer);
33
+ }
@@ -1,7 +1,7 @@
1
- import { titleToName } from '../../../../../../src/utils/normalization/titleToName';
2
1
  import hexEncoder from 'crypto-js/enc-hex';
3
2
  import sha256 from 'crypto-js/sha256';
4
3
  import type { string_uri } from '../../../../../../src/types/typeAliases';
4
+ import { titleToName } from '../../../../../../src/utils/normalization/titleToName';
5
5
  import { nameToSubfolderPath } from './nameToSubfolderPath';
6
6
 
7
7
  /**
@@ -9,6 +9,7 @@ import { nameToSubfolderPath } from './nameToSubfolderPath';
9
9
  */
10
10
  export function getUserFileCdnKey(file: Buffer, originalFilename: string): string_uri {
11
11
  const hash = sha256(hexEncoder.parse(file.toString('hex'))).toString(/* hex */);
12
+ // <- TODO: [🥬] Encapsulate sha256 to some private utility function
12
13
 
13
14
  const originalFilenameParts = originalFilename.split('.');
14
15
  const extension = originalFilenameParts.pop();
@@ -5,5 +5,5 @@ export function nameToSubfolderPath(name: string_name): Array<string> {
5
5
  }
6
6
 
7
7
  /**
8
- * TODO: !!! Use `nameToSubfolderPath` from src
8
+ * TODO: [🐱‍🚀] Use `nameToSubfolderPath` from src
9
9
  */