@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
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { cookies } from 'next/headers';
|
|
2
|
+
import { getSession } from './session';
|
|
3
|
+
|
|
4
|
+
export type UserInfo = {
|
|
5
|
+
username: string;
|
|
6
|
+
isAdmin: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export async function getCurrentUser(): Promise<UserInfo | null> {
|
|
10
|
+
// Check session
|
|
11
|
+
const session = await getSession();
|
|
12
|
+
if (session) {
|
|
13
|
+
return {
|
|
14
|
+
username: session.username,
|
|
15
|
+
isAdmin: session.isAdmin
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Check legacy admin token
|
|
20
|
+
if (process.env.ADMIN_PASSWORD) {
|
|
21
|
+
const cookieStore = await cookies();
|
|
22
|
+
const adminToken = cookieStore.get('adminToken');
|
|
23
|
+
if (adminToken?.value === process.env.ADMIN_PASSWORD) {
|
|
24
|
+
return {
|
|
25
|
+
username: 'admin',
|
|
26
|
+
isAdmin: true
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AgentCollection } from '@promptbook-local/types';
|
|
2
|
+
|
|
3
|
+
type Agent = Awaited<ReturnType<AgentCollection['listAgents']>>[number];
|
|
4
|
+
|
|
5
|
+
type AgentWithUrl = Agent & {
|
|
6
|
+
url: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type AgentsApiResponse = {
|
|
10
|
+
agents: AgentWithUrl[];
|
|
11
|
+
federatedServers: string[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetches agents from federated servers recursively
|
|
16
|
+
*/
|
|
17
|
+
export async function getFederatedAgents(initialServers: string[]): Promise<AgentWithUrl[]> {
|
|
18
|
+
const visited = new Set<string>();
|
|
19
|
+
const queue = [...initialServers];
|
|
20
|
+
const externalAgentsMap = new Map<string, AgentWithUrl>();
|
|
21
|
+
const MAX_SERVERS = 20;
|
|
22
|
+
|
|
23
|
+
while (queue.length > 0 && visited.size < MAX_SERVERS) {
|
|
24
|
+
const serverUrl = queue.shift();
|
|
25
|
+
if (!serverUrl) continue;
|
|
26
|
+
|
|
27
|
+
const normalizedUrl = serverUrl.trim().replace(/\/$/, '');
|
|
28
|
+
if (visited.has(normalizedUrl)) continue;
|
|
29
|
+
visited.add(normalizedUrl);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// TODO: [🧠] Should we use some shorter timeout?
|
|
33
|
+
const response = await fetch(`${normalizedUrl}/api/agents`, {
|
|
34
|
+
next: { revalidate: 600 } // Cache for 10 minutes
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
console.warn(`Failed to fetch agents from ${normalizedUrl}: ${response.status} ${response.statusText}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const data: AgentsApiResponse = await response.json();
|
|
43
|
+
|
|
44
|
+
if (data.agents && Array.isArray(data.agents)) {
|
|
45
|
+
for (const agent of data.agents) {
|
|
46
|
+
if (agent.url && !externalAgentsMap.has(agent.url)) {
|
|
47
|
+
externalAgentsMap.set(agent.url, agent);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (data.federatedServers && Array.isArray(data.federatedServers)) {
|
|
53
|
+
for (const server of data.federatedServers) {
|
|
54
|
+
const normalizedServer = server.trim().replace(/\/$/, '');
|
|
55
|
+
if (!visited.has(normalizedServer) && normalizedServer !== '') {
|
|
56
|
+
queue.push(normalizedServer);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(`Error fetching agents from ${normalizedUrl}:`, error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Array.from(externalAgentsMap.values());
|
|
66
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { isIPv4, isIPv6 } from 'net';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if the IP address is allowed based on the allowed IPs list
|
|
5
|
+
*
|
|
6
|
+
* @param clientIp - The IP address of the client
|
|
7
|
+
* @param allowedIps - Comma separated list of allowed IPs or CIDR ranges
|
|
8
|
+
* @returns true if the IP is allowed, false otherwise
|
|
9
|
+
*/
|
|
10
|
+
export function isIpAllowed(clientIp: string, allowedIps: string | null | undefined): boolean {
|
|
11
|
+
if (!allowedIps || allowedIps.trim() === '') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const allowedList = allowedIps.split(',').map((ip) => ip.trim());
|
|
16
|
+
|
|
17
|
+
for (const allowed of allowedList) {
|
|
18
|
+
if (allowed === '') {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (allowed.includes('/')) {
|
|
23
|
+
// CIDR
|
|
24
|
+
if (isIpInCidr(clientIp, allowed)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
// Single IP
|
|
29
|
+
if (clientIp === allowed) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isIpInCidr(ip: string, cidr: string): boolean {
|
|
39
|
+
try {
|
|
40
|
+
const [range, bitsStr] = cidr.split('/');
|
|
41
|
+
const bits = parseInt(bitsStr, 10);
|
|
42
|
+
|
|
43
|
+
if (isIPv4(ip) && isIPv4(range)) {
|
|
44
|
+
return isIPv4InCidr(ip, range, bits);
|
|
45
|
+
} else if (isIPv6(ip) && isIPv6(range)) {
|
|
46
|
+
return isIPv6InCidr(ip, range, bits);
|
|
47
|
+
} else if (isIPv6(ip) && isIPv4(range)) {
|
|
48
|
+
// Check if IPv6 is IPv4-mapped
|
|
49
|
+
if (ip.startsWith('::ffff:')) {
|
|
50
|
+
return isIPv4InCidr(ip.substring(7), range, bits);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(`Error checking CIDR ${cidr} for IP ${ip}:`, error);
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ipToLong(ip: string): number {
|
|
60
|
+
return (
|
|
61
|
+
ip.split('.').reduce((acc, octet) => {
|
|
62
|
+
return (acc << 8) + parseInt(octet, 10);
|
|
63
|
+
}, 0) >>> 0
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isIPv4InCidr(ip: string, range: string, bits: number): boolean {
|
|
68
|
+
const mask = ~((1 << (32 - bits)) - 1);
|
|
69
|
+
const ipLong = ipToLong(ip);
|
|
70
|
+
const rangeLong = ipToLong(range);
|
|
71
|
+
|
|
72
|
+
return (ipLong & mask) === (rangeLong & mask);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parseIPv6(ip: string): bigint {
|
|
76
|
+
// Expand ::
|
|
77
|
+
let fullIp = ip;
|
|
78
|
+
if (ip.includes('::')) {
|
|
79
|
+
const parts = ip.split('::');
|
|
80
|
+
const left = parts[0].split(':').filter(Boolean);
|
|
81
|
+
const right = parts[1].split(':').filter(Boolean);
|
|
82
|
+
const missing = 8 - (left.length + right.length);
|
|
83
|
+
const zeros = Array(missing).fill('0');
|
|
84
|
+
fullIp = [...left, ...zeros, ...right].join(':');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const parts = fullIp.split(':');
|
|
88
|
+
let value = BigInt(0);
|
|
89
|
+
for (const part of parts) {
|
|
90
|
+
value = (value << BigInt(16)) + BigInt(parseInt(part || '0', 16));
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isIPv6InCidr(ip: string, range: string, bits: number): boolean {
|
|
96
|
+
const ipBigInt = parseIPv6(ip);
|
|
97
|
+
const rangeBigInt = parseIPv6(range);
|
|
98
|
+
const mask = (BigInt(1) << BigInt(128)) - BigInt(1) ^ ((BigInt(1) << BigInt(128 - bits)) - BigInt(1));
|
|
99
|
+
|
|
100
|
+
return (ipBigInt & mask) === (rangeBigInt & mask);
|
|
101
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { cookies } from 'next/headers';
|
|
2
|
+
import { getSession } from './session';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Checks if the current user is an admin
|
|
6
|
+
*
|
|
7
|
+
* Note: If `process.env.ADMIN_PASSWORD` is not set, everyone is admin
|
|
8
|
+
*
|
|
9
|
+
* @returns true if the user is admin
|
|
10
|
+
*/
|
|
11
|
+
export async function isUserAdmin(): Promise<boolean> {
|
|
12
|
+
if (!process.env.ADMIN_PASSWORD) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check legacy admin token
|
|
17
|
+
const cookieStore = await cookies();
|
|
18
|
+
const adminToken = cookieStore.get('adminToken');
|
|
19
|
+
|
|
20
|
+
if (adminToken?.value === process.env.ADMIN_PASSWORD) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check session
|
|
25
|
+
const session = await getSession();
|
|
26
|
+
if (session?.isAdmin) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHmac } from 'crypto';
|
|
2
|
+
import { cookies } from 'next/headers';
|
|
3
|
+
|
|
4
|
+
const SESSION_COOKIE_NAME = 'sessionToken';
|
|
5
|
+
const SECRET_KEY = process.env.ADMIN_PASSWORD || 'default-secret-key-change-me';
|
|
6
|
+
|
|
7
|
+
type SessionUser = {
|
|
8
|
+
username: string;
|
|
9
|
+
isAdmin: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export async function setSession(user: SessionUser) {
|
|
13
|
+
const payload = JSON.stringify(user);
|
|
14
|
+
const signature = createHmac('sha256', SECRET_KEY).update(payload).digest('hex');
|
|
15
|
+
const token = `${Buffer.from(payload).toString('base64')}.${signature}`;
|
|
16
|
+
|
|
17
|
+
(await cookies()).set(SESSION_COOKIE_NAME, token, {
|
|
18
|
+
httpOnly: true,
|
|
19
|
+
secure: process.env.NODE_ENV === 'production',
|
|
20
|
+
path: '/',
|
|
21
|
+
maxAge: 60 * 60 * 24 * 7, // 1 week
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function clearSession() {
|
|
26
|
+
(await cookies()).delete(SESSION_COOKIE_NAME);
|
|
27
|
+
// Also clear legacy adminToken
|
|
28
|
+
(await cookies()).delete('adminToken');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function getSession(): Promise<SessionUser | null> {
|
|
32
|
+
const cookieStore = await cookies();
|
|
33
|
+
const token = cookieStore.get(SESSION_COOKIE_NAME)?.value;
|
|
34
|
+
|
|
35
|
+
if (!token) return null;
|
|
36
|
+
|
|
37
|
+
const [payloadBase64, signature] = token.split('.');
|
|
38
|
+
if (!payloadBase64 || !signature) return null;
|
|
39
|
+
|
|
40
|
+
const payload = Buffer.from(payloadBase64, 'base64').toString('utf-8');
|
|
41
|
+
const expectedSignature = createHmac('sha256', SECRET_KEY).update(payload).digest('hex');
|
|
42
|
+
|
|
43
|
+
if (signature !== expectedSignature) return null;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(payload) as SessionUser;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/esm/index.es.js
CHANGED
|
@@ -47,7 +47,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
47
47
|
* @generated
|
|
48
48
|
* @see https://github.com/webgptorg/promptbook
|
|
49
49
|
*/
|
|
50
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-
|
|
50
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-50';
|
|
51
51
|
/**
|
|
52
52
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
53
53
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -86,6 +86,7 @@ const REMOTE_SERVER_URLS = [
|
|
|
86
86
|
*/
|
|
87
87
|
];
|
|
88
88
|
/**
|
|
89
|
+
* TODO: [🐱🚀] Auto-federated server from url in here
|
|
89
90
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
90
91
|
*/
|
|
91
92
|
|
|
@@ -432,6 +433,9 @@ class Color {
|
|
|
432
433
|
if (hex.length === 3) {
|
|
433
434
|
return Color.fromHex3(hex);
|
|
434
435
|
}
|
|
436
|
+
if (hex.length === 4) {
|
|
437
|
+
return Color.fromHex4(hex);
|
|
438
|
+
}
|
|
435
439
|
if (hex.length === 6) {
|
|
436
440
|
return Color.fromHex6(hex);
|
|
437
441
|
}
|
|
@@ -452,6 +456,19 @@ class Color {
|
|
|
452
456
|
const b = parseInt(hex.substr(2, 1), 16) * 16;
|
|
453
457
|
return take(new Color(r, g, b));
|
|
454
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Creates a new Color instance from color in hex format with 4 digits (with alpha channel)
|
|
461
|
+
*
|
|
462
|
+
* @param color in hex for example `09df`
|
|
463
|
+
* @returns Color object
|
|
464
|
+
*/
|
|
465
|
+
static fromHex4(hex) {
|
|
466
|
+
const r = parseInt(hex.substr(0, 1), 16) * 16;
|
|
467
|
+
const g = parseInt(hex.substr(1, 1), 16) * 16;
|
|
468
|
+
const b = parseInt(hex.substr(2, 1), 16) * 16;
|
|
469
|
+
const a = parseInt(hex.substr(3, 1), 16) * 16;
|
|
470
|
+
return take(new Color(r, g, b, a));
|
|
471
|
+
}
|
|
455
472
|
/**
|
|
456
473
|
* Creates a new Color instance from color in hex format with 6 color digits (without alpha channel)
|
|
457
474
|
*
|
|
@@ -642,7 +659,8 @@ class Color {
|
|
|
642
659
|
* @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
|
|
643
660
|
*/
|
|
644
661
|
static isHexColorString(value) {
|
|
645
|
-
return typeof value === 'string' &&
|
|
662
|
+
return (typeof value === 'string' &&
|
|
663
|
+
/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
|
|
646
664
|
}
|
|
647
665
|
/**
|
|
648
666
|
* Creates new Color object
|
|
@@ -983,6 +1001,7 @@ const PROMPTBOOK_COLOR = Color.fromHex('#79EAFD');
|
|
|
983
1001
|
({
|
|
984
1002
|
TITLE: Color.fromHex('#244EA8'),
|
|
985
1003
|
LINE: Color.fromHex('#eeeeee'),
|
|
1004
|
+
SEPARATOR: Color.fromHex('#cccccc'),
|
|
986
1005
|
COMMITMENT: Color.fromHex('#DA0F78'),
|
|
987
1006
|
PARAMETER: Color.fromHex('#8e44ad'),
|
|
988
1007
|
});
|
|
@@ -2385,7 +2404,7 @@ function deepClone(objectValue) {
|
|
|
2385
2404
|
TODO: [🧠] Is there a better implementation?
|
|
2386
2405
|
> const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
2387
2406
|
> for (const propertyName of propertyNames) {
|
|
2388
|
-
> const value = (objectValue as
|
|
2407
|
+
> const value = (objectValue as chococake)[propertyName];
|
|
2389
2408
|
> if (value && typeof value === 'object') {
|
|
2390
2409
|
> deepClone(value);
|
|
2391
2410
|
> }
|
|
@@ -3204,7 +3223,7 @@ class DatabaseError extends Error {
|
|
|
3204
3223
|
}
|
|
3205
3224
|
}
|
|
3206
3225
|
/**
|
|
3207
|
-
* TODO:
|
|
3226
|
+
* TODO: [🐱🚀] Explain that NotFoundError ([🐱🚀] and other specific errors) has priority over DatabaseError in some contexts
|
|
3208
3227
|
*/
|
|
3209
3228
|
|
|
3210
3229
|
/**
|
|
@@ -12972,6 +12991,18 @@ function renderPromptbookMermaid(pipelineJson, options) {
|
|
|
12972
12991
|
* TODO: [🕌] When more than 2 functionalities, split into separate functions
|
|
12973
12992
|
*/
|
|
12974
12993
|
|
|
12994
|
+
/**
|
|
12995
|
+
* Computes SHA-256 hash of the given object
|
|
12996
|
+
*
|
|
12997
|
+
* @public exported from `@promptbook/utils`
|
|
12998
|
+
*/
|
|
12999
|
+
function computeHash(value) {
|
|
13000
|
+
return SHA256(hexEncoder.parse(spaceTrim(valueToString(value)))).toString( /* hex */);
|
|
13001
|
+
}
|
|
13002
|
+
/**
|
|
13003
|
+
* TODO: [🥬][🥬] Use this ACRY
|
|
13004
|
+
*/
|
|
13005
|
+
|
|
12975
13006
|
/**
|
|
12976
13007
|
* Makes first letter of a string lowercase
|
|
12977
13008
|
*
|
|
@@ -15083,7 +15114,7 @@ function $initializeRunCommand(program) {
|
|
|
15083
15114
|
*/
|
|
15084
15115
|
|
|
15085
15116
|
/**
|
|
15086
|
-
*
|
|
15117
|
+
* [🐱🚀]
|
|
15087
15118
|
* Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
|
|
15088
15119
|
*
|
|
15089
15120
|
* You can simply use `RemoteExecutionTools` on client-side javascript and connect to your remote server.
|
|
@@ -15091,11 +15122,11 @@ function $initializeRunCommand(program) {
|
|
|
15091
15122
|
*
|
|
15092
15123
|
* @see https://github.com/webgptorg/promptbook#remote-server
|
|
15093
15124
|
* @public exported from `@promptbook/remote-server`
|
|
15094
|
-
* <- TODO:
|
|
15125
|
+
* <- TODO: [🐱🚀] Change to `@promptbook/agent-server`
|
|
15095
15126
|
*/
|
|
15096
15127
|
async function startAgentServer(options) {
|
|
15097
15128
|
const { port = 4440 } = options;
|
|
15098
|
-
// TODO:
|
|
15129
|
+
// TODO: [🐱🚀] [🌕]
|
|
15099
15130
|
const agentsServerRoot = join(__dirname, '../apps/agents-server');
|
|
15100
15131
|
console.trace(`!!! Starting agents server on port ${port}...`);
|
|
15101
15132
|
console.log(`!!! cwd`, process.cwd());
|
|
@@ -15131,7 +15162,7 @@ function $initializeStartAgentsServerCommand(program) {
|
|
|
15131
15162
|
startServerCommand.alias('start');
|
|
15132
15163
|
startServerCommand.action(handleActionErrors(async (path, cliOptions) => {
|
|
15133
15164
|
const { port: portRaw, reload: isCacheReloaded, verbose: isVerbose } = cliOptions;
|
|
15134
|
-
// TODO:
|
|
15165
|
+
// TODO: [🐱🚀] [🌕] DRY
|
|
15135
15166
|
const port = parseInt(portRaw, 10);
|
|
15136
15167
|
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
15137
15168
|
console.error(colors.red(`Invalid port number: ${portRaw}`));
|
|
@@ -15173,11 +15204,11 @@ function $initializeStartAgentsServerCommand(program) {
|
|
|
15173
15204
|
TODO_USE(tools);
|
|
15174
15205
|
TODO_USE(collection);
|
|
15175
15206
|
*/
|
|
15176
|
-
// TODO:
|
|
15177
|
-
// TODO:
|
|
15178
|
-
// TODO:
|
|
15207
|
+
// TODO: [🐱🚀] Use
|
|
15208
|
+
// TODO: [🐱🚀] Pass collection and tools to the server starter
|
|
15209
|
+
// TODO: [🐱🚀] The Next app should be build during the package build step not here
|
|
15179
15210
|
/*
|
|
15180
|
-
// TODO:
|
|
15211
|
+
// TODO: [🐱🚀] Run this conditionally only in production mode in dev mode use `next dev`
|
|
15181
15212
|
await $execCommand({
|
|
15182
15213
|
cwd: './apps/agents-server',
|
|
15183
15214
|
command: `next build`,
|
|
@@ -20390,11 +20421,12 @@ class OpenAiExecutionTools extends OpenAiCompatibleExecutionTools {
|
|
|
20390
20421
|
*
|
|
20391
20422
|
* This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
|
|
20392
20423
|
*
|
|
20393
|
-
*
|
|
20424
|
+
* Note: [🦖] There are several different things in Promptbook:
|
|
20394
20425
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
20395
20426
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
20396
20427
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
20397
20428
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
20429
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
20398
20430
|
*
|
|
20399
20431
|
* @public exported from `@promptbook/openai`
|
|
20400
20432
|
*/
|
|
@@ -20429,6 +20461,12 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
20429
20461
|
* Calls OpenAI API to use a chat model.
|
|
20430
20462
|
*/
|
|
20431
20463
|
async callChatModel(prompt) {
|
|
20464
|
+
return this.callChatModelStream(prompt, () => { });
|
|
20465
|
+
}
|
|
20466
|
+
/**
|
|
20467
|
+
* Calls OpenAI API to use a chat model with streaming.
|
|
20468
|
+
*/
|
|
20469
|
+
async callChatModelStream(prompt, onProgress) {
|
|
20432
20470
|
var _a, _b, _c;
|
|
20433
20471
|
if (this.options.isVerbose) {
|
|
20434
20472
|
console.info('💬 OpenAI callChatModel call', { prompt });
|
|
@@ -20496,21 +20534,24 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
20496
20534
|
console.info('connect', stream.currentEvent);
|
|
20497
20535
|
}
|
|
20498
20536
|
});
|
|
20499
|
-
|
|
20500
|
-
|
|
20501
|
-
|
|
20502
|
-
this.options.isVerbose &&
|
|
20503
|
-
messageDelta &&
|
|
20504
|
-
messageDelta.content &&
|
|
20505
|
-
messageDelta.content[0] &&
|
|
20506
|
-
messageDelta.content[0].type === 'text'
|
|
20507
|
-
) {
|
|
20508
|
-
console.info('messageDelta', messageDelta.content[0].text?.value);
|
|
20537
|
+
stream.on('textDelta', (textDelta, snapshot) => {
|
|
20538
|
+
if (this.options.isVerbose && textDelta.value) {
|
|
20539
|
+
console.info('textDelta', textDelta.value);
|
|
20509
20540
|
}
|
|
20510
|
-
|
|
20511
|
-
|
|
20541
|
+
const chunk = {
|
|
20542
|
+
content: textDelta.value || '',
|
|
20543
|
+
modelName: 'assistant',
|
|
20544
|
+
timing: {
|
|
20545
|
+
start,
|
|
20546
|
+
complete: $getCurrentDate(),
|
|
20547
|
+
},
|
|
20548
|
+
usage: UNCERTAIN_USAGE,
|
|
20549
|
+
rawPromptContent,
|
|
20550
|
+
rawRequest,
|
|
20551
|
+
rawResponse: snapshot,
|
|
20552
|
+
};
|
|
20553
|
+
onProgress(chunk);
|
|
20512
20554
|
});
|
|
20513
|
-
*/
|
|
20514
20555
|
stream.on('messageCreated', (message) => {
|
|
20515
20556
|
if (this.options.isVerbose) {
|
|
20516
20557
|
console.info('messageCreated', message);
|
|
@@ -20546,7 +20587,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
20546
20587
|
}
|
|
20547
20588
|
return exportJson({
|
|
20548
20589
|
name: 'promptResult',
|
|
20549
|
-
message: `Result of \`OpenAiAssistantExecutionTools.
|
|
20590
|
+
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
|
|
20550
20591
|
order: [],
|
|
20551
20592
|
value: {
|
|
20552
20593
|
content: resultContent,
|
|
@@ -20685,9 +20726,9 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
20685
20726
|
}
|
|
20686
20727
|
const assistant = await client.beta.assistants.create(assistantConfig);
|
|
20687
20728
|
console.log(`✅ Assistant created: ${assistant.id}`);
|
|
20688
|
-
// TODO:
|
|
20689
|
-
// TODO:
|
|
20690
|
-
// TODO:
|
|
20729
|
+
// TODO: [🐱🚀] Try listing existing assistants
|
|
20730
|
+
// TODO: [🐱🚀] Try marking existing assistants by DISCRIMINANT
|
|
20731
|
+
// TODO: [🐱🚀] Allow to update and reconnect to existing assistants
|
|
20691
20732
|
return new OpenAiAssistantExecutionTools({
|
|
20692
20733
|
...this.options,
|
|
20693
20734
|
isCreatingNewAssistantsAllowed: false,
|
|
@@ -23009,6 +23050,60 @@ class MemoryCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
23009
23050
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
23010
23051
|
*/
|
|
23011
23052
|
|
|
23053
|
+
/**
|
|
23054
|
+
* INITIAL MESSAGE commitment definition
|
|
23055
|
+
*
|
|
23056
|
+
* The INITIAL MESSAGE commitment defines the first message that the user sees when opening the chat.
|
|
23057
|
+
* It is used to greet the user and set the tone of the conversation.
|
|
23058
|
+
*
|
|
23059
|
+
* Example usage in agent source:
|
|
23060
|
+
*
|
|
23061
|
+
* ```book
|
|
23062
|
+
* INITIAL MESSAGE Hello! I am ready to help you with your tasks.
|
|
23063
|
+
* ```
|
|
23064
|
+
*
|
|
23065
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
23066
|
+
*/
|
|
23067
|
+
class InitialMessageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
23068
|
+
constructor() {
|
|
23069
|
+
super('INITIAL MESSAGE');
|
|
23070
|
+
}
|
|
23071
|
+
/**
|
|
23072
|
+
* Short one-line description of INITIAL MESSAGE.
|
|
23073
|
+
*/
|
|
23074
|
+
get description() {
|
|
23075
|
+
return 'Defines the **initial message** shown to the user when the chat starts.';
|
|
23076
|
+
}
|
|
23077
|
+
/**
|
|
23078
|
+
* Markdown documentation for INITIAL MESSAGE commitment.
|
|
23079
|
+
*/
|
|
23080
|
+
get documentation() {
|
|
23081
|
+
return spaceTrim$1(`
|
|
23082
|
+
# ${this.type}
|
|
23083
|
+
|
|
23084
|
+
Defines the first message that the user sees when opening the chat. This message is purely for display purposes in the UI and does not inherently become part of the LLM's system prompt context (unless also included via other means).
|
|
23085
|
+
|
|
23086
|
+
## Key aspects
|
|
23087
|
+
|
|
23088
|
+
- Used to greet the user.
|
|
23089
|
+
- Sets the tone of the conversation.
|
|
23090
|
+
- Displayed immediately when the chat interface loads.
|
|
23091
|
+
|
|
23092
|
+
## Examples
|
|
23093
|
+
|
|
23094
|
+
\`\`\`book
|
|
23095
|
+
Support Agent
|
|
23096
|
+
|
|
23097
|
+
PERSONA You are a helpful support agent.
|
|
23098
|
+
INITIAL MESSAGE Hi there! How can I assist you today?
|
|
23099
|
+
\`\`\`
|
|
23100
|
+
`);
|
|
23101
|
+
}
|
|
23102
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
23103
|
+
return requirements;
|
|
23104
|
+
}
|
|
23105
|
+
}
|
|
23106
|
+
|
|
23012
23107
|
/**
|
|
23013
23108
|
* MESSAGE commitment definition
|
|
23014
23109
|
*
|
|
@@ -24170,6 +24265,7 @@ const COMMITMENT_REGISTRY = [
|
|
|
24170
24265
|
new NoteCommitmentDefinition('NONCE'),
|
|
24171
24266
|
new GoalCommitmentDefinition('GOAL'),
|
|
24172
24267
|
new GoalCommitmentDefinition('GOALS'),
|
|
24268
|
+
new InitialMessageCommitmentDefinition(),
|
|
24173
24269
|
new MessageCommitmentDefinition('MESSAGE'),
|
|
24174
24270
|
new MessageCommitmentDefinition('MESSAGES'),
|
|
24175
24271
|
new ScenarioCommitmentDefinition('SCENARIO'),
|
|
@@ -24548,7 +24644,7 @@ function generatePlaceholderAgentProfileImageUrl(agentName) {
|
|
|
24548
24644
|
* @public exported from `@promptbook/core`
|
|
24549
24645
|
*/
|
|
24550
24646
|
function computeAgentHash(agentSource) {
|
|
24551
|
-
return
|
|
24647
|
+
return computeHash(agentSource);
|
|
24552
24648
|
}
|
|
24553
24649
|
|
|
24554
24650
|
/**
|
|
@@ -24597,13 +24693,31 @@ function parseAgentSource(agentSource) {
|
|
|
24597
24693
|
}
|
|
24598
24694
|
personaDescription += commitment.content;
|
|
24599
24695
|
}
|
|
24696
|
+
let initialMessage = null;
|
|
24697
|
+
for (const commitment of parseResult.commitments) {
|
|
24698
|
+
if (commitment.type !== 'INITIAL MESSAGE') {
|
|
24699
|
+
continue;
|
|
24700
|
+
}
|
|
24701
|
+
// Note: Initial message override logic - later overrides earlier
|
|
24702
|
+
// Or should it append? Usually initial message is just one block.
|
|
24703
|
+
// Let's stick to "later overrides earlier" for simplicity, or just take the last one.
|
|
24704
|
+
initialMessage = commitment.content;
|
|
24705
|
+
}
|
|
24600
24706
|
const meta = {};
|
|
24707
|
+
const links = [];
|
|
24601
24708
|
for (const commitment of parseResult.commitments) {
|
|
24709
|
+
if (commitment.type === 'META LINK') {
|
|
24710
|
+
links.push(spaceTrim(commitment.content));
|
|
24711
|
+
continue;
|
|
24712
|
+
}
|
|
24602
24713
|
if (commitment.type !== 'META') {
|
|
24603
24714
|
continue;
|
|
24604
24715
|
}
|
|
24605
24716
|
// Parse META commitments - format is "META TYPE content"
|
|
24606
24717
|
const metaTypeRaw = commitment.content.split(' ')[0] || 'NONE';
|
|
24718
|
+
if (metaTypeRaw === 'LINK') {
|
|
24719
|
+
links.push(spaceTrim(commitment.content.substring(metaTypeRaw.length)));
|
|
24720
|
+
}
|
|
24607
24721
|
const metaType = normalizeTo_camelCase(metaTypeRaw);
|
|
24608
24722
|
meta[metaType] = spaceTrim(commitment.content.substring(metaTypeRaw.length));
|
|
24609
24723
|
}
|
|
@@ -24619,7 +24733,9 @@ function parseAgentSource(agentSource) {
|
|
|
24619
24733
|
agentName: normalizeAgentName(parseResult.agentName || createDefaultAgentName(agentSource)),
|
|
24620
24734
|
agentHash,
|
|
24621
24735
|
personaDescription,
|
|
24736
|
+
initialMessage,
|
|
24622
24737
|
meta,
|
|
24738
|
+
links,
|
|
24623
24739
|
parameters,
|
|
24624
24740
|
};
|
|
24625
24741
|
}
|