@blinkk/root-cms 3.0.1-alpha.1 → 3.0.1-beta.2
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/dist/app.js +12 -6
- package/dist/chunk-ATPWU3CU.js +921 -0
- package/dist/chunk-CRK7N6RR.js +298 -0
- package/dist/{chunk-SNZ4S4IC.js → chunk-F4SODS5S.js} +87 -297
- package/dist/{chunk-R4LKO3EZ.js → chunk-MAZA5B27.js} +16 -18
- package/dist/{chunk-UTSL2E2P.js → chunk-TRM4MQHU.js} +270 -11
- package/dist/{chunk-KDXHFMIH.js → chunk-XLX37FRL.js} +3 -0
- package/dist/cli.js +3 -2
- package/dist/{client-DdB4xpM6.d.ts → client-1puwLgNx.d.ts} +194 -10
- package/dist/client.d.ts +2 -2
- package/dist/client.js +2 -1
- package/dist/core.d.ts +3 -3
- package/dist/core.js +2 -1
- package/dist/functions.js +3 -2
- package/dist/{generate-types-MHWSSOWV.js → generate-types-SPV7I3A5.js} +1 -1
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.js +284 -78
- package/dist/project.d.ts +15 -2
- package/dist/project.js +3 -1
- package/dist/richtext.d.ts +25 -18
- package/dist/richtext.js +98 -43
- package/dist/{schema-rjBOZk-j.d.ts → schema-Db_xODoi.d.ts} +5 -0
- package/dist/ui/signin.css +1 -1
- package/dist/ui/signin.js +3 -3
- package/dist/ui/ui.css +1 -1
- package/dist/ui/ui.js +255 -177
- package/dist/ui/ui.js.LEGAL.txt +2 -1
- package/package.json +10 -7
- package/dist/ai-OZY3JXDH.js +0 -19
- package/dist/altText-RDKJNVGH.js +0 -7
- package/dist/chunk-BBOESYH7.js +0 -192
- package/dist/chunk-T5UK2H24.js +0 -419
package/dist/chunk-T5UK2H24.js
DELETED
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
// core/ai.ts
|
|
2
|
-
import crypto from "crypto";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import { vertexAI } from "@genkit-ai/google-genai";
|
|
6
|
-
import { Timestamp } from "firebase-admin/firestore";
|
|
7
|
-
import { genkit } from "genkit";
|
|
8
|
-
import { logger } from "genkit/logging";
|
|
9
|
-
logger.setLogLevel("warn");
|
|
10
|
-
var DEFAULT_MODEL = "gemini-3-flash-preview";
|
|
11
|
-
var DEFAULT_IMAGEGEN_MODEL = "gemini-2.5-flash-image";
|
|
12
|
-
var LEGACY_MODEL_RENAME = {
|
|
13
|
-
"vertexai/gemini-2.5-flash": "gemini-2.5-flash",
|
|
14
|
-
"vertexai/gemini-2.0-pro": "gemini-2.0-pro"
|
|
15
|
-
};
|
|
16
|
-
async function summarizeDiff(cmsClient, options) {
|
|
17
|
-
const cmsPluginOptions = cmsClient.cmsPlugin.getConfig();
|
|
18
|
-
const firebaseConfig = cmsPluginOptions.firebaseConfig;
|
|
19
|
-
const model = "gemini-2.5-flash";
|
|
20
|
-
const ai = genkit({
|
|
21
|
-
plugins: [
|
|
22
|
-
vertexAI({
|
|
23
|
-
projectId: firebaseConfig.projectId,
|
|
24
|
-
location: firebaseConfig.location || "us-central1"
|
|
25
|
-
})
|
|
26
|
-
]
|
|
27
|
-
});
|
|
28
|
-
const beforeJson = JSON.stringify(options.before ?? null, null, 2);
|
|
29
|
-
const afterJson = JSON.stringify(options.after ?? null, null, 2);
|
|
30
|
-
const systemPrompt = [
|
|
31
|
-
"Summarize CMS document changes in 2-4 bullet points.",
|
|
32
|
-
"Focus on content changes only. Ignore metadata like timestamps.",
|
|
33
|
-
'If no meaningful changes, say "No significant changes."'
|
|
34
|
-
].join("\n");
|
|
35
|
-
const diffPrompt = [
|
|
36
|
-
"Before:",
|
|
37
|
-
beforeJson,
|
|
38
|
-
"",
|
|
39
|
-
"After:",
|
|
40
|
-
afterJson,
|
|
41
|
-
"",
|
|
42
|
-
"What changed?"
|
|
43
|
-
].join("\n");
|
|
44
|
-
const res = await generate(ai, model, {
|
|
45
|
-
messages: [
|
|
46
|
-
{
|
|
47
|
-
role: "system",
|
|
48
|
-
content: [{ text: systemPrompt }]
|
|
49
|
-
}
|
|
50
|
-
],
|
|
51
|
-
prompt: [{ text: diffPrompt }],
|
|
52
|
-
config: {
|
|
53
|
-
// Respond more quickly with less creativity.
|
|
54
|
-
temperature: 0.3
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
return res.text?.trim() || "";
|
|
58
|
-
}
|
|
59
|
-
async function generatePublishMessage(cmsClient, options) {
|
|
60
|
-
const cmsPluginOptions = cmsClient.cmsPlugin.getConfig();
|
|
61
|
-
const firebaseConfig = cmsPluginOptions.firebaseConfig;
|
|
62
|
-
const model = (typeof cmsPluginOptions.experiments?.ai === "object" ? cmsPluginOptions.experiments.ai.model : void 0) || DEFAULT_MODEL;
|
|
63
|
-
const ai = genkit({
|
|
64
|
-
plugins: [
|
|
65
|
-
vertexAI({
|
|
66
|
-
projectId: firebaseConfig.projectId,
|
|
67
|
-
location: firebaseConfig.location || "us-central1"
|
|
68
|
-
})
|
|
69
|
-
]
|
|
70
|
-
});
|
|
71
|
-
const beforeJson = JSON.stringify(options.before ?? null, null, 2);
|
|
72
|
-
const afterJson = JSON.stringify(options.after ?? null, null, 2);
|
|
73
|
-
const systemPrompt = [
|
|
74
|
-
"You are an assistant that generates concise commit-style messages for CMS document changes.",
|
|
75
|
-
"Generate a single short sentence (maximum 60 characters) describing the most important change.",
|
|
76
|
-
'Use imperative mood like "Add feature" or "Update content" or "Fix typo".',
|
|
77
|
-
"Focus on the key content change, ignore structural metadata changes.",
|
|
78
|
-
"Do not use punctuation at the end.",
|
|
79
|
-
'Examples: "Add new hero image", "Update pricing details", "Fix typo in headline"',
|
|
80
|
-
"Include "
|
|
81
|
-
].join("\n");
|
|
82
|
-
const diffPrompt = [
|
|
83
|
-
"Previous version JSON:",
|
|
84
|
-
"```json",
|
|
85
|
-
beforeJson,
|
|
86
|
-
"```",
|
|
87
|
-
"",
|
|
88
|
-
"Updated version JSON:",
|
|
89
|
-
"```json",
|
|
90
|
-
afterJson,
|
|
91
|
-
"```",
|
|
92
|
-
"",
|
|
93
|
-
"Generate a commit message for these changes."
|
|
94
|
-
].join("\n");
|
|
95
|
-
const res = await generate(ai, model, {
|
|
96
|
-
messages: [
|
|
97
|
-
{
|
|
98
|
-
role: "system",
|
|
99
|
-
content: [{ text: systemPrompt }]
|
|
100
|
-
}
|
|
101
|
-
],
|
|
102
|
-
prompt: [{ text: diffPrompt }]
|
|
103
|
-
});
|
|
104
|
-
return res.text?.trim() || "";
|
|
105
|
-
}
|
|
106
|
-
async function generateImage(cmsClient, options) {
|
|
107
|
-
const cmsPluginOptions = cmsClient.cmsPlugin.getConfig();
|
|
108
|
-
const firebaseConfig = cmsPluginOptions.firebaseConfig;
|
|
109
|
-
const model = options.model || DEFAULT_IMAGEGEN_MODEL;
|
|
110
|
-
const ai = genkit({
|
|
111
|
-
plugins: [
|
|
112
|
-
vertexAI({
|
|
113
|
-
projectId: firebaseConfig.projectId,
|
|
114
|
-
location: firebaseConfig.location || "us-central1"
|
|
115
|
-
})
|
|
116
|
-
]
|
|
117
|
-
});
|
|
118
|
-
const res = await generate(ai, model, {
|
|
119
|
-
prompt: [
|
|
120
|
-
{
|
|
121
|
-
text: options.prompt
|
|
122
|
-
}
|
|
123
|
-
],
|
|
124
|
-
config: {
|
|
125
|
-
imageConfig: {
|
|
126
|
-
aspectRatio: options.aspectRatio
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
output: {
|
|
130
|
-
format: "media"
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
if (!res.media) {
|
|
134
|
-
throw new Error("No image generated");
|
|
135
|
-
}
|
|
136
|
-
return res.media.url;
|
|
137
|
-
}
|
|
138
|
-
async function generate(ai, model, options) {
|
|
139
|
-
const generateOptions = {
|
|
140
|
-
...options,
|
|
141
|
-
model: vertexAI.model(model)
|
|
142
|
-
};
|
|
143
|
-
if (model.startsWith("gemini-3")) {
|
|
144
|
-
generateOptions.config = {
|
|
145
|
-
...generateOptions.config,
|
|
146
|
-
location: "global"
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
return ai.generate(generateOptions);
|
|
150
|
-
}
|
|
151
|
-
var Chat = class {
|
|
152
|
-
constructor(chatClient, id, options) {
|
|
153
|
-
this.chatClient = chatClient;
|
|
154
|
-
this.cmsClient = chatClient.cmsClient;
|
|
155
|
-
this.cmsPluginOptions = this.cmsClient.cmsPlugin.getConfig();
|
|
156
|
-
this.id = id;
|
|
157
|
-
this.history = options?.history ?? [];
|
|
158
|
-
this.model = cleanModelName(
|
|
159
|
-
options?.model || (typeof this.cmsPluginOptions.experiments?.ai === "object" ? this.cmsPluginOptions.experiments.ai.model : void 0) || DEFAULT_MODEL
|
|
160
|
-
);
|
|
161
|
-
const firebaseConfig = this.cmsPluginOptions.firebaseConfig;
|
|
162
|
-
this.ai = genkit({
|
|
163
|
-
plugins: [
|
|
164
|
-
vertexAI({
|
|
165
|
-
projectId: firebaseConfig.projectId,
|
|
166
|
-
location: firebaseConfig.location || "us-central1"
|
|
167
|
-
})
|
|
168
|
-
]
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
/** Builds the messages for the AI request. */
|
|
172
|
-
async buildMessages(options) {
|
|
173
|
-
const messages = this.history;
|
|
174
|
-
const hasSystemPrompt = messages.some(
|
|
175
|
-
(msg) => msg.role === "system" && msg.content.length > 0
|
|
176
|
-
);
|
|
177
|
-
if (!hasSystemPrompt) {
|
|
178
|
-
messages.push({
|
|
179
|
-
role: "system",
|
|
180
|
-
content: [{ text: await this.buildSystemPrompt(options) }]
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
if (options.mode === "edit") {
|
|
184
|
-
messages.push({
|
|
185
|
-
role: "user",
|
|
186
|
-
content: [
|
|
187
|
-
{
|
|
188
|
-
text: [
|
|
189
|
-
"The JSON you must edit is:",
|
|
190
|
-
"",
|
|
191
|
-
JSON.stringify(options.editData || {}, null, 2)
|
|
192
|
-
].join("")
|
|
193
|
-
}
|
|
194
|
-
]
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
return messages;
|
|
198
|
-
}
|
|
199
|
-
/** Builds the request sent to the AI based on the `ChatMode`. */
|
|
200
|
-
async buildGenerateRequest(prompt, options) {
|
|
201
|
-
if (options.mode === "edit") {
|
|
202
|
-
return {
|
|
203
|
-
messages: await this.buildMessages(options),
|
|
204
|
-
model: this.model,
|
|
205
|
-
prompt
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
return {
|
|
209
|
-
messages: await this.buildMessages(options),
|
|
210
|
-
model: this.model,
|
|
211
|
-
prompt
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
/** Sends the request to the AI and stores the history in the session and the database. */
|
|
215
|
-
async sendPrompt(prompt, options = {}) {
|
|
216
|
-
const chatRequest = await this.buildGenerateRequest(prompt, options);
|
|
217
|
-
const res = await generate(this.ai, chatRequest.model, {
|
|
218
|
-
messages: chatRequest.messages,
|
|
219
|
-
prompt: Array.isArray(prompt) ? prompt.flat() : prompt
|
|
220
|
-
});
|
|
221
|
-
this.history = res.messages;
|
|
222
|
-
await this.dbDoc().update({
|
|
223
|
-
history: this.history,
|
|
224
|
-
modifiedAt: Timestamp.now()
|
|
225
|
-
});
|
|
226
|
-
if (options.mode === "edit") {
|
|
227
|
-
const aiResponse = res.output;
|
|
228
|
-
if (aiResponse?.data && !aiResponse.message) {
|
|
229
|
-
aiResponse.message = "";
|
|
230
|
-
}
|
|
231
|
-
return aiResponse;
|
|
232
|
-
}
|
|
233
|
-
return { message: res.text, data: null };
|
|
234
|
-
}
|
|
235
|
-
dbDoc() {
|
|
236
|
-
return this.chatClient.dbCollection().doc(this.id);
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Builds the system prompt sent to the AI, based on the `ChatMode` and
|
|
240
|
-
* supplied `SendPromptOptions`. `SendPromptOptions` may contain data or
|
|
241
|
-
* references to information needed to construct the prompt.
|
|
242
|
-
*/
|
|
243
|
-
async buildSystemPrompt(options) {
|
|
244
|
-
const serializedRootConfig = JSON.stringify(
|
|
245
|
-
this.cmsClient.rootConfig,
|
|
246
|
-
null,
|
|
247
|
-
2
|
|
248
|
-
);
|
|
249
|
-
if (options.mode === "edit") {
|
|
250
|
-
const rootDir = process.cwd();
|
|
251
|
-
const rootCmsDefsPath = path.resolve(rootDir, "root-cms.d.ts");
|
|
252
|
-
const rootCmsDefs = fs.existsSync(rootCmsDefsPath) ? fs.readFileSync(rootCmsDefsPath, {
|
|
253
|
-
encoding: "utf8"
|
|
254
|
-
}) : null;
|
|
255
|
-
const text = [(await import("./edit-XX3LAGK6.js")).default];
|
|
256
|
-
if (rootCmsDefs) {
|
|
257
|
-
text.push(
|
|
258
|
-
"Here is the `root-cms.d.ts` file for this project:",
|
|
259
|
-
"```",
|
|
260
|
-
rootCmsDefs,
|
|
261
|
-
"```"
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
return text.join("\n");
|
|
265
|
-
}
|
|
266
|
-
if (options.mode === "altText") {
|
|
267
|
-
return (await import("./altText-RDKJNVGH.js")).default;
|
|
268
|
-
}
|
|
269
|
-
const systemText = [
|
|
270
|
-
`You are an assistant for a headless CMS called Root CMS which is used on a website called ${this.cmsPluginOptions.name || this.cmsPluginOptions.id}. Your job is to answer questions about the docs in the system, and if requested, help suggest changes to the JSON data in the docs. If you don't know the answer, just say that you don't know, don't try to make up an answer. Be friendly and playful with your messaging.`,
|
|
271
|
-
"",
|
|
272
|
-
"Here is the root.config.ts file for the site:",
|
|
273
|
-
"```",
|
|
274
|
-
serializedRootConfig,
|
|
275
|
-
"```",
|
|
276
|
-
"",
|
|
277
|
-
"Here are the docs that exist in the system:"
|
|
278
|
-
];
|
|
279
|
-
const pages = await this.cmsClient.listDocs("Pages", { mode: "draft" });
|
|
280
|
-
pages.docs.forEach((doc) => {
|
|
281
|
-
systemText.push(JSON.stringify(doc));
|
|
282
|
-
});
|
|
283
|
-
return systemText.join("\n");
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
var ChatClient = class {
|
|
287
|
-
constructor(cmsClient, user) {
|
|
288
|
-
this.cmsClient = cmsClient;
|
|
289
|
-
this.user = user;
|
|
290
|
-
}
|
|
291
|
-
async createChat() {
|
|
292
|
-
const chatId = crypto.randomUUID();
|
|
293
|
-
const docRef = this.dbCollection().doc(chatId);
|
|
294
|
-
const chat = new Chat(this, chatId);
|
|
295
|
-
await docRef.set({
|
|
296
|
-
id: chatId,
|
|
297
|
-
createdBy: this.user,
|
|
298
|
-
createdAt: Timestamp.now(),
|
|
299
|
-
modifiedAt: Timestamp.now(),
|
|
300
|
-
model: chat.model
|
|
301
|
-
});
|
|
302
|
-
return chat;
|
|
303
|
-
}
|
|
304
|
-
async getOrCreateChat(chatId) {
|
|
305
|
-
return chatId ? this.getChat(chatId) : this.createChat();
|
|
306
|
-
}
|
|
307
|
-
async getChat(chatId) {
|
|
308
|
-
const docRef = this.dbCollection().doc(chatId);
|
|
309
|
-
const chatDoc = await docRef.get();
|
|
310
|
-
if (!chatDoc.exists) {
|
|
311
|
-
throw new Error(`${chatId} does not exist`);
|
|
312
|
-
}
|
|
313
|
-
const chatData = chatDoc.data() || {};
|
|
314
|
-
return new Chat(this, chatId, {
|
|
315
|
-
history: chatData.history,
|
|
316
|
-
model: chatData.model
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
async listChats(options) {
|
|
320
|
-
const limit = options?.limit || 20;
|
|
321
|
-
const query = this.dbCollection().where("createdBy", "==", this.user).limit(limit).orderBy("createdAt", "desc");
|
|
322
|
-
const res = await query.get();
|
|
323
|
-
return res.docs.map((doc) => doc.data());
|
|
324
|
-
}
|
|
325
|
-
dbCollection() {
|
|
326
|
-
return this.cmsClient.db.collection(
|
|
327
|
-
`Projects/${this.cmsClient.projectId}/Experiments/ai/Chat`
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
function cleanModelName(model) {
|
|
332
|
-
return LEGACY_MODEL_RENAME[model] || model;
|
|
333
|
-
}
|
|
334
|
-
function extractJsonFromResponse(responseText) {
|
|
335
|
-
let jsonText = responseText.trim();
|
|
336
|
-
if (jsonText.startsWith("```")) {
|
|
337
|
-
const lines = jsonText.split("\n");
|
|
338
|
-
jsonText = lines.slice(1, -1).join("\n");
|
|
339
|
-
if (jsonText.startsWith("json")) {
|
|
340
|
-
jsonText = jsonText.substring(4).trim();
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return jsonText;
|
|
344
|
-
}
|
|
345
|
-
async function translateString(cmsClient, options) {
|
|
346
|
-
const cmsPluginOptions = cmsClient.cmsPlugin.getConfig();
|
|
347
|
-
const firebaseConfig = cmsPluginOptions.firebaseConfig;
|
|
348
|
-
const model = (typeof cmsPluginOptions.experiments?.ai === "object" ? cmsPluginOptions.experiments.ai.model : void 0) || DEFAULT_MODEL;
|
|
349
|
-
const ai = genkit({
|
|
350
|
-
plugins: [
|
|
351
|
-
vertexAI({
|
|
352
|
-
projectId: firebaseConfig.projectId,
|
|
353
|
-
location: firebaseConfig.location || "us-central1"
|
|
354
|
-
})
|
|
355
|
-
]
|
|
356
|
-
});
|
|
357
|
-
const systemPrompt = [
|
|
358
|
-
"You are a professional translator assistant.",
|
|
359
|
-
"Translate the given source text into the requested target languages.",
|
|
360
|
-
"Maintain the tone, style, and intent of the original text.",
|
|
361
|
-
"Return ONLY a valid JSON object with locale codes as keys and translations as values.",
|
|
362
|
-
"Do not include any markdown formatting, code blocks, or explanatory text."
|
|
363
|
-
].join("\n");
|
|
364
|
-
const userPromptParts = [
|
|
365
|
-
`Source text: "${options.sourceText}"`,
|
|
366
|
-
""
|
|
367
|
-
];
|
|
368
|
-
if (options.description) {
|
|
369
|
-
userPromptParts.push(`Context/Description: ${options.description}`, "");
|
|
370
|
-
}
|
|
371
|
-
if (options.existingTranslations && Object.keys(options.existingTranslations).length > 0) {
|
|
372
|
-
userPromptParts.push("Existing translations for reference:");
|
|
373
|
-
Object.entries(options.existingTranslations).forEach(
|
|
374
|
-
([locale, translation]) => {
|
|
375
|
-
if (translation) {
|
|
376
|
-
userPromptParts.push(`- ${locale}: "${translation}"`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
);
|
|
380
|
-
userPromptParts.push("");
|
|
381
|
-
}
|
|
382
|
-
userPromptParts.push(
|
|
383
|
-
`Target locales: ${options.targetLocales.join(", ")}`,
|
|
384
|
-
"",
|
|
385
|
-
"Provide translations as a JSON object with locale codes as keys."
|
|
386
|
-
);
|
|
387
|
-
const userPrompt = userPromptParts.join("\n");
|
|
388
|
-
const res = await generate(ai, model, {
|
|
389
|
-
messages: [
|
|
390
|
-
{
|
|
391
|
-
role: "system",
|
|
392
|
-
content: [{ text: systemPrompt }]
|
|
393
|
-
},
|
|
394
|
-
{
|
|
395
|
-
role: "user",
|
|
396
|
-
content: [{ text: userPrompt }]
|
|
397
|
-
}
|
|
398
|
-
]
|
|
399
|
-
});
|
|
400
|
-
const responseText = res.text || "{}";
|
|
401
|
-
const jsonText = extractJsonFromResponse(responseText);
|
|
402
|
-
try {
|
|
403
|
-
const translations = JSON.parse(jsonText);
|
|
404
|
-
return translations;
|
|
405
|
-
} catch (err) {
|
|
406
|
-
console.error("Failed to parse AI translation response:", responseText);
|
|
407
|
-
throw new Error("Invalid response format from AI translation");
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
export {
|
|
412
|
-
summarizeDiff,
|
|
413
|
-
generatePublishMessage,
|
|
414
|
-
generateImage,
|
|
415
|
-
Chat,
|
|
416
|
-
ChatClient,
|
|
417
|
-
extractJsonFromResponse,
|
|
418
|
-
translateString
|
|
419
|
-
};
|