@nextblock-cms/cortex 0.18.1 → 0.18.3
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/index.cjs.js +1 -1
- package/index.es.js +207 -204
- package/lib/ai-global-agent-site-tools.cjs.js +3 -2
- package/lib/ai-global-agent-site-tools.d.ts +12 -4
- package/lib/ai-global-agent-site-tools.es.js +411 -369
- package/lib/ai-model-registry.cjs.js +2 -3
- package/lib/ai-model-registry.d.ts +29 -6
- package/lib/ai-model-registry.es.js +152 -107
- package/lib/mcp-tool-registry.cjs.js +3 -3
- package/lib/mcp-tool-registry.es.js +5 -5
- package/lib/site-brief.cjs.js +2 -2
- package/lib/site-brief.d.ts +9 -0
- package/lib/site-brief.es.js +10 -6
- package/package.json +4 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { tool as
|
|
2
|
-
import { requireActorRole as
|
|
3
|
-
import { cortexSiteBriefInputSchema as
|
|
1
|
+
import { tool as L } from "ai";
|
|
2
|
+
import { requireActorRole as se } from "./ai-global-agent-theming-tools.es.js";
|
|
3
|
+
import { cortexSiteBriefInputSchema as le, safeParseCortexSiteBrief as de, CORTEX_AI_SITE_BRIEF_SETTING_KEY as V, cortexBuildSessionSchema as ue, CORTEX_AI_BUILD_SESSION_SETTING_KEY as G, cortexSiteBriefSchema as we, isCortexBuildSessionUsable as ke } from "./site-brief.es.js";
|
|
4
4
|
import "./zod-config.es.js";
|
|
5
|
-
import { z as
|
|
5
|
+
import { z as l } from "zod";
|
|
6
6
|
const te = /* @__PURE__ */ new Set([
|
|
7
7
|
// pages: home, articles, contact, privacy policy, terms of service
|
|
8
8
|
"0098e4f0-e5f3-4e28-acfc-bb9fdb3a4a6b",
|
|
@@ -30,7 +30,7 @@ const te = /* @__PURE__ */ new Set([
|
|
|
30
30
|
"images/nextblock-logo-small.webp",
|
|
31
31
|
"images/programmer-upscaled.webp",
|
|
32
32
|
"images/update_nextblock.webp"
|
|
33
|
-
]),
|
|
33
|
+
]), ve = "NextBlock™ CMS", $e = "nextblock cms", N = "home", Te = [
|
|
34
34
|
"active_logo_id",
|
|
35
35
|
"footer_copyright",
|
|
36
36
|
"footer_show_attribution",
|
|
@@ -38,33 +38,33 @@ const te = /* @__PURE__ */ new Set([
|
|
|
38
38
|
"site_keywords",
|
|
39
39
|
"site_title"
|
|
40
40
|
];
|
|
41
|
-
function
|
|
41
|
+
function O(t) {
|
|
42
42
|
if (!t?.supabase)
|
|
43
43
|
throw new Error("No database connection is available for this tool.");
|
|
44
44
|
return t.supabase;
|
|
45
45
|
}
|
|
46
|
-
function
|
|
46
|
+
function h(t) {
|
|
47
47
|
return t ? t instanceof Error ? t.message : typeof t == "object" && "message" in t ? String(t.message) : String(t) : "unknown error";
|
|
48
48
|
}
|
|
49
49
|
function ie(t) {
|
|
50
50
|
return t === null || typeof t != "object" ? JSON.stringify(t) : Array.isArray(t) ? `[${t.map((i) => ie(i)).join(",")}]` : `{${Object.keys(t).sort().map((i) => `${JSON.stringify(i)}:${ie(t[i])}`).join(",")}}`;
|
|
51
51
|
}
|
|
52
|
-
function
|
|
52
|
+
function Ce(t) {
|
|
53
53
|
let i = 2166136261;
|
|
54
|
-
const
|
|
55
|
-
for (let
|
|
56
|
-
i ^=
|
|
54
|
+
const a = ie(t);
|
|
55
|
+
for (let s = 0; s < a.length; s++)
|
|
56
|
+
i ^= a.charCodeAt(s), i = Math.imul(i, 16777619);
|
|
57
57
|
return (i >>> 0).toString(16).padStart(8, "0");
|
|
58
58
|
}
|
|
59
59
|
function ee(t) {
|
|
60
60
|
return t.replace(/\s+/g, " ").trim().toUpperCase();
|
|
61
61
|
}
|
|
62
|
-
function
|
|
62
|
+
function ae(t) {
|
|
63
63
|
if (t.context?.skipConfirmation)
|
|
64
64
|
return null;
|
|
65
65
|
const i = `${ee(
|
|
66
66
|
`CONFIRM ${t.action} ${t.subject}`
|
|
67
|
-
)} #${
|
|
67
|
+
)} #${Ce(t.payload)}`;
|
|
68
68
|
return ee(t.context?.latestUserMessage || "").includes(ee(i)) ? null : {
|
|
69
69
|
confirmationPhrase: i,
|
|
70
70
|
mutationExecuted: !1,
|
|
@@ -77,179 +77,180 @@ function xe() {
|
|
|
77
77
|
return globalThis.crypto?.randomUUID?.() || `id-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
78
78
|
}
|
|
79
79
|
function ce(t, i = 200) {
|
|
80
|
-
const
|
|
81
|
-
for (let
|
|
82
|
-
|
|
83
|
-
return
|
|
80
|
+
const a = [];
|
|
81
|
+
for (let s = 0; s < t.length; s += i)
|
|
82
|
+
a.push(t.slice(s, s + i));
|
|
83
|
+
return a;
|
|
84
84
|
}
|
|
85
|
-
function
|
|
86
|
-
let
|
|
87
|
-
if (!
|
|
85
|
+
function oe(t, i = []) {
|
|
86
|
+
let a = t?.revalidatePath;
|
|
87
|
+
if (!a)
|
|
88
88
|
try {
|
|
89
|
-
|
|
89
|
+
a = require("next/cache").revalidatePath;
|
|
90
90
|
} catch {
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
try {
|
|
94
|
-
|
|
95
|
-
for (const
|
|
96
|
-
s
|
|
94
|
+
a("/", "layout");
|
|
95
|
+
for (const s of i)
|
|
96
|
+
a(s);
|
|
97
97
|
} catch {
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
async function
|
|
101
|
-
const { data:
|
|
102
|
-
if (
|
|
103
|
-
throw new Error(`Could not read the site settings: ${
|
|
104
|
-
return new Map((Array.isArray(
|
|
100
|
+
async function Ee(t, i) {
|
|
101
|
+
const { data: a, error: s } = await t.from("site_settings").select("key, value").in("key", [...i]);
|
|
102
|
+
if (s)
|
|
103
|
+
throw new Error(`Could not read the site settings: ${h(s)}`);
|
|
104
|
+
return new Map((Array.isArray(a) ? a : []).map((n) => [n.key, n.value]));
|
|
105
105
|
}
|
|
106
|
-
async function
|
|
107
|
-
const { error:
|
|
108
|
-
if (
|
|
109
|
-
throw new Error(`Could not save the "${i}" setting: ${
|
|
106
|
+
async function k(t, i, a) {
|
|
107
|
+
const { error: s } = await t.from("site_settings").upsert({ key: i, value: a });
|
|
108
|
+
if (s)
|
|
109
|
+
throw new Error(`Could not save the "${i}" setting: ${h(s)}`);
|
|
110
110
|
}
|
|
111
|
-
async function
|
|
112
|
-
let
|
|
113
|
-
for (const
|
|
114
|
-
const { error: r } = await t.from(i).delete().in(
|
|
111
|
+
async function I(t, i, a, s) {
|
|
112
|
+
let n = 0;
|
|
113
|
+
for (const o of ce(s)) {
|
|
114
|
+
const { error: r } = await t.from(i).delete().in(a, o);
|
|
115
115
|
if (r)
|
|
116
|
-
throw new Error(`Could not delete from ${i}: ${
|
|
117
|
-
|
|
116
|
+
throw new Error(`Could not delete from ${i}: ${h(r)}`);
|
|
117
|
+
n += o.length;
|
|
118
118
|
}
|
|
119
|
-
return
|
|
119
|
+
return n;
|
|
120
120
|
}
|
|
121
|
-
function
|
|
121
|
+
function Ie(t) {
|
|
122
122
|
return !t || typeof t != "object" ? !1 : Object.values(t).some(
|
|
123
|
-
(i) => typeof i == "string" && i.toLowerCase().includes(
|
|
123
|
+
(i) => typeof i == "string" && i.toLowerCase().includes($e)
|
|
124
124
|
);
|
|
125
125
|
}
|
|
126
|
-
async function
|
|
127
|
-
const { data: i, error:
|
|
128
|
-
return
|
|
126
|
+
async function ne(t) {
|
|
127
|
+
const { data: i, error: a } = await t.from("site_settings").select("value").eq("key", V).maybeSingle();
|
|
128
|
+
return a ? null : de(i?.value);
|
|
129
129
|
}
|
|
130
|
-
const ge =
|
|
131
|
-
brief:
|
|
130
|
+
const ge = l.strictObject({
|
|
131
|
+
brief: le.describe(
|
|
132
132
|
'The fields you learned. With mode "merge" (default) they are layered over the saved brief, so you can save after every answer.'
|
|
133
133
|
),
|
|
134
|
-
mode:
|
|
134
|
+
mode: l.enum(["merge", "replace"]).default("merge")
|
|
135
135
|
});
|
|
136
|
-
async function
|
|
137
|
-
const
|
|
138
|
-
Object.entries(
|
|
139
|
-
),
|
|
140
|
-
...
|
|
136
|
+
async function Q(t, i) {
|
|
137
|
+
const a = ge.parse(t), s = O(i), n = a.mode === "merge" ? await ne(s) : null, o = new Set(Object.keys(t.brief ?? {})), r = Object.fromEntries(
|
|
138
|
+
Object.entries(a.brief).filter(([f]) => o.has(f))
|
|
139
|
+
), d = we.parse({
|
|
140
|
+
...n ?? {},
|
|
141
141
|
...r,
|
|
142
142
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
143
143
|
});
|
|
144
|
-
return await
|
|
145
|
-
brief:
|
|
146
|
-
created: !
|
|
144
|
+
return await k(s, V, d), {
|
|
145
|
+
brief: d,
|
|
146
|
+
created: !n,
|
|
147
147
|
mutationExecuted: !0,
|
|
148
148
|
success: !0
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
async function pe(t) {
|
|
152
|
-
const { data: i, error:
|
|
153
|
-
if (
|
|
152
|
+
const { data: i, error: a } = await t.from("site_settings").select("value").eq("key", G).maybeSingle();
|
|
153
|
+
if (a)
|
|
154
154
|
return null;
|
|
155
|
-
const
|
|
156
|
-
return
|
|
155
|
+
const s = ue.safeParse(i?.value);
|
|
156
|
+
return s.success ? s.data : null;
|
|
157
157
|
}
|
|
158
|
-
async function
|
|
159
|
-
const
|
|
160
|
-
return
|
|
158
|
+
async function He(t, i) {
|
|
159
|
+
const a = await pe(t);
|
|
160
|
+
return ke(a, { actorUserId: i }) ? a : null;
|
|
161
161
|
}
|
|
162
|
-
async function
|
|
162
|
+
async function Ne(t) {
|
|
163
163
|
const { error: i } = await t.from("site_settings").delete().eq("key", G);
|
|
164
164
|
if (i)
|
|
165
|
-
throw new Error(`Could not close the build session: ${
|
|
165
|
+
throw new Error(`Could not close the build session: ${h(i)}`);
|
|
166
166
|
}
|
|
167
|
-
const me =
|
|
168
|
-
includeMedia:
|
|
169
|
-
includeNavigation:
|
|
170
|
-
limit:
|
|
167
|
+
const me = l.strictObject({
|
|
168
|
+
includeMedia: l.boolean().default(!1).describe("Also list the most recent media library entries (id, file name, alt text)."),
|
|
169
|
+
includeNavigation: l.boolean().default(!0),
|
|
170
|
+
limit: l.number().int().min(10).max(300).default(120).describe("Maximum pages, posts, and products to list.")
|
|
171
171
|
});
|
|
172
|
-
function
|
|
173
|
-
const
|
|
174
|
-
for (const
|
|
175
|
-
const r = String(
|
|
176
|
-
|
|
172
|
+
function Ae(t, i) {
|
|
173
|
+
const a = { footer: {}, header: {} }, s = /* @__PURE__ */ new Map();
|
|
174
|
+
for (const o of t) {
|
|
175
|
+
const r = String(o.menu_key).toUpperCase() === "FOOTER" ? "footer" : "header";
|
|
176
|
+
s.set(Number(o.id), {
|
|
177
177
|
children: [],
|
|
178
|
-
id: Number(
|
|
179
|
-
label: String(
|
|
180
|
-
languageCode: i.get(Number(
|
|
178
|
+
id: Number(o.id),
|
|
179
|
+
label: String(o.label ?? ""),
|
|
180
|
+
languageCode: i.get(Number(o.language_id)) ?? String(o.language_id),
|
|
181
181
|
menu: r,
|
|
182
|
-
order: Number(
|
|
183
|
-
parentId:
|
|
184
|
-
url: String(
|
|
182
|
+
order: Number(o.order ?? 0),
|
|
183
|
+
parentId: o.parent_id === null || o.parent_id === void 0 ? null : Number(o.parent_id),
|
|
184
|
+
url: String(o.url ?? "")
|
|
185
185
|
});
|
|
186
186
|
}
|
|
187
|
-
const
|
|
188
|
-
for (const
|
|
189
|
-
const r =
|
|
187
|
+
const n = [...s.values()].sort((o, r) => o.order - r.order);
|
|
188
|
+
for (const o of n) {
|
|
189
|
+
const r = o.parentId === null ? null : s.get(o.parentId);
|
|
190
190
|
if (r) {
|
|
191
|
-
r.children.push({ children:
|
|
191
|
+
r.children.push({ children: o.children, id: o.id, label: o.label, url: o.url });
|
|
192
192
|
continue;
|
|
193
193
|
}
|
|
194
|
-
(
|
|
194
|
+
(a[o.menu][o.languageCode] ??= []).push({ children: o.children, id: o.id, label: o.label, url: o.url });
|
|
195
195
|
}
|
|
196
|
-
return
|
|
196
|
+
return a;
|
|
197
197
|
}
|
|
198
|
-
async function
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
198
|
+
async function De(t, i) {
|
|
199
|
+
const a = me.parse(t), s = O(i), [n, o, r, d, f, c, g] = await Promise.all([
|
|
200
|
+
s.from("languages").select("id, code, name, is_default, is_active").order("id"),
|
|
201
|
+
Ee(s, [...Te, V, G]),
|
|
202
|
+
// `count: 'exact'` so the totals are the site's, not the capped list's.
|
|
203
|
+
s.from("pages").select("id, slug, title, status, language_id, translation_group_id, author_id, updated_at", { count: "exact" }).order("id").limit(a.limit),
|
|
204
|
+
s.from("posts").select("id, slug, title, status, language_id, translation_group_id, author_id, published_at", { count: "exact" }).order("id").limit(a.limit),
|
|
205
|
+
s.from("site_themes").select("slug, name, is_default, is_active, color_scheme").order("sort_order"),
|
|
206
|
+
s.from("custom_block_definitions").select("slug, name, fields").order("name"),
|
|
207
|
+
s.from("content_drafts").select("parent_type, parent_id, updated_at")
|
|
207
208
|
]);
|
|
208
|
-
for (const [e,
|
|
209
|
-
["languages",
|
|
209
|
+
for (const [e, u] of [
|
|
210
|
+
["languages", n],
|
|
210
211
|
["pages", r],
|
|
211
|
-
["posts",
|
|
212
|
-
["themes",
|
|
213
|
-
["custom blocks",
|
|
214
|
-
["drafts",
|
|
212
|
+
["posts", d],
|
|
213
|
+
["themes", f],
|
|
214
|
+
["custom blocks", c],
|
|
215
|
+
["drafts", g]
|
|
215
216
|
])
|
|
216
|
-
if (
|
|
217
|
-
throw new Error(`Could not read the ${e}: ${
|
|
218
|
-
const
|
|
219
|
-
let
|
|
217
|
+
if (u?.error)
|
|
218
|
+
throw new Error(`Could not read the ${e}: ${h(u.error)}`);
|
|
219
|
+
const p = n.data ?? [], _ = new Map(p.map((e) => [Number(e.id), String(e.code)])), M = p.find((e) => e.is_default) ?? p[0] ?? null;
|
|
220
|
+
let v = [];
|
|
220
221
|
try {
|
|
221
|
-
const { data: e } = await
|
|
222
|
-
|
|
222
|
+
const { data: e } = await s.from("products").select("id, slug, title, status, language_id").order("created_at", { ascending: !1 }).limit(a.limit);
|
|
223
|
+
v = Array.isArray(e) ? e : [];
|
|
223
224
|
} catch {
|
|
224
|
-
|
|
225
|
+
v = [];
|
|
225
226
|
}
|
|
226
|
-
const
|
|
227
|
+
const $ = /* @__PURE__ */ new Map();
|
|
227
228
|
{
|
|
228
|
-
const { data: e } = await
|
|
229
|
-
for (const
|
|
230
|
-
const
|
|
231
|
-
|
|
229
|
+
const { data: e } = await s.from("blocks").select("page_id, post_id").limit(5e3);
|
|
230
|
+
for (const u of Array.isArray(e) ? e : []) {
|
|
231
|
+
const b = u.page_id ? `page:${u.page_id}` : u.post_id ? `post:${u.post_id}` : null;
|
|
232
|
+
b && $.set(b, ($.get(b) ?? 0) + 1);
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
let F = null;
|
|
235
|
-
if (
|
|
236
|
-
const { data: e, error:
|
|
237
|
-
if (
|
|
238
|
-
throw new Error(`Could not read the navigation: ${
|
|
239
|
-
F =
|
|
236
|
+
if (a.includeNavigation) {
|
|
237
|
+
const { data: e, error: u } = await s.from("navigation_items").select("id, label, url, menu_key, language_id, parent_id, order, page_id").order("order");
|
|
238
|
+
if (u)
|
|
239
|
+
throw new Error(`Could not read the navigation: ${h(u)}`);
|
|
240
|
+
F = Ae(Array.isArray(e) ? e : [], _);
|
|
240
241
|
}
|
|
241
|
-
const { data:
|
|
242
|
-
blockCount:
|
|
242
|
+
const { data: H } = await s.from("media").select("id, object_key, file_name, description, created_at").order("created_at", { ascending: !1 }).limit(500), y = Array.isArray(H) ? H : [], T = y.filter((e) => J.has(String(e.object_key))).length, { data: C } = await s.from("logos").select("id, name, media_id, created_at").order("created_at", { ascending: !1 }), j = Array.isArray(C) ? C : [], K = typeof o.get("active_logo_id") == "string" ? o.get("active_logo_id") : null, w = (K ? j.find((e) => e.id === K) : null) ?? j[0] ?? null, A = w ? y.find((e) => e.id === w.media_id) ?? null : null, D = A ? String(A.object_key) : null, B = (r.data ?? []).map((e) => ({
|
|
243
|
+
blockCount: $.get(`page:${e.id}`) ?? 0,
|
|
243
244
|
id: Number(e.id),
|
|
244
|
-
isHome: e.slug ===
|
|
245
|
+
isHome: e.slug === N && (!M || Number(e.language_id) === Number(M.id)),
|
|
245
246
|
isSeeded: te.has(String(e.translation_group_id)),
|
|
246
247
|
languageCode: _.get(Number(e.language_id)) ?? String(e.language_id),
|
|
247
248
|
slug: String(e.slug),
|
|
248
249
|
status: String(e.status),
|
|
249
250
|
title: String(e.title ?? ""),
|
|
250
251
|
translationGroupId: e.translation_group_id ? String(e.translation_group_id) : null
|
|
251
|
-
})),
|
|
252
|
-
blockCount:
|
|
252
|
+
})), P = (d.data ?? []).map((e) => ({
|
|
253
|
+
blockCount: $.get(`post:${e.id}`) ?? 0,
|
|
253
254
|
id: Number(e.id),
|
|
254
255
|
isSeeded: te.has(String(e.translation_group_id)),
|
|
255
256
|
languageCode: _.get(Number(e.language_id)) ?? String(e.language_id),
|
|
@@ -258,54 +259,54 @@ async function Ae(t, i) {
|
|
|
258
259
|
status: String(e.status),
|
|
259
260
|
title: String(e.title ?? ""),
|
|
260
261
|
translationGroupId: e.translation_group_id ? String(e.translation_group_id) : null
|
|
261
|
-
})), U = typeof
|
|
262
|
-
copyright:
|
|
263
|
-
logo: !!(
|
|
264
|
-
media:
|
|
262
|
+
})), U = typeof o.get("site_title") == "string" ? o.get("site_title") : "", Y = o.get("footer_copyright"), m = {
|
|
263
|
+
copyright: Ie(Y),
|
|
264
|
+
logo: !!(D && J.has(D)),
|
|
265
|
+
media: T,
|
|
265
266
|
pages: B.filter((e) => e.isSeeded).length,
|
|
266
|
-
posts:
|
|
267
|
-
siteTitle: U.trim() ===
|
|
268
|
-
},
|
|
269
|
-
return
|
|
270
|
-
`There is no "${
|
|
271
|
-
),
|
|
272
|
-
`NextBlock demo content is still present (${
|
|
273
|
-
), q ||
|
|
267
|
+
posts: P.filter((e) => e.isSeeded).length,
|
|
268
|
+
siteTitle: U.trim() === ve
|
|
269
|
+
}, W = m.copyright || m.logo || m.media > 0 || m.pages > 0 || m.posts > 0 || m.siteTitle, q = de(o.get(V)), X = ue.safeParse(o.get(G)), x = X.success ? X.data : null, R = B.find((e) => e.isHome) ?? null, S = [];
|
|
270
|
+
return R ? R.blockCount === 0 && S.push("The home page exists but has no blocks. Fill it with rewrite_page_draft then publish_content_draft.") : S.push(
|
|
271
|
+
`There is no "${N}" page in the default language, so "/" returns 404. Create one with create_cms_page (slug "${N}", status "published").`
|
|
272
|
+
), W && S.push(
|
|
273
|
+
`NextBlock demo content is still present (${m.pages} seeded pages, ${m.posts} seeded posts, ${m.media} bundled images${m.logo ? ", the NextBlock logo" : ""}${m.siteTitle ? ", the default site title" : ""}${m.copyright ? ", the default copyright" : ""}). Remove it with reset_site_content, or pass reset to start_site_build.`
|
|
274
|
+
), q || S.push("No site brief is saved yet. Interview the client and record the answers with save_site_brief."), {
|
|
274
275
|
brief: q,
|
|
275
276
|
buildSession: x ? { expiresAt: x.expiresAt, id: x.id, summary: x.summary } : null,
|
|
276
277
|
counts: {
|
|
277
|
-
customBlocks: (
|
|
278
|
-
drafts: (
|
|
278
|
+
customBlocks: (c.data ?? []).length,
|
|
279
|
+
drafts: (g.data ?? []).length,
|
|
279
280
|
media: y.length,
|
|
280
|
-
pages: B.length,
|
|
281
|
-
posts:
|
|
282
|
-
products:
|
|
281
|
+
pages: typeof r.count == "number" ? r.count : B.length,
|
|
282
|
+
posts: typeof d.count == "number" ? d.count : P.length,
|
|
283
|
+
products: v.length
|
|
283
284
|
},
|
|
284
|
-
customBlocks: (
|
|
285
|
+
customBlocks: (c.data ?? []).map((e) => ({
|
|
285
286
|
fieldCount: Array.isArray(e.fields) ? e.fields.length : 0,
|
|
286
|
-
fields: Array.isArray(e.fields) ? e.fields.map((
|
|
287
|
+
fields: Array.isArray(e.fields) ? e.fields.map((u) => ({ key: u?.key, required: u?.required === !0, type: u?.type })) : [],
|
|
287
288
|
name: String(e.name ?? ""),
|
|
288
289
|
slug: String(e.slug)
|
|
289
290
|
})),
|
|
290
|
-
drafts: (
|
|
291
|
+
drafts: (g.data ?? []).map((e) => ({
|
|
291
292
|
parentId: Number(e.parent_id),
|
|
292
293
|
parentType: String(e.parent_type)
|
|
293
294
|
})),
|
|
294
|
-
homePage:
|
|
295
|
+
homePage: R,
|
|
295
296
|
identity: {
|
|
296
|
-
activeLogo:
|
|
297
|
-
id: String(
|
|
298
|
-
isSeeded:
|
|
299
|
-
name: String(
|
|
300
|
-
objectKey:
|
|
297
|
+
activeLogo: w ? {
|
|
298
|
+
id: String(w.id),
|
|
299
|
+
isSeeded: m.logo,
|
|
300
|
+
name: String(w.name ?? ""),
|
|
301
|
+
objectKey: D
|
|
301
302
|
} : null,
|
|
302
|
-
footerCopyright:
|
|
303
|
-
footerShowAttribution:
|
|
304
|
-
siteDescription: typeof
|
|
305
|
-
siteKeywords: typeof
|
|
303
|
+
footerCopyright: Y ?? null,
|
|
304
|
+
footerShowAttribution: o.get("footer_show_attribution") !== !1,
|
|
305
|
+
siteDescription: typeof o.get("site_description") == "string" ? o.get("site_description") : "",
|
|
306
|
+
siteKeywords: typeof o.get("site_keywords") == "string" ? o.get("site_keywords") : "",
|
|
306
307
|
siteTitle: U
|
|
307
308
|
},
|
|
308
|
-
languages:
|
|
309
|
+
languages: p.map((e) => ({
|
|
309
310
|
code: String(e.code),
|
|
310
311
|
id: Number(e.id),
|
|
311
312
|
isActive: e.is_active !== !1,
|
|
@@ -314,28 +315,28 @@ async function Ae(t, i) {
|
|
|
314
315
|
})),
|
|
315
316
|
media: {
|
|
316
317
|
count: y.length,
|
|
317
|
-
sample:
|
|
318
|
+
sample: a.includeMedia ? y.slice(0, 40).map((e) => ({
|
|
318
319
|
altText: e.description ?? null,
|
|
319
320
|
fileName: e.file_name ?? null,
|
|
320
321
|
id: String(e.id),
|
|
321
322
|
isSeeded: J.has(String(e.object_key))
|
|
322
323
|
})) : void 0,
|
|
323
|
-
seededCount:
|
|
324
|
+
seededCount: T
|
|
324
325
|
},
|
|
325
326
|
navigation: F,
|
|
326
|
-
nextSteps:
|
|
327
|
+
nextSteps: S,
|
|
327
328
|
pages: B,
|
|
328
|
-
posts:
|
|
329
|
-
products:
|
|
329
|
+
posts: P,
|
|
330
|
+
products: v.map((e) => ({
|
|
330
331
|
id: String(e.id),
|
|
331
332
|
languageCode: _.get(Number(e.language_id)) ?? String(e.language_id),
|
|
332
333
|
slug: String(e.slug),
|
|
333
334
|
status: String(e.status),
|
|
334
335
|
title: String(e.title ?? "")
|
|
335
336
|
})),
|
|
336
|
-
seeded: { ...
|
|
337
|
+
seeded: { ...m, anyPresent: W },
|
|
337
338
|
success: !0,
|
|
338
|
-
themes: (
|
|
339
|
+
themes: (f.data ?? []).map((e) => ({
|
|
339
340
|
colorScheme: String(e.color_scheme ?? "light"),
|
|
340
341
|
isActive: e.is_active !== !1,
|
|
341
342
|
isDefault: e.is_default === !0,
|
|
@@ -344,57 +345,97 @@ async function Ae(t, i) {
|
|
|
344
345
|
}))
|
|
345
346
|
};
|
|
346
347
|
}
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
348
|
+
const Be = 30;
|
|
349
|
+
function Ye(t) {
|
|
350
|
+
const { counts: i, seeded: a } = t, s = [];
|
|
351
|
+
s.push(
|
|
352
|
+
`Counts: ${i.pages} pages, ${i.posts} posts, ${i.products} products, ${i.media} media, ${i.customBlocks} custom blocks, ${i.drafts} drafts.`
|
|
353
|
+
);
|
|
354
|
+
const n = t.languages.filter((c) => c.isActive), o = (n.length > 0 ? n : t.languages).map((c) => `${c.code}${c.isDefault ? " (default)" : ""}`).join(", ");
|
|
355
|
+
if (s.push(`Languages: ${o || "none"}.`), a.anyPresent) {
|
|
356
|
+
const c = [
|
|
357
|
+
a.pages > 0 ? `${a.pages} demo pages` : null,
|
|
358
|
+
a.posts > 0 ? `${a.posts} demo posts` : null,
|
|
359
|
+
a.media > 0 ? `${a.media} bundled images` : null,
|
|
360
|
+
a.logo ? "the NextBlock logo" : null,
|
|
361
|
+
a.siteTitle ? "the default site title" : null,
|
|
362
|
+
a.copyright ? "the default copyright" : null
|
|
363
|
+
].filter((g) => !!g);
|
|
364
|
+
s.push(`NextBlock demo content: present (${c.join(", ")}).`);
|
|
365
|
+
} else
|
|
366
|
+
s.push("NextBlock demo content: none detected.");
|
|
367
|
+
const r = t.homePage;
|
|
368
|
+
s.push(
|
|
369
|
+
r ? `Home page: "${r.title}" (/${r.slug}) ${r.status}, ${r.blockCount} blocks${r.isSeeded ? ", seeded demo content" : ""}.` : 'Home page: missing in the default language ("/" returns 404).'
|
|
370
|
+
);
|
|
371
|
+
const d = t.identity, f = d.activeLogo ? `logo "${d.activeLogo.name || d.activeLogo.id}"${d.activeLogo.isSeeded ? " (NextBlock demo logo)" : ""}` : "no logo";
|
|
372
|
+
if (s.push(`Site title: ${d.siteTitle ? `"${d.siteTitle}"` : "not set"}; ${f}.`), t.themes.length > 0 && s.push(
|
|
373
|
+
`Themes: ${t.themes.map(
|
|
374
|
+
(c) => `${c.name || c.slug} (${c.colorScheme}${c.isDefault ? ", default" : ""}${c.isActive ? "" : ", inactive"})`
|
|
375
|
+
).join("; ")}.`
|
|
376
|
+
), s.push(t.brief ? `Site brief: saved (status ${t.brief.status}).` : "Site brief: none saved."), s.push(
|
|
377
|
+
t.buildSession ? `Build session: active until ${t.buildSession.expiresAt}.` : "Build session: none."
|
|
378
|
+
), t.pages.length > 0) {
|
|
379
|
+
const c = t.pages.slice(0, Be), g = Math.max(i.pages, t.pages.length) - c.length;
|
|
380
|
+
s.push(
|
|
381
|
+
`Pages: ${c.map((p) => `${p.title || p.slug} (/${p.slug}) [${p.languageCode}]`).join(", ")}${g > 0 ? `, +${g} more` : ""}.`
|
|
382
|
+
);
|
|
383
|
+
} else
|
|
384
|
+
s.push("Pages: none.");
|
|
385
|
+
return s.join(`
|
|
386
|
+
`);
|
|
387
|
+
}
|
|
388
|
+
const fe = l.strictObject({
|
|
389
|
+
active_logo_id: l.string().uuid().nullable().optional().describe("Pin a logos.id as the active logo, or null to fall back to the newest logo."),
|
|
390
|
+
footer_copyright: l.record(l.string().trim().min(2).max(12), l.string().trim().min(1).max(500)).optional().describe('Copyright line per locale code; "{year}" is replaced with the current year. Example: { "en": "© {year} Acme Bakery", "fr": "© {year} Boulangerie Acme" }.'),
|
|
391
|
+
footer_show_attribution: l.boolean().optional().describe('Whether the "Published with NextBlock" credit shows in the footer. Set false when the client wants no NextBlock mention.'),
|
|
392
|
+
site_description: l.string().trim().max(500).optional().describe("Default meta description and header tagline."),
|
|
393
|
+
site_keywords: l.string().trim().max(500).optional().describe("Comma-separated SEO keywords."),
|
|
394
|
+
site_title: l.string().trim().min(1).max(160).optional().describe("The brand name shown in the header and <title>.")
|
|
354
395
|
}).refine((t) => Object.values(t).some((i) => i !== void 0), {
|
|
355
396
|
message: "Provide at least one identity field to update."
|
|
356
397
|
});
|
|
357
|
-
async function
|
|
358
|
-
const
|
|
359
|
-
await
|
|
360
|
-
const
|
|
398
|
+
async function Pe(t, i) {
|
|
399
|
+
const a = fe.parse(t);
|
|
400
|
+
await se(i, ["ADMIN", "WRITER"], "Updating the site identity");
|
|
401
|
+
const s = O(i), n = Object.entries(a).filter(([, r]) => r !== void 0), o = ae({
|
|
361
402
|
action: "UPDATE SITE IDENTITY",
|
|
362
403
|
context: i,
|
|
363
|
-
payload: { input:
|
|
404
|
+
payload: { input: a, tool: "update_site_identity" },
|
|
364
405
|
preview: {
|
|
365
|
-
changes: Object.fromEntries(
|
|
366
|
-
summary: `Update ${
|
|
406
|
+
changes: Object.fromEntries(n),
|
|
407
|
+
summary: `Update ${n.map(([r]) => r).join(", ")} for the whole site.`
|
|
367
408
|
},
|
|
368
|
-
subject:
|
|
409
|
+
subject: n.map(([r]) => r).join(" ")
|
|
369
410
|
});
|
|
370
|
-
if (
|
|
371
|
-
return
|
|
372
|
-
for (const [r,
|
|
373
|
-
await
|
|
374
|
-
return
|
|
411
|
+
if (o)
|
|
412
|
+
return o;
|
|
413
|
+
for (const [r, d] of n)
|
|
414
|
+
await k(s, r, d);
|
|
415
|
+
return oe(i, ["/cms/settings/logos", "/cms/settings/copyright"]), {
|
|
375
416
|
mutationExecuted: !0,
|
|
376
417
|
success: !0,
|
|
377
|
-
updatedKeys:
|
|
418
|
+
updatedKeys: n.map(([r]) => r)
|
|
378
419
|
};
|
|
379
420
|
}
|
|
380
|
-
const Re =
|
|
381
|
-
customBlocks:
|
|
382
|
-
identity:
|
|
383
|
-
navigation:
|
|
384
|
-
pages:
|
|
385
|
-
posts:
|
|
386
|
-
products:
|
|
387
|
-
seededMedia:
|
|
388
|
-
}).partial(), Z =
|
|
389
|
-
clearKeptPageBlocks:
|
|
390
|
-
dryRun:
|
|
391
|
-
keepLanguages:
|
|
392
|
-
keepPageSlugs:
|
|
393
|
-
onlySeeded:
|
|
421
|
+
const Re = l.strictObject({
|
|
422
|
+
customBlocks: l.boolean().default(!1).describe("Delete every custom block definition."),
|
|
423
|
+
identity: l.boolean().default(!0).describe("Clear the NextBlock site title, description, keywords, copyright line, and the seeded logo."),
|
|
424
|
+
navigation: l.boolean().default(!0).describe("Delete every header and footer navigation item."),
|
|
425
|
+
pages: l.boolean().default(!0).describe("Delete pages (all languages) except keepPageSlugs."),
|
|
426
|
+
posts: l.boolean().default(!0).describe("Delete every blog post."),
|
|
427
|
+
products: l.boolean().default(!1).describe("Delete every product, with its variants, images, and drafts."),
|
|
428
|
+
seededMedia: l.boolean().default(!0).describe("Delete the bundled NextBlock demo images from the media library.")
|
|
429
|
+
}).partial(), Z = l.strictObject({
|
|
430
|
+
clearKeptPageBlocks: l.boolean().default(!0).describe('Empty the blocks of the kept pages (usually "home") so they can be rebuilt. Set false to leave their content untouched.'),
|
|
431
|
+
dryRun: l.boolean().default(!1).describe("Report what would be removed without changing anything. No confirmation needed."),
|
|
432
|
+
keepLanguages: l.array(l.string().trim().min(2).max(10)).max(10).optional().describe("Locale codes the site should keep. Other languages are deactivated (never deleted) and their pages and posts removed. Omit to keep every language as it is."),
|
|
433
|
+
keepPageSlugs: l.array(l.string().trim().min(1).max(300)).max(50).default([N]).describe('Pages to keep, by slug in any language; every translation of a kept page is kept. Defaults to ["home"] because "/" must always resolve.'),
|
|
434
|
+
onlySeeded: l.boolean().default(!1).describe("Remove only rows that match the NextBlock demo seed (fixed translation groups, bundled images). Use when the site already has real content next to the demo content."),
|
|
394
435
|
scope: Re.default({})
|
|
395
436
|
});
|
|
396
437
|
async function he(t, i) {
|
|
397
|
-
const
|
|
438
|
+
const a = {
|
|
398
439
|
customBlocks: t.scope.customBlocks ?? !1,
|
|
399
440
|
identity: t.scope.identity ?? !0,
|
|
400
441
|
navigation: t.scope.navigation ?? !0,
|
|
@@ -402,84 +443,84 @@ async function he(t, i) {
|
|
|
402
443
|
posts: t.scope.posts ?? !0,
|
|
403
444
|
products: t.scope.products ?? !1,
|
|
404
445
|
seededMedia: t.scope.seededMedia ?? !0
|
|
405
|
-
}, { data:
|
|
406
|
-
if (
|
|
407
|
-
throw new Error(`Could not read the languages: ${
|
|
408
|
-
const
|
|
409
|
-
if (
|
|
410
|
-
const e = new Set(r.values()),
|
|
411
|
-
if (
|
|
412
|
-
throw new Error(`Unknown language code(s) in keepLanguages: ${
|
|
446
|
+
}, { data: s, error: n } = await i.from("languages").select("id, code, name, is_default, is_active");
|
|
447
|
+
if (n)
|
|
448
|
+
throw new Error(`Could not read the languages: ${h(n)}`);
|
|
449
|
+
const o = Array.isArray(s) ? s : [], r = new Map(o.map((e) => [Number(e.id), String(e.code).toLowerCase()])), d = t.keepLanguages ? new Set(t.keepLanguages.map((e) => e.toLowerCase())) : null;
|
|
450
|
+
if (d) {
|
|
451
|
+
const e = new Set(r.values()), u = [...d].filter((b) => !e.has(b));
|
|
452
|
+
if (u.length > 0)
|
|
453
|
+
throw new Error(`Unknown language code(s) in keepLanguages: ${u.join(", ")}. Existing: ${[...e].join(", ")}.`);
|
|
413
454
|
}
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
),
|
|
417
|
-
const e =
|
|
455
|
+
const f = new Set(
|
|
456
|
+
o.filter((e) => !d || d.has(String(e.code).toLowerCase())).map((e) => Number(e.id))
|
|
457
|
+
), c = o.filter((e) => d && !d.has(String(e.code).toLowerCase()) && e.is_active !== !1).map((e) => ({ code: String(e.code), id: Number(e.id) })), g = o.find((e) => e.is_default === !0), p = d && g && !d.has(String(g.code).toLowerCase()) ? (() => {
|
|
458
|
+
const e = o.find((u) => d.has(String(u.code).toLowerCase()));
|
|
418
459
|
return e ? { code: String(e.code), id: Number(e.id) } : null;
|
|
419
|
-
})() : null, _ = (e) => te.has(String(e.translation_group_id)), { data: M, error:
|
|
420
|
-
if (
|
|
421
|
-
throw new Error(`Could not read the pages: ${
|
|
422
|
-
const
|
|
423
|
-
|
|
460
|
+
})() : null, _ = (e) => te.has(String(e.translation_group_id)), { data: M, error: v } = await i.from("pages").select("id, slug, language_id, translation_group_id, author_id");
|
|
461
|
+
if (v)
|
|
462
|
+
throw new Error(`Could not read the pages: ${h(v)}`);
|
|
463
|
+
const $ = Array.isArray(M) ? M : [], F = new Set(t.keepPageSlugs.map((e) => e.toLowerCase())), H = new Set(
|
|
464
|
+
$.filter((e) => F.has(String(e.slug).toLowerCase())).map((e) => String(e.translation_group_id))
|
|
424
465
|
), y = (e) => ({
|
|
425
466
|
id: Number(e.id),
|
|
426
467
|
languageCode: r.get(Number(e.language_id)) ?? String(e.language_id),
|
|
427
468
|
slug: String(e.slug)
|
|
428
|
-
}),
|
|
429
|
-
for (const e of
|
|
430
|
-
const
|
|
431
|
-
|
|
469
|
+
}), T = [], C = [];
|
|
470
|
+
for (const e of $) {
|
|
471
|
+
const u = H.has(String(e.translation_group_id)), b = f.has(Number(e.language_id)), z = !t.onlySeeded || _(e);
|
|
472
|
+
u && b ? C.push(y(e)) : a.pages && z || !b ? T.push(y(e)) : C.push(y(e));
|
|
432
473
|
}
|
|
433
474
|
const { data: j, error: K } = await i.from("posts").select("id, slug, language_id, translation_group_id, author_id");
|
|
434
475
|
if (K)
|
|
435
|
-
throw new Error(`Could not read the posts: ${
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
return
|
|
476
|
+
throw new Error(`Could not read the posts: ${h(K)}`);
|
|
477
|
+
const w = (Array.isArray(j) ? j : []).filter((e) => {
|
|
478
|
+
const u = f.has(Number(e.language_id)), b = !t.onlySeeded || _(e);
|
|
479
|
+
return a.posts && b || !u;
|
|
439
480
|
}).map(y);
|
|
440
|
-
let
|
|
441
|
-
if (
|
|
481
|
+
let A = [];
|
|
482
|
+
if (a.products)
|
|
442
483
|
try {
|
|
443
484
|
const { data: e } = await i.from("products").select("id");
|
|
444
|
-
|
|
485
|
+
A = (Array.isArray(e) ? e : []).map((u) => String(u.id));
|
|
445
486
|
} catch {
|
|
446
|
-
|
|
487
|
+
A = [];
|
|
447
488
|
}
|
|
448
|
-
const
|
|
489
|
+
const D = new Set(T.map((e) => e.id)), B = new Set(T.map((e) => `/${e.slug}`)), { data: P, error: U } = await i.from("navigation_items").select("id, url, page_id");
|
|
449
490
|
if (U)
|
|
450
|
-
throw new Error(`Could not read the navigation: ${
|
|
451
|
-
const
|
|
452
|
-
(e) =>
|
|
453
|
-
).map((e) => Number(e.id)),
|
|
454
|
-
const
|
|
455
|
-
return e.parent_type === "page" ?
|
|
491
|
+
throw new Error(`Could not read the navigation: ${h(U)}`);
|
|
492
|
+
const Y = (Array.isArray(P) ? P : []).filter(
|
|
493
|
+
(e) => a.navigation || e.page_id !== null && e.page_id !== void 0 && D.has(Number(e.page_id)) || B.has(String(e.url))
|
|
494
|
+
).map((e) => Number(e.id)), m = C.map((e) => e.id), W = new Set(w.map((e) => e.id)), { data: q } = await i.from("content_drafts").select("id, parent_type, parent_id"), X = (Array.isArray(q) ? q : []).filter((e) => {
|
|
495
|
+
const u = Number(e.parent_id);
|
|
496
|
+
return e.parent_type === "page" ? D.has(u) || t.clearKeptPageBlocks && m.includes(u) : e.parent_type === "post" && W.has(u);
|
|
456
497
|
}).map((e) => ({ parentId: Number(e.parent_id), parentType: String(e.parent_type) }));
|
|
457
498
|
let x = [];
|
|
458
|
-
if (
|
|
499
|
+
if (a.customBlocks) {
|
|
459
500
|
const { data: e } = await i.from("custom_block_definitions").select("slug");
|
|
460
|
-
x = (Array.isArray(e) ? e : []).map((
|
|
501
|
+
x = (Array.isArray(e) ? e : []).map((u) => String(u.slug));
|
|
461
502
|
}
|
|
462
|
-
let
|
|
463
|
-
if (
|
|
464
|
-
const { data: e } = await i.from("media").select("id, object_key"),
|
|
465
|
-
(
|
|
466
|
-
),
|
|
467
|
-
|
|
503
|
+
let R = [], S = [];
|
|
504
|
+
if (a.seededMedia || a.identity) {
|
|
505
|
+
const { data: e } = await i.from("media").select("id, object_key"), u = (Array.isArray(e) ? e : []).filter(
|
|
506
|
+
(E) => J.has(String(E.object_key))
|
|
507
|
+
), b = new Set(u.map((E) => String(E.id))), { data: z } = await i.from("logos").select("id, media_id");
|
|
508
|
+
S = (Array.isArray(z) ? z : []).filter((E) => b.has(String(E.media_id))).map((E) => String(E.id)), R = a.seededMedia ? [...b] : [], a.identity || (S = []);
|
|
468
509
|
}
|
|
469
510
|
return {
|
|
470
|
-
blocksToClearPageIds: t.clearKeptPageBlocks ?
|
|
511
|
+
blocksToClearPageIds: t.clearKeptPageBlocks ? m : [],
|
|
471
512
|
customBlockSlugs: x,
|
|
472
|
-
draftsToDelete:
|
|
473
|
-
identityKeys:
|
|
474
|
-
languagesToDeactivate:
|
|
475
|
-
logosToDelete:
|
|
476
|
-
mediaToDelete:
|
|
477
|
-
navigationToDelete:
|
|
478
|
-
newDefaultLanguage:
|
|
479
|
-
pagesToDelete:
|
|
480
|
-
pagesToKeep:
|
|
481
|
-
postsToDelete:
|
|
482
|
-
productsToDelete:
|
|
513
|
+
draftsToDelete: X,
|
|
514
|
+
identityKeys: a.identity ? ["site_title", "site_description", "site_keywords", "footer_copyright"] : [],
|
|
515
|
+
languagesToDeactivate: c,
|
|
516
|
+
logosToDelete: S,
|
|
517
|
+
mediaToDelete: R,
|
|
518
|
+
navigationToDelete: Y,
|
|
519
|
+
newDefaultLanguage: p,
|
|
520
|
+
pagesToDelete: T,
|
|
521
|
+
pagesToKeep: C,
|
|
522
|
+
postsToDelete: w,
|
|
523
|
+
productsToDelete: A
|
|
483
524
|
};
|
|
484
525
|
}
|
|
485
526
|
function be(t) {
|
|
@@ -498,194 +539,194 @@ function be(t) {
|
|
|
498
539
|
products: t.productsToDelete.length
|
|
499
540
|
};
|
|
500
541
|
}
|
|
501
|
-
async function
|
|
502
|
-
for (const
|
|
503
|
-
const { error:
|
|
504
|
-
if (
|
|
505
|
-
throw new Error(`Could not delete a draft: ${
|
|
542
|
+
async function Le(t, i, a) {
|
|
543
|
+
for (const s of t.draftsToDelete) {
|
|
544
|
+
const { error: n } = await i.from("content_drafts").delete().eq("parent_type", s.parentType).eq("parent_id", s.parentId);
|
|
545
|
+
if (n)
|
|
546
|
+
throw new Error(`Could not delete a draft: ${h(n)}`);
|
|
506
547
|
}
|
|
507
|
-
if (await
|
|
508
|
-
for (const
|
|
509
|
-
const { error:
|
|
510
|
-
if (
|
|
511
|
-
throw new Error(`Could not clear the kept pages: ${
|
|
548
|
+
if (await I(i, "navigation_items", "id", t.navigationToDelete), t.blocksToClearPageIds.length > 0)
|
|
549
|
+
for (const s of ce(t.blocksToClearPageIds)) {
|
|
550
|
+
const { error: n } = await i.from("blocks").delete().in("page_id", s);
|
|
551
|
+
if (n)
|
|
552
|
+
throw new Error(`Could not clear the kept pages: ${h(n)}`);
|
|
512
553
|
}
|
|
513
|
-
if (await
|
|
554
|
+
if (await I(i, "pages", "id", t.pagesToDelete.map((s) => s.id)), await I(i, "posts", "id", t.postsToDelete.map((s) => s.id)), await I(i, "products", "id", t.productsToDelete), await I(i, "custom_block_definitions", "slug", t.customBlockSlugs), await I(i, "logos", "id", t.logosToDelete), await I(i, "media", "id", t.mediaToDelete), t.identityKeys.length > 0 && (await k(i, "site_title", ""), await k(i, "site_description", ""), await k(i, "site_keywords", ""), await k(
|
|
514
555
|
i,
|
|
515
556
|
"footer_copyright",
|
|
516
|
-
Object.fromEntries(
|
|
557
|
+
Object.fromEntries(a.map((s) => [s, "© {year}"]))
|
|
517
558
|
)), t.logosToDelete.length > 0) {
|
|
518
|
-
const { data:
|
|
519
|
-
typeof
|
|
559
|
+
const { data: s } = await i.from("site_settings").select("value").eq("key", "active_logo_id").maybeSingle();
|
|
560
|
+
typeof s?.value == "string" && t.logosToDelete.includes(s.value) && await k(i, "active_logo_id", null);
|
|
520
561
|
}
|
|
521
|
-
for (const
|
|
522
|
-
const { error:
|
|
523
|
-
if (
|
|
524
|
-
throw new Error(`Could not deactivate the "${
|
|
562
|
+
for (const s of t.languagesToDeactivate) {
|
|
563
|
+
const { error: n } = await i.from("languages").update({ is_active: !1, is_default: !1 }).eq("id", s.id);
|
|
564
|
+
if (n)
|
|
565
|
+
throw new Error(`Could not deactivate the "${s.code}" language: ${h(n)}`);
|
|
525
566
|
}
|
|
526
567
|
if (t.newDefaultLanguage) {
|
|
527
|
-
const { error:
|
|
568
|
+
const { error: s } = await i.from("languages").update({ is_default: !1 }).neq("id", t.newDefaultLanguage.id);
|
|
569
|
+
if (s)
|
|
570
|
+
throw new Error(`Could not change the default language: ${h(s)}`);
|
|
571
|
+
const { error: n } = await i.from("languages").update({ is_active: !0, is_default: !0 }).eq("id", t.newDefaultLanguage.id);
|
|
528
572
|
if (n)
|
|
529
|
-
throw new Error(`Could not change the default language: ${
|
|
530
|
-
const { error: o } = await i.from("languages").update({ is_active: !0, is_default: !0 }).eq("id", t.newDefaultLanguage.id);
|
|
531
|
-
if (o)
|
|
532
|
-
throw new Error(`Could not change the default language: ${g(o)}`);
|
|
573
|
+
throw new Error(`Could not change the default language: ${h(n)}`);
|
|
533
574
|
}
|
|
534
575
|
}
|
|
535
576
|
function re(t) {
|
|
536
|
-
const i = [],
|
|
537
|
-
return
|
|
577
|
+
const i = [], a = t.pagesToKeep.some((s) => s.slug === N);
|
|
578
|
+
return a && t.blocksToClearPageIds.length > 0 ? i.push('The home page was kept but is now empty: rebuild it with rewrite_page_draft (contentType "page", slug "home") and then publish_content_draft.') : a || i.push(`No "${N}" page remains, so "/" is a 404 until you create_cms_page with slug "${N}" and status "published".`), t.navigationToDelete.length > 0 && i.push('The header and footer menus are empty: rebuild them with update_navigation_bar (mode "replace") and update_footer for every active language.'), t.identityKeys.length > 0 && i.push("Site title, description, keywords, and copyright are blank: set them with update_site_identity."), i.push('The "Published with NextBlock" footer credit is unchanged; set footer_show_attribution to false with update_site_identity if the client wants no NextBlock mention.'), i;
|
|
538
579
|
}
|
|
539
580
|
async function ye(t, i) {
|
|
540
|
-
const
|
|
541
|
-
await
|
|
542
|
-
const
|
|
543
|
-
if (
|
|
581
|
+
const a = Z.parse(t);
|
|
582
|
+
await se(i, ["ADMIN"], "Resetting the site content");
|
|
583
|
+
const s = O(i), n = await he(a, s), o = be(n), r = o.pages + o.posts + o.products + o.navigationItems + o.customBlocks + o.media + o.logos;
|
|
584
|
+
if (a.dryRun)
|
|
544
585
|
return {
|
|
545
586
|
dryRun: !0,
|
|
546
587
|
mutationExecuted: !1,
|
|
547
|
-
nextSteps: re(
|
|
588
|
+
nextSteps: re(n),
|
|
548
589
|
success: !0,
|
|
549
|
-
summary:
|
|
590
|
+
summary: o,
|
|
550
591
|
wouldDelete: {
|
|
551
|
-
pages:
|
|
552
|
-
posts:
|
|
592
|
+
pages: n.pagesToDelete,
|
|
593
|
+
posts: n.postsToDelete
|
|
553
594
|
}
|
|
554
595
|
};
|
|
555
|
-
const
|
|
596
|
+
const d = ae({
|
|
556
597
|
action: "RESET SITE CONTENT",
|
|
557
598
|
context: i,
|
|
558
|
-
payload: { input:
|
|
599
|
+
payload: { input: a, summary: o, tool: "reset_site_content" },
|
|
559
600
|
preview: {
|
|
560
601
|
irreversible: !0,
|
|
561
|
-
summary: `Permanently delete ${
|
|
562
|
-
...
|
|
602
|
+
summary: `Permanently delete ${o.pages} page(s), ${o.posts} post(s), ${o.products} product(s), ${o.navigationItems} navigation item(s), ${o.customBlocks} custom block definition(s), ${o.media} demo image(s) and ${o.logos} logo(s)${o.identityCleared ? ", clear the site identity" : ""}${o.languagesDeactivated.length ? `, deactivate ${o.languagesDeactivated.join(", ")}` : ""}. Kept: ${o.pagesKept.join(", ") || "nothing"}. This cannot be undone — take a backup first at /cms/settings/backup-restore if the content matters.`,
|
|
603
|
+
...o
|
|
563
604
|
},
|
|
564
605
|
subject: `${r} items`
|
|
565
606
|
});
|
|
566
|
-
if (
|
|
567
|
-
return
|
|
568
|
-
const
|
|
569
|
-
const { data:
|
|
570
|
-
return (Array.isArray(
|
|
607
|
+
if (d)
|
|
608
|
+
return d;
|
|
609
|
+
const f = a.keepLanguages?.map((g) => g.toLowerCase()) ?? [], c = f.length > 0 ? f : await (async () => {
|
|
610
|
+
const { data: g } = await s.from("languages").select("code, is_active");
|
|
611
|
+
return (Array.isArray(g) ? g : []).filter((p) => p.is_active !== !1).map((p) => String(p.code).toLowerCase());
|
|
571
612
|
})();
|
|
572
|
-
return await
|
|
573
|
-
deleted:
|
|
613
|
+
return await Le(n, s, c), oe(i, ["/cms/pages", "/cms/posts", "/cms/navigation", "/cms/media", "/cms/custom-blocks", "/cms/products"]), {
|
|
614
|
+
deleted: o,
|
|
574
615
|
mutationExecuted: !0,
|
|
575
|
-
nextSteps: re(
|
|
616
|
+
nextSteps: re(n),
|
|
576
617
|
success: !0
|
|
577
618
|
};
|
|
578
619
|
}
|
|
579
|
-
const
|
|
580
|
-
brief:
|
|
581
|
-
durationMinutes:
|
|
620
|
+
const Oe = 60, _e = l.strictObject({
|
|
621
|
+
brief: le.optional().describe("The brief as agreed with the client. Merged over the saved brief and marked confirmed."),
|
|
622
|
+
durationMinutes: l.number().int().min(15).max(180).default(Oe).describe("How long the unattended build session stays open."),
|
|
582
623
|
reset: Z.optional().describe("Remove the seeded NextBlock demo content as the first step of the build. Omit when the client keeps existing content."),
|
|
583
|
-
summary:
|
|
624
|
+
summary: l.string().trim().min(20).max(4e3).describe("The plan the operator is approving, in plain language: what is removed, every page and its sections, navigation, footer, theme, languages.")
|
|
584
625
|
});
|
|
585
|
-
async function
|
|
586
|
-
const
|
|
587
|
-
await
|
|
588
|
-
const
|
|
589
|
-
let
|
|
590
|
-
|
|
591
|
-
const
|
|
626
|
+
async function Me(t, i) {
|
|
627
|
+
const a = _e.parse(t);
|
|
628
|
+
await se(i, ["ADMIN"], "Starting a site build");
|
|
629
|
+
const s = O(i), n = i?.actorUserId;
|
|
630
|
+
let o = null, r = null;
|
|
631
|
+
a.reset && (r = Z.parse({ ...a.reset, dryRun: !1 }), o = be(await he(r, s)));
|
|
632
|
+
const d = ae({
|
|
592
633
|
action: "START SITE BUILD",
|
|
593
634
|
context: i,
|
|
594
|
-
payload: { brief:
|
|
635
|
+
payload: { brief: a.brief ?? null, durationMinutes: a.durationMinutes, reset: r, summary: a.summary, tool: "start_site_build" },
|
|
595
636
|
preview: {
|
|
596
|
-
durationMinutes:
|
|
597
|
-
plan:
|
|
598
|
-
reset:
|
|
599
|
-
summary: `${
|
|
637
|
+
durationMinutes: a.durationMinutes,
|
|
638
|
+
plan: a.summary,
|
|
639
|
+
reset: o,
|
|
640
|
+
summary: `${a.summary}${o ? ` First, permanently delete ${o.pages} page(s), ${o.posts} post(s), ${o.navigationItems} navigation item(s), ${o.media} demo image(s)${o.identityCleared ? " and the NextBlock site identity" : ""} (kept: ${o.pagesKept.join(", ") || "nothing"}).` : ""} After you confirm, Cortex applies every step of this plan without asking again, for up to ${a.durationMinutes} minutes. You can stop it at any time.`
|
|
600
641
|
},
|
|
601
642
|
subject: "plan"
|
|
602
643
|
});
|
|
603
|
-
if (
|
|
604
|
-
return
|
|
605
|
-
let
|
|
606
|
-
|
|
607
|
-
{ brief: { ...
|
|
644
|
+
if (d)
|
|
645
|
+
return d;
|
|
646
|
+
let f = null;
|
|
647
|
+
a.brief ? f = (await Q(
|
|
648
|
+
{ brief: { ...a.brief, status: "confirmed" }, mode: "merge" },
|
|
608
649
|
{ ...i }
|
|
609
|
-
)).brief : await
|
|
610
|
-
let
|
|
611
|
-
r && (
|
|
612
|
-
const
|
|
613
|
-
actorUserId:
|
|
614
|
-
createdAt:
|
|
615
|
-
expiresAt: new Date(
|
|
650
|
+
)).brief : await ne(s) && (f = (await Q({ brief: { status: "confirmed" }, mode: "merge" }, { ...i })).brief);
|
|
651
|
+
let c = null;
|
|
652
|
+
r && (c = await ye(r, { ...i, skipConfirmation: !0 }));
|
|
653
|
+
const g = /* @__PURE__ */ new Date(), p = {
|
|
654
|
+
actorUserId: n,
|
|
655
|
+
createdAt: g.toISOString(),
|
|
656
|
+
expiresAt: new Date(g.getTime() + a.durationMinutes * 6e4).toISOString(),
|
|
616
657
|
id: xe(),
|
|
617
|
-
summary:
|
|
658
|
+
summary: a.summary
|
|
618
659
|
};
|
|
619
|
-
return await
|
|
620
|
-
brief:
|
|
621
|
-
buildSession: { expiresAt:
|
|
660
|
+
return await k(s, G, p), {
|
|
661
|
+
brief: f,
|
|
662
|
+
buildSession: { expiresAt: p.expiresAt, id: p.id },
|
|
622
663
|
continuePrompt: "The build session is active and the plan is approved. Continue the site build now, step by step, without asking for confirmation: identity, languages, theme, pages (rewrite home, create the others), publish every draft, navigation, footer, translations, then call finish_site_build and summarize what was built with links.",
|
|
623
664
|
mutationExecuted: !0,
|
|
624
|
-
resetResult:
|
|
665
|
+
resetResult: c,
|
|
625
666
|
success: !0
|
|
626
667
|
};
|
|
627
668
|
}
|
|
628
|
-
const
|
|
629
|
-
outcome:
|
|
630
|
-
summary:
|
|
669
|
+
const Se = l.strictObject({
|
|
670
|
+
outcome: l.enum(["built", "stopped"]).default("built").describe('"built" when the plan was completed; "stopped" when the operator interrupted it.'),
|
|
671
|
+
summary: l.string().trim().max(4e3).optional().describe("What was built, for the record.")
|
|
631
672
|
});
|
|
632
|
-
async function
|
|
633
|
-
const
|
|
634
|
-
await
|
|
635
|
-
let
|
|
636
|
-
if (
|
|
637
|
-
const r = await
|
|
638
|
-
r && (
|
|
639
|
-
{ brief: { status: "built", ...
|
|
640
|
-
` : ""}Built: ${
|
|
673
|
+
async function je(t, i) {
|
|
674
|
+
const a = Se.parse(t), s = O(i), n = await pe(s);
|
|
675
|
+
await Ne(s);
|
|
676
|
+
let o = null;
|
|
677
|
+
if (a.outcome === "built") {
|
|
678
|
+
const r = await ne(s);
|
|
679
|
+
r && (o = (await Q(
|
|
680
|
+
{ brief: { status: "built", ...a.summary ? { notes: `${r.notes ? `${r.notes}
|
|
681
|
+
` : ""}Built: ${a.summary}` } : {} }, mode: "merge" },
|
|
641
682
|
{ ...i }
|
|
642
683
|
)).brief);
|
|
643
684
|
}
|
|
644
|
-
return
|
|
645
|
-
brief:
|
|
685
|
+
return oe(i, ["/cms/dashboard"]), {
|
|
686
|
+
brief: o,
|
|
646
687
|
buildSessionEnded: !0,
|
|
647
|
-
hadSession: !!
|
|
688
|
+
hadSession: !!n,
|
|
648
689
|
mutationExecuted: !0,
|
|
649
|
-
outcome:
|
|
690
|
+
outcome: a.outcome,
|
|
650
691
|
success: !0
|
|
651
692
|
};
|
|
652
693
|
}
|
|
653
|
-
function
|
|
694
|
+
function We(t) {
|
|
654
695
|
return {
|
|
655
|
-
get_site_overview:
|
|
696
|
+
get_site_overview: L({
|
|
656
697
|
description: 'Read the WHOLE site in one call: languages, site identity (title, description, copyright, active logo), every page, post, and product with status and language, the header and footer menus per language, themes, custom block definitions, media counts, pending Live Drafts, the saved site brief, and whether NextBlock demo content is still present. Read-only. Call this FIRST for any request about the site as a whole ("build my site", "update the whole website", "what do I have?") instead of several narrower reads.',
|
|
657
|
-
execute: (i) =>
|
|
698
|
+
execute: (i) => De(i, t),
|
|
658
699
|
inputSchema: me,
|
|
659
700
|
strict: !0
|
|
660
701
|
}),
|
|
661
|
-
update_site_identity:
|
|
702
|
+
update_site_identity: L({
|
|
662
703
|
description: 'Set the site-wide identity: site_title (brand name in the header and <title>), site_description, site_keywords, footer_copyright per locale ("{year}" is substituted), footer_show_attribution (the "Published with NextBlock" credit), and the pinned active_logo_id. Use it right after a reset and whenever the client renames or re-describes their business. Mutating: first returns a confirmation phrase; only executes after exact confirmation.',
|
|
663
|
-
execute: (i) =>
|
|
704
|
+
execute: (i) => Pe(i, t),
|
|
664
705
|
inputSchema: fe,
|
|
665
706
|
strict: !0
|
|
666
707
|
}),
|
|
667
|
-
save_site_brief:
|
|
708
|
+
save_site_brief: L({
|
|
668
709
|
description: 'Record what the client wants their website to be — business, audience, goals, site type (a one-page landing site is fine), pages and their sections, languages, brand colours and tone, contact details, whether existing content is kept. Saved site-wide and shown to you at the start of every later conversation, so save it as soon as you learn something (mode "merge" adds to what is saved). Runs immediately, no confirmation.',
|
|
669
|
-
execute: (i) =>
|
|
710
|
+
execute: (i) => Q(i, t),
|
|
670
711
|
inputSchema: ge,
|
|
671
712
|
strict: !0
|
|
672
713
|
}),
|
|
673
|
-
reset_site_content:
|
|
714
|
+
reset_site_content: L({
|
|
674
715
|
description: 'Remove the NextBlock demo content that ships with a fresh install — or, by scope, everything: pages (all languages, except keepPageSlugs, default ["home"] which is kept but emptied), posts, navigation, the bundled demo images and logo, the default site title and copyright; optionally products and custom block definitions. keepLanguages deactivates every other language and removes its content, so a client who only wants English gets an English-only site. Pass dryRun true to see the counts first. ADMIN only, irreversible; take a backup at /cms/settings/backup-restore if in doubt. Mutating: first returns a confirmation phrase; only executes after exact confirmation. Prefer start_site_build with `reset` when you are about to rebuild the site, so one confirmation covers both.',
|
|
675
716
|
execute: (i) => ye(i, t),
|
|
676
717
|
inputSchema: Z,
|
|
677
718
|
strict: !0
|
|
678
719
|
}),
|
|
679
|
-
start_site_build:
|
|
720
|
+
start_site_build: L({
|
|
680
721
|
description: "Approve a whole-site plan with ONE confirmation. Pass the plain-language plan in `summary`, the agreed `brief`, and (usually) `reset` so the demo content is removed first. On confirmation it saves the brief, runs the reset, and opens a time-boxed build session: until it expires, every later tool call in this chat executes without asking for confirmation, so you can create pages, publish drafts, set navigation, footer, theme, and translations in one unattended run. Call it only after the client has seen the plan and agreed. After it succeeds, keep working through the plan immediately and finish with finish_site_build. ADMIN only.",
|
|
681
|
-
execute: (i) =>
|
|
722
|
+
execute: (i) => Me(i, t),
|
|
682
723
|
inputSchema: _e,
|
|
683
724
|
strict: !0
|
|
684
725
|
}),
|
|
685
|
-
finish_site_build:
|
|
726
|
+
finish_site_build: L({
|
|
686
727
|
description: 'Close the current build session (opened by start_site_build) and mark the brief as built. Call it as the LAST step of a site build, or with outcome "stopped" if the operator interrupted. Runs immediately, no confirmation.',
|
|
687
|
-
execute: (i) =>
|
|
688
|
-
inputSchema:
|
|
728
|
+
execute: (i) => je(i, t),
|
|
729
|
+
inputSchema: Se,
|
|
689
730
|
strict: !0
|
|
690
731
|
})
|
|
691
732
|
};
|
|
@@ -693,19 +734,20 @@ function Ye(t) {
|
|
|
693
734
|
export {
|
|
694
735
|
J as NEXTBLOCK_SEED_MEDIA_OBJECT_KEYS,
|
|
695
736
|
te as NEXTBLOCK_SEED_TRANSLATION_GROUP_IDS,
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
737
|
+
We as createCortexSiteTools,
|
|
738
|
+
je as executeFinishSiteBuild,
|
|
739
|
+
De as executeGetSiteOverview,
|
|
699
740
|
ye as executeResetSiteContent,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
741
|
+
Q as executeSaveSiteBrief,
|
|
742
|
+
Me as executeStartSiteBuild,
|
|
743
|
+
Pe as executeUpdateSiteIdentity,
|
|
744
|
+
Se as finishSiteBuildInputSchema,
|
|
745
|
+
Ye as formatCortexSiteOverviewForPrompt,
|
|
704
746
|
me as getSiteOverviewInputSchema,
|
|
705
747
|
pe as readCortexBuildSession,
|
|
706
|
-
|
|
748
|
+
ne as readCortexSiteBrief,
|
|
707
749
|
Z as resetSiteContentInputSchema,
|
|
708
|
-
|
|
750
|
+
He as resolveCortexBuildSession,
|
|
709
751
|
ge as saveSiteBriefInputSchema,
|
|
710
752
|
_e as startSiteBuildInputSchema,
|
|
711
753
|
fe as updateSiteIdentityInputSchema
|