@promptbook/cli 0.103.0-48 → 0.103.0-50
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/README.md +1 -1
- package/apps/agents-server/TODO.txt +6 -5
- package/apps/agents-server/config.ts +130 -0
- package/apps/agents-server/next.config.ts +1 -1
- package/apps/agents-server/public/fonts/OpenMoji-black-glyf.woff2 +0 -0
- package/apps/agents-server/public/fonts/download-font.js +22 -0
- package/apps/agents-server/src/app/[agentName]/[...rest]/page.tsx +11 -0
- package/apps/agents-server/src/app/[agentName]/page.tsx +1 -0
- package/apps/agents-server/src/app/actions.ts +37 -2
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/AgentQrCode.tsx +55 -0
- package/apps/agents-server/src/app/agents/[agentName]/AgentUrlCopy.tsx +4 -5
- package/apps/agents-server/src/app/agents/[agentName]/CopyField.tsx +44 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +121 -25
- package/apps/agents-server/src/app/agents/[agentName]/api/feedback/route.ts +54 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/route.ts +6 -6
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts +3 -3
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +29 -10
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +4 -5
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +9 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +23 -0
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/{AgentBookAndChatComponent.tsx → AgentBookAndChatComponent.tsx.todo} +4 -4
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +28 -17
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx.todo +21 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatWrapper.tsx +34 -4
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +4 -1
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +42 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +117 -106
- package/apps/agents-server/src/app/agents/page.tsx +1 -1
- package/apps/agents-server/src/app/api/agents/route.ts +34 -0
- package/apps/agents-server/src/app/api/auth/login/route.ts +65 -0
- package/apps/agents-server/src/app/api/auth/logout/route.ts +7 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +116 -0
- package/apps/agents-server/src/app/api/upload/route.ts +7 -3
- package/apps/agents-server/src/app/api/users/[username]/route.ts +75 -0
- package/apps/agents-server/src/app/api/users/route.ts +71 -0
- package/apps/agents-server/src/app/globals.css +35 -1
- package/apps/agents-server/src/app/layout.tsx +43 -23
- package/apps/agents-server/src/app/metadata/MetadataClient.tsx +271 -0
- package/apps/agents-server/src/app/metadata/page.tsx +13 -0
- package/apps/agents-server/src/app/not-found.tsx +5 -0
- package/apps/agents-server/src/app/page.tsx +117 -46
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +123 -0
- package/apps/agents-server/src/components/ErrorPage/ErrorPage.tsx +33 -0
- package/apps/agents-server/src/components/ForbiddenPage/ForbiddenPage.tsx +15 -0
- package/apps/agents-server/src/components/Header/Header.tsx +146 -0
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +27 -0
- package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +40 -0
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +109 -0
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +17 -0
- package/apps/agents-server/src/components/UsersList/UsersList.tsx +190 -0
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +60 -0
- package/apps/agents-server/src/database/$getTableName.ts +18 -0
- package/apps/agents-server/src/database/$provideSupabase.ts +2 -2
- package/apps/agents-server/src/database/$provideSupabaseForServer.ts +3 -3
- package/apps/agents-server/src/database/getMetadata.ts +31 -0
- package/apps/agents-server/src/database/metadataDefaults.ts +37 -0
- package/apps/agents-server/src/database/schema.sql +81 -33
- package/apps/agents-server/src/database/schema.ts +35 -1
- package/apps/agents-server/src/middleware.ts +200 -0
- package/apps/agents-server/src/tools/$provideAgentCollectionForServer.ts +11 -7
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +1 -1
- package/apps/agents-server/src/tools/$provideExecutionToolsForServer.ts +11 -13
- package/apps/agents-server/src/tools/$provideOpenAiAssistantExecutionToolsForServer.ts +7 -7
- package/apps/agents-server/src/tools/$provideServer.ts +39 -0
- package/apps/agents-server/src/utils/auth.ts +33 -0
- package/apps/agents-server/src/utils/cdn/utils/nameToSubfolderPath.ts +1 -1
- package/apps/agents-server/src/utils/getCurrentUser.ts +32 -0
- package/apps/agents-server/src/utils/getFederatedAgents.ts +66 -0
- package/apps/agents-server/src/utils/isIpAllowed.ts +101 -0
- package/apps/agents-server/src/utils/isUserAdmin.ts +31 -0
- package/apps/agents-server/src/utils/session.ts +50 -0
- package/apps/agents-server/tailwind.config.ts +2 -0
- package/esm/index.es.js +147 -31
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +1 -0
- package/esm/typings/src/_packages/components.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +2 -0
- package/esm/typings/src/_packages/utils.index.d.ts +2 -0
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +12 -2
- package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgent.d.ts +20 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +14 -8
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabaseOptions.d.ts +10 -0
- package/esm/typings/src/commitments/MESSAGE/InitialMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/config.d.ts +1 -0
- package/esm/typings/src/errors/DatabaseError.d.ts +2 -2
- package/esm/typings/src/errors/WrappedError.d.ts +2 -2
- package/esm/typings/src/execution/ExecutionTask.d.ts +2 -2
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +6 -1
- package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizardOrCli.d.ts +2 -2
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +19 -3
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +13 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +11 -2
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +6 -1
- package/esm/typings/src/remote-server/startAgentServer.d.ts +2 -2
- package/esm/typings/src/utils/color/Color.d.ts +7 -0
- package/esm/typings/src/utils/color/Color.test.d.ts +1 -0
- package/esm/typings/src/utils/environment/$getGlobalScope.d.ts +2 -2
- package/esm/typings/src/utils/misc/computeHash.d.ts +11 -0
- package/esm/typings/src/utils/misc/computeHash.test.d.ts +1 -0
- package/esm/typings/src/utils/organization/$sideEffect.d.ts +2 -2
- package/esm/typings/src/utils/organization/$side_effect.d.ts +2 -2
- package/esm/typings/src/utils/organization/TODO_USE.d.ts +2 -2
- package/esm/typings/src/utils/organization/keepUnused.d.ts +2 -2
- package/esm/typings/src/utils/organization/preserve.d.ts +3 -3
- package/esm/typings/src/utils/organization/really_any.d.ts +7 -0
- package/esm/typings/src/utils/serialization/asSerializable.d.ts +2 -2
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +147 -31
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/config.ts.todo +0 -38
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
-- To update, search for [💽]
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
CREATE TABLE IF NOT EXISTS "EnvironmentVariable" (
|
|
7
|
+
CREATE TABLE IF NOT EXISTS "prefix_Metadata" (
|
|
9
8
|
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
9
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
10
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
10
11
|
"key" TEXT NOT NULL,
|
|
11
12
|
"value" TEXT NOT NULL,
|
|
12
13
|
"note" TEXT NULL,
|
|
13
|
-
CONSTRAINT
|
|
14
|
+
CONSTRAINT prefix_Metadata_key_key UNIQUE ("key")
|
|
14
15
|
);
|
|
15
16
|
|
|
17
|
+
ALTER TABLE "prefix_Metadata" ENABLE ROW LEVEL SECURITY;
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS "prefix_Agent" (
|
|
18
21
|
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
19
22
|
"agentName" TEXT NOT NULL,
|
|
20
23
|
|
|
@@ -33,18 +36,20 @@ CREATE TABLE IF NOT EXISTS "Agent" (
|
|
|
33
36
|
"preparedExternals" JSONB NULL
|
|
34
37
|
);
|
|
35
38
|
-- Ensure uniqueness of agentName even on repeated schema application without duplicate constraint creation
|
|
36
|
-
CREATE UNIQUE INDEX IF NOT EXISTS
|
|
37
|
-
COMMENT ON COLUMN "
|
|
38
|
-
COMMENT ON COLUMN "
|
|
39
|
-
COMMENT ON COLUMN "
|
|
40
|
-
COMMENT ON COLUMN "
|
|
41
|
-
COMMENT ON COLUMN "
|
|
42
|
-
COMMENT ON COLUMN "
|
|
43
|
-
COMMENT ON COLUMN "
|
|
44
|
-
COMMENT ON COLUMN "
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
CREATE UNIQUE INDEX IF NOT EXISTS prefix_agent_agentname_key ON "prefix_Agent" ("agentName");
|
|
40
|
+
COMMENT ON COLUMN "prefix_Agent"."agentName" IS 'The unique name of the agent derived from the first line of the `agentSource`, automatically updated on `agentSource` change';
|
|
41
|
+
COMMENT ON COLUMN "prefix_Agent"."agentHash" IS 'The hash of the `agentSource`, automatically updated on `agentSource` change';
|
|
42
|
+
COMMENT ON COLUMN "prefix_Agent"."agentSource" IS 'The source code of the agent';
|
|
43
|
+
COMMENT ON COLUMN "prefix_Agent"."agentProfile" IS 'The profile of the agent generated from the `agentSource`, automatically updated on `agentSource` change <- TODO: [🕛]';
|
|
44
|
+
COMMENT ON COLUMN "prefix_Agent"."promptbookEngineVersion" IS 'The version of the Promptbook engine used for last update of the agent';
|
|
45
|
+
COMMENT ON COLUMN "prefix_Agent"."usage" IS 'Usage and spending statistics for the agent';
|
|
46
|
+
COMMENT ON COLUMN "prefix_Agent"."preparedModelRequirements" IS 'The prepared model requirements for the agent, generated from the `agentSource` but not after every change (only on explicit request), there is `agentHash` to identify the version of the `agentSource` this was prepared from';
|
|
47
|
+
COMMENT ON COLUMN "prefix_Agent"."preparedExternals" IS 'The prepared externals for the agent, generated from the `agentSource` but not after every change (only on explicit request), there is `agentHash` to identify the version of the `agentSource` this was prepared from';
|
|
48
|
+
|
|
49
|
+
ALTER TABLE "prefix_Agent" ENABLE ROW LEVEL SECURITY;
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
CREATE TABLE IF NOT EXISTS "prefix_AgentHistory" (
|
|
48
53
|
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
49
54
|
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
50
55
|
|
|
@@ -56,11 +61,13 @@ CREATE TABLE IF NOT EXISTS "AgentHistory" (
|
|
|
56
61
|
|
|
57
62
|
);
|
|
58
63
|
|
|
59
|
-
CREATE INDEX IF NOT EXISTS "
|
|
60
|
-
CREATE INDEX IF NOT EXISTS "
|
|
64
|
+
CREATE INDEX IF NOT EXISTS "prefix_AgentHistory_agentName_idx" ON "prefix_AgentHistory" ("agentName");
|
|
65
|
+
CREATE INDEX IF NOT EXISTS "prefix_AgentHistory_agentHash_idx" ON "prefix_AgentHistory" ("agentHash");
|
|
61
66
|
|
|
67
|
+
ALTER TABLE "prefix_AgentHistory" ENABLE ROW LEVEL SECURITY;
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
|
|
70
|
+
CREATE TABLE IF NOT EXISTS "prefix_ChatHistory" (
|
|
64
71
|
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
65
72
|
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
66
73
|
|
|
@@ -78,14 +85,16 @@ CREATE TABLE IF NOT EXISTS "ChatHistory" (
|
|
|
78
85
|
"language" TEXT NULL,
|
|
79
86
|
"platform" TEXT NULL
|
|
80
87
|
);
|
|
81
|
-
COMMENT ON COLUMN "
|
|
82
|
-
COMMENT ON COLUMN "
|
|
83
|
-
COMMENT ON COLUMN "
|
|
84
|
-
COMMENT ON COLUMN "
|
|
85
|
-
COMMENT ON COLUMN "
|
|
88
|
+
COMMENT ON COLUMN "prefix_ChatHistory"."url" IS 'The URL where the chat was happening';
|
|
89
|
+
COMMENT ON COLUMN "prefix_ChatHistory"."ip" IS 'The IP address of the user';
|
|
90
|
+
COMMENT ON COLUMN "prefix_ChatHistory"."userAgent" IS 'The user agent (browser) of the user';
|
|
91
|
+
COMMENT ON COLUMN "prefix_ChatHistory"."language" IS 'The language (from the browser) of the user';
|
|
92
|
+
COMMENT ON COLUMN "prefix_ChatHistory"."platform" IS 'The platform of the user';
|
|
93
|
+
|
|
94
|
+
ALTER TABLE "prefix_ChatHistory" ENABLE ROW LEVEL SECURITY;
|
|
86
95
|
|
|
87
96
|
|
|
88
|
-
CREATE TABLE IF NOT EXISTS "
|
|
97
|
+
CREATE TABLE IF NOT EXISTS "prefix_ChatFeedback" (
|
|
89
98
|
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
90
99
|
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
91
100
|
|
|
@@ -105,15 +114,31 @@ CREATE TABLE IF NOT EXISTS "ChatFeedback" (
|
|
|
105
114
|
"language" TEXT NULL,
|
|
106
115
|
"platform" TEXT NULL
|
|
107
116
|
);
|
|
108
|
-
COMMENT ON COLUMN "
|
|
109
|
-
COMMENT ON COLUMN "
|
|
110
|
-
COMMENT ON COLUMN "
|
|
111
|
-
COMMENT ON COLUMN "
|
|
112
|
-
COMMENT ON COLUMN "
|
|
117
|
+
COMMENT ON COLUMN "prefix_ChatFeedback"."url" IS 'The URL where the chat was happening';
|
|
118
|
+
COMMENT ON COLUMN "prefix_ChatFeedback"."ip" IS 'The IP address of the user';
|
|
119
|
+
COMMENT ON COLUMN "prefix_ChatFeedback"."userAgent" IS 'The user agent (browser) of the user';
|
|
120
|
+
COMMENT ON COLUMN "prefix_ChatFeedback"."language" IS 'The language (from the browser) of the user';
|
|
121
|
+
COMMENT ON COLUMN "prefix_ChatFeedback"."platform" IS 'The platform of the user';
|
|
122
|
+
|
|
123
|
+
ALTER TABLE "prefix_ChatFeedback" ENABLE ROW LEVEL SECURITY;
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
CREATE TABLE IF NOT EXISTS "prefix_User" (
|
|
127
|
+
"id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
128
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
129
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
130
|
+
|
|
131
|
+
"username" TEXT NOT NULL,
|
|
132
|
+
"passwordHash" TEXT NOT NULL,
|
|
133
|
+
"isAdmin" BOOLEAN NOT NULL DEFAULT FALSE
|
|
134
|
+
);
|
|
135
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "prefix_User_username_idx" ON "prefix_User" ("username");
|
|
136
|
+
|
|
137
|
+
ALTER TABLE "prefix_User" ENABLE ROW LEVEL SECURITY;
|
|
113
138
|
|
|
114
139
|
|
|
115
140
|
/*
|
|
116
|
-
CREATE TABLE IF NOT EXISTS "
|
|
141
|
+
CREATE TABLE IF NOT EXISTS "prefix_AgentActionHistory" (
|
|
117
142
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
118
143
|
createdAt TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
119
144
|
|
|
@@ -122,10 +147,33 @@ createdAt TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
|
122
147
|
|
|
123
148
|
|
|
124
149
|
/*
|
|
125
|
-
CREATE TABLE IF NOT EXISTS "
|
|
150
|
+
CREATE TABLE IF NOT EXISTS "prefix_Xxx" (
|
|
126
151
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
127
152
|
);
|
|
128
153
|
*/
|
|
129
154
|
|
|
130
155
|
|
|
131
|
-
--
|
|
156
|
+
-- Foreign key relationships
|
|
157
|
+
-- Note: Using agentName as the foreign key reference since it's the unique identifier used across tables
|
|
158
|
+
ALTER TABLE "prefix_AgentHistory"
|
|
159
|
+
DROP CONSTRAINT IF EXISTS "prefix_AgentHistory_agentName_fkey",
|
|
160
|
+
ADD CONSTRAINT "prefix_AgentHistory_agentName_fkey"
|
|
161
|
+
FOREIGN KEY ("agentName")
|
|
162
|
+
REFERENCES "prefix_Agent"("agentName")
|
|
163
|
+
ON DELETE CASCADE;
|
|
164
|
+
|
|
165
|
+
ALTER TABLE "prefix_ChatHistory"
|
|
166
|
+
DROP CONSTRAINT IF EXISTS "prefix_ChatHistory_agentName_fkey",
|
|
167
|
+
ADD CONSTRAINT "prefix_ChatHistory_agentName_fkey"
|
|
168
|
+
FOREIGN KEY ("agentName")
|
|
169
|
+
REFERENCES "prefix_Agent"("agentName")
|
|
170
|
+
ON DELETE CASCADE;
|
|
171
|
+
|
|
172
|
+
ALTER TABLE "prefix_ChatFeedback"
|
|
173
|
+
DROP CONSTRAINT IF EXISTS "prefix_ChatFeedback_agentName_fkey",
|
|
174
|
+
ADD CONSTRAINT "prefix_ChatFeedback_agentName_fkey"
|
|
175
|
+
FOREIGN KEY ("agentName")
|
|
176
|
+
REFERENCES "prefix_Agent"("agentName")
|
|
177
|
+
ON DELETE CASCADE;
|
|
178
|
+
|
|
179
|
+
-- TODO: [🐱🚀] Create propper database migrations
|
|
@@ -11,23 +11,30 @@ export type Json = string | number | boolean | null | { [key: string]: Json | un
|
|
|
11
11
|
|
|
12
12
|
// Public schema database interface (Supabase convention)
|
|
13
13
|
export type AgentsServerDatabase = {
|
|
14
|
+
// <- TODO: [🧠][🕜] Better naming
|
|
14
15
|
public: {
|
|
15
16
|
Tables: {
|
|
16
|
-
|
|
17
|
+
Metadata: {
|
|
17
18
|
Row: {
|
|
18
19
|
id: number;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
updatedAt: string;
|
|
19
22
|
key: string;
|
|
20
23
|
value: string;
|
|
21
24
|
note: string | null;
|
|
22
25
|
};
|
|
23
26
|
Insert: {
|
|
24
27
|
id?: number;
|
|
28
|
+
createdAt?: string;
|
|
29
|
+
updatedAt?: string;
|
|
25
30
|
key: string;
|
|
26
31
|
value: string;
|
|
27
32
|
note?: string | null;
|
|
28
33
|
};
|
|
29
34
|
Update: {
|
|
30
35
|
id?: number;
|
|
36
|
+
createdAt?: string;
|
|
37
|
+
updatedAt?: string;
|
|
31
38
|
key?: string;
|
|
32
39
|
value?: string;
|
|
33
40
|
note?: string | null;
|
|
@@ -208,6 +215,33 @@ export type AgentsServerDatabase = {
|
|
|
208
215
|
};
|
|
209
216
|
Relationships: [];
|
|
210
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
|
+
};
|
|
211
245
|
};
|
|
212
246
|
Views: Record<string, never>;
|
|
213
247
|
Functions: Record<string, never>;
|
|
@@ -0,0 +1,200 @@
|
|
|
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 =
|
|
76
|
+
allowedIpsMetadata !== null && allowedIpsMetadata !== undefined ? allowedIpsMetadata : allowedIpsEnv;
|
|
77
|
+
|
|
78
|
+
if (isIpAllowed(ip, allowedIps)) {
|
|
79
|
+
// Handle OPTIONS (preflight) requests before any redirects
|
|
80
|
+
// to avoid CORS issues ("Redirect is not allowed for a preflight request")
|
|
81
|
+
if (req.method === 'OPTIONS') {
|
|
82
|
+
return new NextResponse(null, {
|
|
83
|
+
status: 200,
|
|
84
|
+
headers: {
|
|
85
|
+
'Access-Control-Allow-Origin': '*',
|
|
86
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
87
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 3. Redirect /:agentName/* to /agents/:agentName/*
|
|
93
|
+
// This enables accessing agents from the root path
|
|
94
|
+
const pathParts = req.nextUrl.pathname.split('/');
|
|
95
|
+
const potentialAgentName = pathParts[1];
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
potentialAgentName &&
|
|
99
|
+
!['agents', 'api', '_next', 'favicon.ico'].includes(potentialAgentName) &&
|
|
100
|
+
!potentialAgentName.startsWith('.') &&
|
|
101
|
+
// Note: Other static files are excluded by the matcher configuration below
|
|
102
|
+
true
|
|
103
|
+
) {
|
|
104
|
+
const url = req.nextUrl.clone();
|
|
105
|
+
url.pathname = `/agents${req.nextUrl.pathname}`;
|
|
106
|
+
const response = NextResponse.redirect(url);
|
|
107
|
+
|
|
108
|
+
// Enable CORS for the redirect
|
|
109
|
+
response.headers.set('Access-Control-Allow-Origin', '*');
|
|
110
|
+
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
111
|
+
response.headers.set('Access-Control-Allow-Headers', 'Content-Type');
|
|
112
|
+
|
|
113
|
+
return response;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 4. Custom Domain Routing
|
|
117
|
+
// If the host is not one of the configured SERVERS, try to find an agent with a matching META LINK
|
|
118
|
+
|
|
119
|
+
if (host && SERVERS && !SERVERS.some((server) => server === host)) {
|
|
120
|
+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
121
|
+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
122
|
+
|
|
123
|
+
if (supabaseUrl && supabaseKey) {
|
|
124
|
+
const supabase = createClient(supabaseUrl, supabaseKey, {
|
|
125
|
+
auth: {
|
|
126
|
+
persistSession: false,
|
|
127
|
+
autoRefreshToken: false,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Determine prefixes to check
|
|
132
|
+
// We check all configured servers because the custom domain could point to any of them
|
|
133
|
+
// (or if they share the database, we need to check the relevant tables)
|
|
134
|
+
const serversToCheck = SERVERS;
|
|
135
|
+
|
|
136
|
+
// TODO: [🧠] If there are many servers, this loop might be slow. Optimize if needed.
|
|
137
|
+
for (const serverHost of serversToCheck) {
|
|
138
|
+
let serverName = serverHost;
|
|
139
|
+
serverName = serverName.replace(/\.ptbk\.io$/, '');
|
|
140
|
+
serverName = normalizeTo_PascalCase(serverName);
|
|
141
|
+
const prefix = `server_${serverName}_`;
|
|
142
|
+
|
|
143
|
+
// Search for agent with matching META LINK
|
|
144
|
+
// agentProfile->links is an array of strings
|
|
145
|
+
// We check if it contains the host, or https://host, or http://host
|
|
146
|
+
|
|
147
|
+
const searchLinks = [host, `https://${host}`, `http://${host}`];
|
|
148
|
+
|
|
149
|
+
// Construct OR filter: agentProfile.cs.{"links":["link1"]},agentProfile.cs.{"links":["link2"]},...
|
|
150
|
+
const orFilter = searchLinks.map((link) => `agentProfile.cs.{"links":["${link}"]}`).join(',');
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const { data } = await supabase
|
|
154
|
+
.from(`${prefix}Agent`)
|
|
155
|
+
.select('agentName')
|
|
156
|
+
.or(orFilter)
|
|
157
|
+
.limit(1)
|
|
158
|
+
.single();
|
|
159
|
+
|
|
160
|
+
if (data && data.agentName) {
|
|
161
|
+
// Found the agent!
|
|
162
|
+
const url = req.nextUrl.clone();
|
|
163
|
+
url.pathname = `/${data.agentName}`;
|
|
164
|
+
|
|
165
|
+
// Pass the server context to the app via header
|
|
166
|
+
const requestHeaders = new Headers(req.headers);
|
|
167
|
+
requestHeaders.set('x-promptbook-server', serverHost);
|
|
168
|
+
|
|
169
|
+
return NextResponse.rewrite(url, {
|
|
170
|
+
request: {
|
|
171
|
+
headers: requestHeaders,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
// Ignore error (e.g. table not found, or agent not found) and continue to next server
|
|
177
|
+
// console.error(`Error checking server ${serverHost} for custom domain ${host}:`, error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return NextResponse.next();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return new NextResponse('Forbidden', { status: 403 });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export const config = {
|
|
190
|
+
matcher: [
|
|
191
|
+
/*
|
|
192
|
+
* Match all request paths except for the ones starting with:
|
|
193
|
+
* - _next/static (static files)
|
|
194
|
+
* - _next/image (image optimization files)
|
|
195
|
+
* - favicon.ico (favicon file)
|
|
196
|
+
* - public folder
|
|
197
|
+
*/
|
|
198
|
+
'/((?!_next/static|_next/image|favicon.ico|logo-|fonts/).*)',
|
|
199
|
+
],
|
|
200
|
+
};
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { AgentCollectionInSupabase } from '@promptbook-local/core';
|
|
4
4
|
import { AgentCollection } from '@promptbook-local/types';
|
|
5
|
+
import { just } from '../../../../src/utils/organization/just';
|
|
5
6
|
import { $provideSupabaseForServer } from '../database/$provideSupabaseForServer';
|
|
7
|
+
import { $provideServer } from './$provideServer';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Cache of provided agent collection
|
|
@@ -12,22 +14,22 @@ import { $provideSupabaseForServer } from '../database/$provideSupabaseForServer
|
|
|
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:
|
|
22
|
+
// TODO: [🐱🚀] [🌕] DRY
|
|
21
23
|
|
|
22
|
-
const isVerbose = true; // <- TODO:
|
|
24
|
+
const isVerbose = true; // <- TODO: [🐱🚀] Pass
|
|
23
25
|
|
|
24
|
-
if (agentCollection !== null) {
|
|
25
|
-
console.log('
|
|
26
|
+
if (agentCollection !== null && just(false /* <- TODO: [🐱🚀] Fix caching */)) {
|
|
27
|
+
console.log('[🐱🚀] Returning cached agent collection');
|
|
26
28
|
return agentCollection;
|
|
27
|
-
// TODO:
|
|
29
|
+
// TODO: [🐱🚀] Be aware of options changes
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
console.log('
|
|
32
|
+
console.log('[🐱🚀] Creating NEW agent collection');
|
|
31
33
|
|
|
32
34
|
/*
|
|
33
35
|
// TODO: [🧟♂️][◽] DRY:
|
|
@@ -41,9 +43,11 @@ export async function $provideAgentCollectionForServer(): Promise<AgentCollectio
|
|
|
41
43
|
*/
|
|
42
44
|
|
|
43
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;
|
|
@@ -43,11 +43,11 @@ $sideEffect(
|
|
|
43
43
|
_MarkitdownScraperMetadataRegistration,
|
|
44
44
|
_PdfScraperMetadataRegistration,
|
|
45
45
|
_WebsiteScraperMetadataRegistration,
|
|
46
|
-
// <- TODO:
|
|
46
|
+
// <- TODO: [🐱🚀] Export all registrations from one variabile in `@promptbook/core`
|
|
47
47
|
);
|
|
48
48
|
$sideEffect(/* [㊗] */ _OpenAiRegistration);
|
|
49
49
|
$sideEffect(/* [㊗] */ _GoogleRegistration);
|
|
50
|
-
// <- TODO:
|
|
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:
|
|
70
|
+
// TODO: [🐱🚀] [🌕] DRY
|
|
73
71
|
|
|
74
|
-
// const path = '../../agents'; // <- TODO:
|
|
75
|
-
const isVerbose = true; // <- TODO:
|
|
76
|
-
const isCacheReloaded = false; // <- TODO:
|
|
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:
|
|
77
|
+
} as TODO_any; // <- TODO: [🐱🚀] Pass
|
|
80
78
|
|
|
81
79
|
if (executionTools !== null) {
|
|
82
|
-
console.log('
|
|
80
|
+
console.log('[🐱🚀] Returning cached execution tools');
|
|
83
81
|
return executionTools;
|
|
84
|
-
// TODO:
|
|
82
|
+
// TODO: [🐱🚀] Be aware of options changes
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
console.log('
|
|
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:
|
|
17
|
-
const isVerbose = true; // <- TODO:
|
|
16
|
+
// TODO: [🐱🚀] [🌕] DRY
|
|
17
|
+
const isVerbose = true; // <- TODO: [🐱🚀] Pass
|
|
18
18
|
|
|
19
19
|
if (executionTools !== null) {
|
|
20
|
-
console.log('
|
|
20
|
+
console.log('[🐱🚀] Returning cached OpenAiAssistantExecutionTools');
|
|
21
21
|
return executionTools;
|
|
22
|
-
// TODO:
|
|
22
|
+
// TODO: [🐱🚀] Be aware of options changes
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
console.log('
|
|
25
|
+
console.log('[🐱🚀] Creating NEW OpenAiAssistantExecutionTools');
|
|
26
26
|
|
|
27
27
|
executionTools = new OpenAiAssistantExecutionTools({
|
|
28
28
|
apiKey: process.env.OPENAI_API_KEY,
|
|
29
|
-
assistantId: 'abstract_assistant', // <- TODO:
|
|
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
|
+
}
|