@promptbook/cli 0.103.0-66 â 0.103.0-67
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/TODO.txt +1 -1
- package/apps/agents-server/config.ts +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +1 -1
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +28 -23
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +13 -1
- package/apps/agents-server/src/utils/cdn/classes/DigitalOceanSpaces.ts +119 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +2 -1
- package/esm/index.es.js +1 -1
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +1 -1
|
@@ -29,7 +29,9 @@ const config = ConfigChecker.from({
|
|
|
29
29
|
*
|
|
30
30
|
* Note: When `SERVERS` are used, this URL will be overridden by the server URL.
|
|
31
31
|
*/
|
|
32
|
-
export const NEXT_PUBLIC_SITE_URL = config
|
|
32
|
+
export const NEXT_PUBLIC_SITE_URL = config
|
|
33
|
+
.get('NEXT_PUBLIC_SITE_URL')
|
|
34
|
+
.url()./* <- TODO: !!!! Is it ok not to be required().*/ value;
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* [âī¸] Vercel environment: "development" | "preview" | "production"
|
|
@@ -57,7 +57,7 @@ export function AgentProfileChat({ agentUrl, agentName, fullname, brandColorHex,
|
|
|
57
57
|
// The fallback above matches AgentChat.tsx default.
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
|
-
<div className="w-full h-[
|
|
60
|
+
<div className="w-full h-[calc(100dvh-300px)] min-h-[350px] md:h-[500px]">
|
|
61
61
|
<Chat
|
|
62
62
|
title={`Chat with ${fullname}`}
|
|
63
63
|
participants={[
|
|
@@ -208,11 +208,14 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
208
208
|
)}
|
|
209
209
|
|
|
210
210
|
{/* Main profile content */}
|
|
211
|
-
<div className="relative z-10
|
|
211
|
+
<div className="relative z-10 grid grid-cols-[auto_1fr] gap-x-6 gap-y-4 md:gap-12 max-w-5xl w-full items-start">
|
|
212
212
|
{/* Agent image card (Flippable) */}
|
|
213
|
-
<div
|
|
213
|
+
<div
|
|
214
|
+
className="flex-shrink-0 perspective-1000 group row-start-1 col-start-1 md:row-span-3"
|
|
215
|
+
style={{ perspective: '1000px' }}
|
|
216
|
+
>
|
|
214
217
|
<div
|
|
215
|
-
className="relative w-
|
|
218
|
+
className="relative w-24 md:w-80 transition-all duration-700 transform-style-3d cursor-pointer"
|
|
216
219
|
style={{
|
|
217
220
|
aspectRatio: '1 / 1.618', // Golden Ratio
|
|
218
221
|
transformStyle: 'preserve-3d',
|
|
@@ -222,7 +225,7 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
222
225
|
>
|
|
223
226
|
{/* Front of Card (Image) */}
|
|
224
227
|
<div
|
|
225
|
-
className="absolute inset-0 w-full h-full backface-hidden rounded-3xl shadow-2xl overflow-hidden backdrop-blur-sm"
|
|
228
|
+
className="absolute inset-0 w-full h-full backface-hidden rounded-lg md:rounded-3xl shadow-lg md:shadow-2xl overflow-hidden backdrop-blur-sm"
|
|
226
229
|
style={{
|
|
227
230
|
backfaceVisibility: 'hidden',
|
|
228
231
|
backgroundColor: brandColorDarkHex,
|
|
@@ -234,7 +237,7 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
234
237
|
<img src={imageUrl} alt={fullname} className="w-full h-full object-cover" />
|
|
235
238
|
) : (
|
|
236
239
|
<div
|
|
237
|
-
className="w-full h-full flex items-center justify-center text-8xl font-bold text-white/80"
|
|
240
|
+
className="w-full h-full flex items-center justify-center text-3xl md:text-8xl font-bold text-white/80"
|
|
238
241
|
style={{ backgroundColor: brandColorDarkHex }}
|
|
239
242
|
>
|
|
240
243
|
{fullname.charAt(0).toUpperCase()}
|
|
@@ -242,14 +245,14 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
242
245
|
)}
|
|
243
246
|
|
|
244
247
|
{/* Flip hint icon */}
|
|
245
|
-
<div className="absolute bottom-4 right-4 bg-black/30 p-2 rounded-full text-white/80 backdrop-blur-md opacity-0 group-hover:opacity-100 transition-opacity">
|
|
246
|
-
<RepeatIcon className="w-5 h-5" />
|
|
248
|
+
<div className="absolute bottom-2 md:bottom-4 right-2 md:right-4 bg-black/30 p-1 md:p-2 rounded-full text-white/80 backdrop-blur-md opacity-0 group-hover:opacity-100 transition-opacity">
|
|
249
|
+
<RepeatIcon className="w-3 h-3 md:w-5 md:h-5" />
|
|
247
250
|
</div>
|
|
248
251
|
</div>
|
|
249
252
|
|
|
250
253
|
{/* Back of Card (QR Code) */}
|
|
251
254
|
<div
|
|
252
|
-
className="absolute inset-0 w-full h-full backface-hidden rounded-3xl shadow-2xl overflow-hidden backdrop-blur-sm flex flex-col items-center justify-center p-6"
|
|
255
|
+
className="absolute inset-0 w-full h-full backface-hidden rounded-lg md:rounded-3xl shadow-lg md:shadow-2xl overflow-hidden backdrop-blur-sm flex flex-col items-center justify-center p-2 md:p-6"
|
|
253
256
|
style={{
|
|
254
257
|
backfaceVisibility: 'hidden',
|
|
255
258
|
transform: 'rotateY(180deg)',
|
|
@@ -269,18 +272,18 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
269
272
|
</div>
|
|
270
273
|
|
|
271
274
|
{/* Flip hint icon */}
|
|
272
|
-
<div className="absolute bottom-4 right-4 bg-black/10 p-2 rounded-full text-black/50 backdrop-blur-md">
|
|
273
|
-
<RepeatIcon className="w-5 h-5" />
|
|
275
|
+
<div className="absolute bottom-2 md:bottom-4 right-2 md:right-4 bg-black/10 p-1 md:p-2 rounded-full text-black/50 backdrop-blur-md">
|
|
276
|
+
<RepeatIcon className="w-3 h-3 md:w-5 md:h-5" />
|
|
274
277
|
</div>
|
|
275
278
|
</div>
|
|
276
279
|
</div>
|
|
277
280
|
</div>
|
|
278
281
|
|
|
279
|
-
{/* Agent info */}
|
|
280
|
-
<div className="flex flex-col
|
|
282
|
+
{/* Agent info - Header Area */}
|
|
283
|
+
<div className="row-start-1 col-start-2 flex flex-col justify-center md:justify-start h-full md:h-auto gap-1 md:gap-6">
|
|
281
284
|
{/* Agent name with custom font */}
|
|
282
285
|
<h1
|
|
283
|
-
className="text-
|
|
286
|
+
className="text-2xl md:text-5xl lg:text-6xl font-bold text-gray-900 tracking-tight leading-tight"
|
|
284
287
|
style={{
|
|
285
288
|
textShadow: '0 2px 20px rgba(255, 255, 255, 0.5)',
|
|
286
289
|
}}
|
|
@@ -289,20 +292,22 @@ export function AgentProfile(props: AgentProfileProps) {
|
|
|
289
292
|
</h1>
|
|
290
293
|
|
|
291
294
|
{/* Short description */}
|
|
292
|
-
<p className="text-
|
|
295
|
+
<p className="text-sm md:text-xl text-gray-700 max-w-lg leading-relaxed font-medium line-clamp-3 md:line-clamp-none">
|
|
293
296
|
{personaDescription}
|
|
294
297
|
</p>
|
|
298
|
+
</div>
|
|
295
299
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
{/* Secondary Actions */}
|
|
300
|
-
{!isHeadless && (
|
|
301
|
-
<div className="flex flex-wrap justify-center md:justify-start items-center gap-4 md:gap-6 mt-2">
|
|
302
|
-
{actions}
|
|
303
|
-
</div>
|
|
304
|
-
)}
|
|
300
|
+
{/* Chat Area */}
|
|
301
|
+
<div className="col-span-2 md:col-span-1 md:col-start-2 w-full mt-2 md:mt-0">
|
|
302
|
+
{children}
|
|
305
303
|
</div>
|
|
304
|
+
|
|
305
|
+
{/* Secondary Actions */}
|
|
306
|
+
{!isHeadless && (
|
|
307
|
+
<div className="col-span-2 md:col-span-1 md:col-start-2 flex flex-wrap justify-center md:justify-start items-center gap-4 md:gap-6 mt-2">
|
|
308
|
+
{actions}
|
|
309
|
+
</div>
|
|
310
|
+
)}
|
|
306
311
|
</div>
|
|
307
312
|
|
|
308
313
|
{/* Subtle gradient overlay at bottom */}
|
|
@@ -14,10 +14,22 @@ let cdn: IIFilesStorageWithCdn | null = null;
|
|
|
14
14
|
export function $provideCdnForServer(): IIFilesStorageWithCdn {
|
|
15
15
|
if (!cdn) {
|
|
16
16
|
cdn = new VercelBlobStorage({
|
|
17
|
-
token: process.env.
|
|
17
|
+
token: process.env.VERCEL_BLOB_READ_WRITE_TOKEN!,
|
|
18
18
|
pathPrefix: process.env.NEXT_PUBLIC_CDN_PATH_PREFIX!,
|
|
19
19
|
cdnPublicUrl: new URL(process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!),
|
|
20
20
|
});
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
cdn = new DigitalOceanSpaces({
|
|
24
|
+
bucket: process.env.CDN_BUCKET!,
|
|
25
|
+
pathPrefix: process.env.NEXT_PUBLIC_CDN_PATH_PREFIX!,
|
|
26
|
+
endpoint: process.env.CDN_ENDPOINT!,
|
|
27
|
+
accessKeyId: process.env.CDN_ACCESS_KEY_ID!,
|
|
28
|
+
secretAccessKey: process.env.CDN_SECRET_ACCESS_KEY!,
|
|
29
|
+
cdnPublicUrl: new URL(process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!),
|
|
30
|
+
gzip: true,
|
|
31
|
+
});
|
|
32
|
+
*/
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
return cdn;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { GetObjectCommand, PutObjectCommand, PutObjectCommandInput, S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
import { NotYetImplementedError } from '@promptbook-local/core';
|
|
3
|
+
import { gzip, ungzip } from 'node-gzip';
|
|
4
|
+
import { TODO_USE } from '../../../../../../src/utils/organization/TODO_USE';
|
|
5
|
+
import { validateMimeType } from '../../validators/validateMimeType';
|
|
6
|
+
import type { IFile, IIFilesStorageWithCdn } from '../interfaces/IFilesStorage';
|
|
7
|
+
|
|
8
|
+
type IDigitalOceanSpacesConfig = {
|
|
9
|
+
readonly bucket: string;
|
|
10
|
+
readonly pathPrefix: string;
|
|
11
|
+
readonly endpoint: string;
|
|
12
|
+
readonly accessKeyId: string;
|
|
13
|
+
readonly secretAccessKey: string;
|
|
14
|
+
readonly cdnPublicUrl: URL;
|
|
15
|
+
readonly gzip: boolean;
|
|
16
|
+
|
|
17
|
+
// TODO: [âŗī¸] Probbably prefix should be in this config not on the consumer side
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class DigitalOceanSpaces implements IIFilesStorageWithCdn {
|
|
21
|
+
public get cdnPublicUrl() {
|
|
22
|
+
return this.config.cdnPublicUrl;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private s3: S3Client;
|
|
26
|
+
|
|
27
|
+
public constructor(private readonly config: IDigitalOceanSpacesConfig) {
|
|
28
|
+
this.s3 = new S3Client({
|
|
29
|
+
region: 'auto',
|
|
30
|
+
endpoint: 'https://' + config.endpoint,
|
|
31
|
+
credentials: {
|
|
32
|
+
accessKeyId: config.accessKeyId,
|
|
33
|
+
secretAccessKey: config.secretAccessKey,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public getItemUrl(key: string): URL {
|
|
39
|
+
return new URL(this.config.pathPrefix + '/' + key, this.cdnPublicUrl);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async getItem(key: string): Promise<IFile | null> {
|
|
43
|
+
const parameters = {
|
|
44
|
+
Bucket: this.config.bucket,
|
|
45
|
+
Key: this.config.pathPrefix + '/' + key,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const { Body, ContentType, ContentEncoding } = await this.s3.send(new GetObjectCommand(parameters));
|
|
50
|
+
|
|
51
|
+
// const blob = new Blob([await Body?.transformToByteArray()!]);
|
|
52
|
+
|
|
53
|
+
if (ContentEncoding === 'gzip') {
|
|
54
|
+
return {
|
|
55
|
+
type: validateMimeType(ContentType),
|
|
56
|
+
data: await ungzip(await Body!.transformToByteArray()),
|
|
57
|
+
};
|
|
58
|
+
} else {
|
|
59
|
+
return {
|
|
60
|
+
type: validateMimeType(ContentType),
|
|
61
|
+
data: (await Body!.transformToByteArray()) as Buffer,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error instanceof Error && error.name.match(/^NoSuchKey/)) {
|
|
66
|
+
return null;
|
|
67
|
+
} else {
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async removeItem(key: string): Promise<void> {
|
|
74
|
+
TODO_USE(key);
|
|
75
|
+
throw new NotYetImplementedError(`DigitalOceanSpaces.removeItem is not implemented yet`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public async setItem(key: string, file: IFile): Promise<void> {
|
|
79
|
+
// TODO: Put putObjectRequestAdditional into processedFile
|
|
80
|
+
const putObjectRequestAdditional: Partial<PutObjectCommandInput> = {};
|
|
81
|
+
|
|
82
|
+
let processedFile: IFile;
|
|
83
|
+
if (this.config.gzip) {
|
|
84
|
+
const gzipped = await gzip(file.data);
|
|
85
|
+
const sizePercentageAfterCompression = gzipped.byteLength / file.data.byteLength;
|
|
86
|
+
if (sizePercentageAfterCompression < 0.7) {
|
|
87
|
+
// consolex.log(`Gzipping ${key} (${Math.floor(sizePercentageAfterCompression * 100)}%)`);
|
|
88
|
+
processedFile = { ...file, data: gzipped };
|
|
89
|
+
putObjectRequestAdditional.ContentEncoding = 'gzip';
|
|
90
|
+
} else {
|
|
91
|
+
processedFile = file;
|
|
92
|
+
// consolex.log(`NOT Gzipping ${key} (${Math.floor(sizePercentageAfterCompression * 100)}%)`);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
processedFile = file;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const uploadResult = await this.s3.send(
|
|
99
|
+
new PutObjectCommand({
|
|
100
|
+
Bucket: this.config.bucket,
|
|
101
|
+
Key: this.config.pathPrefix + '/' + key,
|
|
102
|
+
ContentType: processedFile.type,
|
|
103
|
+
...putObjectRequestAdditional,
|
|
104
|
+
Body: processedFile.data,
|
|
105
|
+
// TODO: Public read access / just private to extending class
|
|
106
|
+
ACL: 'public-read',
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (!uploadResult.ETag) {
|
|
111
|
+
throw new Error(`Upload result does not contain ETag`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* TODO: Implement Read-only mode
|
|
118
|
+
* TODO: [âšī¸] Unite with `PromptbookStorage` and move to `/src/...`
|
|
119
|
+
*/
|
|
@@ -51,12 +51,13 @@ export class VercelBlobStorage implements IIFilesStorageWithCdn {
|
|
|
51
51
|
|
|
52
52
|
public async setItem(key: string, file: IFile): Promise<void> {
|
|
53
53
|
const path = this.config.pathPrefix ? `${this.config.pathPrefix}/${key}` : key;
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
await put(path, file.data, {
|
|
56
56
|
access: 'public',
|
|
57
57
|
addRandomSuffix: false,
|
|
58
58
|
contentType: file.type,
|
|
59
59
|
token: this.config.token,
|
|
60
|
+
allowOverwrite: true, // <- TODO: This is inefficient, we should check first if the file exists and only then decide to overwrite or not
|
|
60
61
|
// Note: We rely on Vercel Blob for compression
|
|
61
62
|
});
|
|
62
63
|
}
|
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-67';
|
|
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
|
|
@@ -15,7 +15,7 @@ export declare const BOOK_LANGUAGE_VERSION: string_semantic_version;
|
|
|
15
15
|
export declare const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version;
|
|
16
16
|
/**
|
|
17
17
|
* Represents the version string of the Promptbook engine.
|
|
18
|
-
* It follows semantic versioning (e.g., `0.103.0-
|
|
18
|
+
* It follows semantic versioning (e.g., `0.103.0-66`).
|
|
19
19
|
*
|
|
20
20
|
* @generated
|
|
21
21
|
*/
|
package/package.json
CHANGED
package/umd/index.umd.js
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
* @generated
|
|
57
57
|
* @see https://github.com/webgptorg/promptbook
|
|
58
58
|
*/
|
|
59
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-
|
|
59
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-67';
|
|
60
60
|
/**
|
|
61
61
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
62
62
|
* Note: [đ] Ignore a discrepancy between file name and entity name
|