@nextblock-cms/cortex 0.17.6 → 0.17.8
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.d.ts +3 -0
- package/index.es.js +237 -199
- package/lib/ai-global-agent-db-tools.cjs.js +1 -1
- package/lib/ai-global-agent-db-tools.d.ts +7 -0
- package/lib/ai-global-agent-db-tools.es.js +309 -192
- package/lib/ai-global-agent-site-tools.cjs.js +2 -0
- package/lib/ai-global-agent-site-tools.d.ts +1153 -0
- package/lib/ai-global-agent-site-tools.es.js +712 -0
- package/lib/ai-global-agent-theming-tools.cjs.js +2 -2
- package/lib/ai-global-agent-theming-tools.d.ts +18 -0
- package/lib/ai-global-agent-theming-tools.es.js +1 -0
- package/lib/ai-global-agent-tools.cjs.js +2 -2
- package/lib/ai-global-agent-tools.es.js +1826 -1925
- package/lib/block-content-schemas.cjs.js +1 -0
- package/lib/block-content-schemas.d.ts +216 -0
- package/lib/block-content-schemas.es.js +246 -0
- package/lib/mcp-tool-registry.cjs.js +4 -2
- package/lib/mcp-tool-registry.d.ts +15 -0
- package/lib/mcp-tool-registry.es.js +103 -70
- package/lib/site-brief.cjs.js +2 -0
- package/lib/site-brief.d.ts +159 -0
- package/lib/site-brief.es.js +109 -0
- package/package.json +4 -4
- package/lib/ai-global-agent-tools.d.ts +0 -4374
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createCortexGlobalAgentTools as b } from "./ai-global-agent-tools.es.js";
|
|
2
|
+
import { availableCortexAiBlockTypes as y } from "./block-content-schemas.es.js";
|
|
2
3
|
import "./zod-config.es.js";
|
|
3
|
-
import { z as
|
|
4
|
-
const
|
|
4
|
+
import { z as w } from "zod";
|
|
5
|
+
const o = {
|
|
5
6
|
create_cms_page: "write",
|
|
6
7
|
create_cms_post: "write",
|
|
7
8
|
create_cms_product: "write",
|
|
@@ -14,6 +15,8 @@ const i = {
|
|
|
14
15
|
execute_database_mutation: "write",
|
|
15
16
|
fetch_ecommerce_stats: "read",
|
|
16
17
|
fetch_url_content: "read",
|
|
18
|
+
finish_site_build: "write",
|
|
19
|
+
get_site_overview: "read",
|
|
17
20
|
insert_content_block: "write",
|
|
18
21
|
list_custom_blocks: "read",
|
|
19
22
|
list_media: "read",
|
|
@@ -30,11 +33,14 @@ const i = {
|
|
|
30
33
|
publish_content_draft: "write",
|
|
31
34
|
read_current_cms_item: "read",
|
|
32
35
|
read_database_records: "read",
|
|
36
|
+
reset_site_content: "write",
|
|
33
37
|
revert_site_script: "write",
|
|
34
38
|
rewrite_page_draft: "write",
|
|
39
|
+
save_site_brief: "write",
|
|
35
40
|
search_documentation: "read",
|
|
36
41
|
search_stock_photos: "read",
|
|
37
42
|
set_content_images: "write",
|
|
43
|
+
start_site_build: "write",
|
|
38
44
|
translate_content_bulk: "write",
|
|
39
45
|
translate_page: "write",
|
|
40
46
|
update_cms_item_field: "write",
|
|
@@ -45,7 +51,8 @@ const i = {
|
|
|
45
51
|
update_footer: "write",
|
|
46
52
|
update_global_css: "write",
|
|
47
53
|
update_navigation_bar: "write",
|
|
48
|
-
update_section_column_block: "write"
|
|
54
|
+
update_section_column_block: "write",
|
|
55
|
+
update_site_identity: "write"
|
|
49
56
|
}, u = {
|
|
50
57
|
generate_jsonb_layout: {
|
|
51
58
|
canonical: "rewrite_page_draft",
|
|
@@ -73,74 +80,74 @@ const i = {
|
|
|
73
80
|
title: "Update site navigation"
|
|
74
81
|
}
|
|
75
82
|
};
|
|
76
|
-
function
|
|
77
|
-
return e in u ? u[e].canonical : e in
|
|
83
|
+
function _(e) {
|
|
84
|
+
return e in u ? u[e].canonical : e in o ? e : null;
|
|
78
85
|
}
|
|
79
|
-
function
|
|
80
|
-
const t =
|
|
81
|
-
return t ?
|
|
86
|
+
function k(e) {
|
|
87
|
+
const t = _(e);
|
|
88
|
+
return t ? o[t] : null;
|
|
82
89
|
}
|
|
83
90
|
function d(e, t) {
|
|
84
|
-
const
|
|
85
|
-
return
|
|
91
|
+
const a = k(t);
|
|
92
|
+
return a ? a === "read" ? e.length > 0 : e.includes("write") : !1;
|
|
86
93
|
}
|
|
87
94
|
function p(e) {
|
|
88
|
-
return e.split("_").map((
|
|
95
|
+
return e.split("_").map((a, r) => r === 0 ? a.charAt(0).toUpperCase() + a.slice(1) : a).join(" ");
|
|
89
96
|
}
|
|
90
|
-
function
|
|
97
|
+
function h(e) {
|
|
91
98
|
const t = { additionalProperties: !1, type: "object" };
|
|
92
99
|
if (!e || typeof e != "object")
|
|
93
100
|
return t;
|
|
94
101
|
try {
|
|
95
|
-
const
|
|
102
|
+
const a = w.toJSONSchema(e, {
|
|
96
103
|
cycles: "ref",
|
|
97
104
|
io: "input",
|
|
98
105
|
reused: "inline",
|
|
99
106
|
target: "draft-2020-12",
|
|
100
107
|
unrepresentable: "any"
|
|
101
108
|
});
|
|
102
|
-
return delete
|
|
109
|
+
return delete a.$schema, a.type === "object" ? a : t;
|
|
103
110
|
} catch {
|
|
104
111
|
return t;
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
function s(e) {
|
|
108
|
-
return
|
|
115
|
+
return b(e);
|
|
109
116
|
}
|
|
110
|
-
function
|
|
111
|
-
const t = Object.keys(s(e)),
|
|
117
|
+
function M(e) {
|
|
118
|
+
const t = Object.keys(s(e)), a = Object.keys(o);
|
|
112
119
|
return {
|
|
113
|
-
missingFromRegistry:
|
|
114
|
-
unclassified: t.filter((
|
|
120
|
+
missingFromRegistry: a.filter((r) => !t.includes(r)),
|
|
121
|
+
unclassified: t.filter((r) => !a.includes(r))
|
|
115
122
|
};
|
|
116
123
|
}
|
|
117
|
-
function
|
|
118
|
-
const t = s(e.context),
|
|
119
|
-
for (const [
|
|
120
|
-
!(
|
|
121
|
-
description: n.description ?? `Cortex AI ${p(
|
|
122
|
-
inputSchema:
|
|
123
|
-
name:
|
|
124
|
-
title: p(
|
|
124
|
+
function N(e) {
|
|
125
|
+
const t = s(e.context), a = [];
|
|
126
|
+
for (const [r, n] of Object.entries(t))
|
|
127
|
+
!(r in o) || !d(e.scopes, r) || a.push({
|
|
128
|
+
description: n.description ?? `Cortex AI ${p(r)} tool.`,
|
|
129
|
+
inputSchema: h(n.inputSchema),
|
|
130
|
+
name: r,
|
|
131
|
+
title: p(r)
|
|
125
132
|
});
|
|
126
|
-
for (const [
|
|
127
|
-
const
|
|
128
|
-
!
|
|
133
|
+
for (const [r, n] of Object.entries(u)) {
|
|
134
|
+
const i = t[n.canonical];
|
|
135
|
+
!i || !d(e.scopes, r) || a.push({
|
|
129
136
|
description: n.description,
|
|
130
|
-
inputSchema:
|
|
131
|
-
name:
|
|
137
|
+
inputSchema: h(i.inputSchema),
|
|
138
|
+
name: r,
|
|
132
139
|
title: n.title
|
|
133
140
|
});
|
|
134
141
|
}
|
|
135
|
-
return
|
|
142
|
+
return a.sort((r, n) => r.name.localeCompare(n.name));
|
|
136
143
|
}
|
|
137
|
-
function
|
|
144
|
+
function g(e, t) {
|
|
138
145
|
return { content: [{ text: e, type: "text" }], isError: t };
|
|
139
146
|
}
|
|
140
147
|
function x(e) {
|
|
141
148
|
return e.issues.map((t) => `${t.path.length > 0 ? t.path.join(".") : "(root)"}: ${t.message}`).join("; ");
|
|
142
149
|
}
|
|
143
|
-
class
|
|
150
|
+
class m extends Error {
|
|
144
151
|
constructor(t) {
|
|
145
152
|
super(`Unknown tool: ${t}`), this.name = "CortexMcpUnknownToolError";
|
|
146
153
|
}
|
|
@@ -152,34 +159,34 @@ class v extends Error {
|
|
|
152
159
|
), this.name = "CortexMcpForbiddenToolError";
|
|
153
160
|
}
|
|
154
161
|
}
|
|
155
|
-
async function
|
|
156
|
-
const t =
|
|
162
|
+
async function O(e) {
|
|
163
|
+
const t = _(e.name);
|
|
157
164
|
if (!t)
|
|
158
|
-
throw new
|
|
165
|
+
throw new m(e.name);
|
|
159
166
|
if (!d(e.scopes, e.name))
|
|
160
167
|
throw new v(e.name);
|
|
161
|
-
const
|
|
162
|
-
if (!
|
|
163
|
-
throw new
|
|
168
|
+
const r = s(e.context)[t];
|
|
169
|
+
if (!r || typeof r.execute != "function")
|
|
170
|
+
throw new m(e.name);
|
|
164
171
|
const n = e.args && typeof e.args == "object" ? e.args : {};
|
|
165
|
-
if (
|
|
166
|
-
const
|
|
167
|
-
if (!
|
|
168
|
-
return
|
|
169
|
-
`Invalid arguments for "${e.name}": ${x(
|
|
172
|
+
if (r.inputSchema && typeof r.inputSchema.safeParse == "function") {
|
|
173
|
+
const i = r.inputSchema.safeParse(n);
|
|
174
|
+
if (!i.success)
|
|
175
|
+
return g(
|
|
176
|
+
`Invalid arguments for "${e.name}": ${x(i.error)}`,
|
|
170
177
|
!0
|
|
171
178
|
);
|
|
172
179
|
}
|
|
173
180
|
try {
|
|
174
|
-
const
|
|
181
|
+
const i = await r.execute(n), c = JSON.stringify(i ?? { success: !0 }, null, 2), l = i && typeof i == "object" && !Array.isArray(i) ? i : null, f = l?.success === !1;
|
|
175
182
|
return {
|
|
176
183
|
content: [{ text: c, type: "text" }],
|
|
177
184
|
isError: f,
|
|
178
185
|
...l ? { structuredContent: l } : {}
|
|
179
186
|
};
|
|
180
|
-
} catch (
|
|
181
|
-
const c =
|
|
182
|
-
return
|
|
187
|
+
} catch (i) {
|
|
188
|
+
const c = i instanceof Error ? i.message : String(i);
|
|
189
|
+
return g(`Tool "${e.name}" failed: ${c}`, !0);
|
|
183
190
|
}
|
|
184
191
|
}
|
|
185
192
|
const A = [
|
|
@@ -205,33 +212,45 @@ const A = [
|
|
|
205
212
|
uri: "cortex://schema/custom-blocks"
|
|
206
213
|
}
|
|
207
214
|
];
|
|
208
|
-
async function
|
|
215
|
+
async function P(e) {
|
|
209
216
|
const t = s(e.context);
|
|
210
217
|
if (e.uri === "cortex://schema/database") {
|
|
211
|
-
const
|
|
218
|
+
const a = await t.describe_database_schema?.execute?.({ includeReadOnly: !0 });
|
|
212
219
|
return {
|
|
213
220
|
mimeType: "application/json",
|
|
214
|
-
text: JSON.stringify(
|
|
221
|
+
text: JSON.stringify(a ?? {}, null, 2),
|
|
215
222
|
uri: e.uri
|
|
216
223
|
};
|
|
217
224
|
}
|
|
218
225
|
if (e.uri === "cortex://schema/blocks")
|
|
219
226
|
return {
|
|
220
227
|
mimeType: "application/json",
|
|
221
|
-
text: JSON.stringify({ blockTypes:
|
|
228
|
+
text: JSON.stringify({ blockTypes: y }, null, 2),
|
|
222
229
|
uri: e.uri
|
|
223
230
|
};
|
|
224
231
|
if (e.uri === "cortex://schema/custom-blocks") {
|
|
225
|
-
const
|
|
232
|
+
const a = await t.list_custom_blocks?.execute?.({});
|
|
226
233
|
return {
|
|
227
234
|
mimeType: "application/json",
|
|
228
|
-
text: JSON.stringify(
|
|
235
|
+
text: JSON.stringify(a ?? {}, null, 2),
|
|
229
236
|
uri: e.uri
|
|
230
237
|
};
|
|
231
238
|
}
|
|
232
239
|
return null;
|
|
233
240
|
}
|
|
234
241
|
const C = [
|
|
242
|
+
{
|
|
243
|
+
arguments: [
|
|
244
|
+
{
|
|
245
|
+
description: 'Everything known about the client and the site they want: business, audience, goals, pages (or "one landing page"), languages, brand, contact details. Leave blank to be asked.',
|
|
246
|
+
name: "brief",
|
|
247
|
+
required: !1
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
description: "Build (or rebuild) the whole NextBlock site from a brief: remove the sample content, set the identity and theme, create and publish every page, the menus, the footer, and the translations.",
|
|
251
|
+
name: "build-site",
|
|
252
|
+
title: "Build the whole site"
|
|
253
|
+
},
|
|
235
254
|
{
|
|
236
255
|
arguments: [
|
|
237
256
|
{ description: "What the page is for, and any brand or tone notes.", name: "brief", required: !0 },
|
|
@@ -260,6 +279,20 @@ const C = [
|
|
|
260
279
|
title: "Translate a page or post"
|
|
261
280
|
}
|
|
262
281
|
], S = {
|
|
282
|
+
"build-site": (e) => [
|
|
283
|
+
"Build the whole NextBlock site for this client.",
|
|
284
|
+
"",
|
|
285
|
+
e.brief?.trim() ? `Brief:
|
|
286
|
+
${e.brief}` : "No brief was given: first ask, in one short numbered list, about the business and what it does, the audience and the one action visitors should take, the site shape (one landing page or which pages), the languages, brand colours and tone, contact details, and whether existing content is kept.",
|
|
287
|
+
"",
|
|
288
|
+
"Process:",
|
|
289
|
+
"1. Call get_site_overview to see what exists, the active languages, and whether the NextBlock demo content is still present. Record what you know with save_site_brief.",
|
|
290
|
+
"2. Present the plan (what is removed, every page with its sections, menus, footer, theme, languages) and call start_site_build with `summary`, `brief`, and `reset` (keepLanguages = the languages wanted) unless existing content is kept. Over MCP the host approves the call; there is no chat confirmation.",
|
|
291
|
+
'3. Build: update_site_identity; manage_language as needed; manage_site_theme with the brand colours; search_stock_media for imagery when available; rewrite the empty "home" page with generate_jsonb_layout then publish_content_draft; create_cms_page (status "published") for every other page; update_site_navigation (mode "replace") and update_footer for every language; translate_content_bulk for each extra language.',
|
|
292
|
+
"4. Call finish_site_build with a summary, then report every page with its URL.",
|
|
293
|
+
"Write real copy from the brief, never placeholders. Every page must exist in every active language."
|
|
294
|
+
].join(`
|
|
295
|
+
`),
|
|
263
296
|
"build-page": (e) => [
|
|
264
297
|
`Build the NextBlock page with slug "${e.slug ?? "<slug>"}" from this brief:`,
|
|
265
298
|
"",
|
|
@@ -288,12 +321,12 @@ const C = [
|
|
|
288
321
|
].join(`
|
|
289
322
|
`)
|
|
290
323
|
};
|
|
291
|
-
function
|
|
292
|
-
const t = C.find((
|
|
293
|
-
return !t || !
|
|
324
|
+
function E(e) {
|
|
325
|
+
const t = C.find((r) => r.name === e.name), a = S[e.name];
|
|
326
|
+
return !t || !a ? null : {
|
|
294
327
|
description: t.description,
|
|
295
328
|
messages: [
|
|
296
|
-
{ content: { text:
|
|
329
|
+
{ content: { text: a(e.args ?? {}), type: "text" }, role: "user" }
|
|
297
330
|
]
|
|
298
331
|
};
|
|
299
332
|
}
|
|
@@ -301,15 +334,15 @@ export {
|
|
|
301
334
|
C as CORTEX_MCP_PROMPTS,
|
|
302
335
|
A as CORTEX_MCP_RESOURCES,
|
|
303
336
|
u as CORTEX_MCP_TOOL_ALIASES,
|
|
304
|
-
|
|
337
|
+
o as CORTEX_MCP_TOOL_KINDS,
|
|
305
338
|
v as CortexMcpForbiddenToolError,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
339
|
+
m as CortexMcpUnknownToolError,
|
|
340
|
+
M as assertCortexMcpToolCoverage,
|
|
341
|
+
N as buildCortexMcpToolDefinitions,
|
|
342
|
+
O as callCortexMcpTool,
|
|
310
343
|
d as cortexMcpScopesAllow,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
344
|
+
E as getCortexMcpPrompt,
|
|
345
|
+
k as getCortexMcpToolKind,
|
|
346
|
+
P as readCortexMcpResource,
|
|
347
|
+
_ as resolveCortexMcpToolName
|
|
315
348
|
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require("./zod-config.cjs.js");const e=require("zod"),c="cortex_ai_site_brief",m="cortex_ai_build_session",l=e.z.string().trim().min(2).max(10),p=e.z.strictObject({purpose:e.z.string().trim().max(600).optional().describe("What this page is for and what a visitor should do there."),sections:e.z.array(e.z.string().trim().min(1).max(200)).max(20).optional().describe('Planned sections, top to bottom, e.g. ["hero", "services", "testimonials", "contact form"].'),slug:e.z.string().trim().min(1).max(120).describe('URL slug in the primary language, e.g. "home", "services", "contact".'),title:e.z.string().trim().min(1).max(200)}),u=e.z.strictObject({accent_color:e.z.string().trim().max(40).optional(),color_scheme:e.z.enum(["light","dark"]).optional().describe("Overall look the client prefers."),font_style:e.z.string().trim().max(120).optional().describe('e.g. "clean sans-serif", "elegant serif".'),logo_notes:e.z.string().trim().max(600).optional().describe("Whether they have a logo, where it is, or what to do meanwhile."),primary_color:e.z.string().trim().max(40).optional().describe('Hex or plain colour name, e.g. "#1e6f5c" or "forest green".'),secondary_color:e.z.string().trim().max(40).optional(),style_keywords:e.z.array(e.z.string().trim().min(1).max(60)).max(12).optional().describe('e.g. ["minimal", "warm", "premium"].'),tone:e.z.string().trim().max(200).optional().describe('Voice for the copy, e.g. "friendly and plain-spoken".')}).partial(),d=e.z.strictObject({address:e.z.string().trim().max(400).optional(),email:e.z.string().trim().max(200).optional(),hours:e.z.string().trim().max(400).optional(),phone:e.z.string().trim().max(60).optional(),social:e.z.array(e.z.strictObject({platform:e.z.string().trim().min(1).max(60),url:e.z.string().trim().min(1).max(2048)})).max(12).optional()}).partial(),r=e.z.strictObject({audience:e.z.string().trim().max(1e3).optional().describe("Who the site is for."),brand:u.optional(),business_name:e.z.string().trim().min(1).max(160),contact:d.optional(),description:e.z.string().trim().max(3e3).optional().describe("What the business does, in the client's own words where possible."),ecommerce:e.z.strictObject({enabled:e.z.boolean().default(!1),products_summary:e.z.string().trim().max(1e3).optional()}).optional(),goals:e.z.array(e.z.string().trim().min(1).max(300)).max(10).optional().describe('What the site must achieve, e.g. ["get quote requests", "show the menu"].'),keep_existing_content:e.z.boolean().default(!1).describe("true when the client wants existing pages/posts kept rather than replaced."),languages:e.z.array(l).min(1).max(10).default(["en"]).describe("Locale codes the public site must offer, primary first."),notes:e.z.string().trim().max(4e3).optional().describe("Anything else the client said that matters."),pages:e.z.array(p).max(30).default([]).describe('The pages to build. A one-page landing site is a single "home" entry.'),primary_language:l.default("en"),site_type:e.z.enum(["landing-page","multi-page","blog","store","portfolio","other"]).default("multi-page"),status:e.z.enum(["draft","confirmed","built"]).default("draft").describe("draft while interviewing, confirmed once the client approved the plan, built when the build finished."),tagline:e.z.string().trim().max(200).optional(),updated_at:e.z.string().max(80).optional()}),g=r.partial(),h=e.z.strictObject({actorUserId:e.z.string().min(1),createdAt:e.z.string().min(1),expiresAt:e.z.string().min(1),id:e.z.string().min(1),summary:e.z.string().max(4e3)});function _(t){const o=r.safeParse(t);return o.success?o.data:null}function x(t,o){if(!t||!o.actorUserId||t.actorUserId!==o.actorUserId)return!1;const i=Date.parse(t.expiresAt),s=(o.now??new Date).getTime();return Number.isFinite(i)&&i>s}function f(t){const o=[`Business: ${t.business_name}${t.tagline?` — ${t.tagline}`:""}`];t.description&&o.push(`What they do: ${t.description}`),t.audience&&o.push(`Audience: ${t.audience}`),t.goals?.length&&o.push(`Goals: ${t.goals.join("; ")}`),o.push(`Site type: ${t.site_type}. Languages: ${t.languages.join(", ")} (primary ${t.primary_language}).`),t.pages.length>0&&o.push(`Pages: ${t.pages.map(n=>`${n.title} (/${n.slug})${n.purpose?` — ${n.purpose}`:""}`).join("; ")}`);const i=t.brand;if(i&&Object.keys(i).length>0){const n=[i.primary_color?`primary ${i.primary_color}`:null,i.secondary_color?`secondary ${i.secondary_color}`:null,i.accent_color?`accent ${i.accent_color}`:null,i.color_scheme?`${i.color_scheme} scheme`:null,i.font_style?`fonts: ${i.font_style}`:null,i.tone?`tone: ${i.tone}`:null,i.style_keywords?.length?`style: ${i.style_keywords.join(", ")}`:null,i.logo_notes?`logo: ${i.logo_notes}`:null].filter(Boolean);n.length>0&&o.push(`Brand: ${n.join("; ")}`)}const s=t.contact;if(s&&Object.keys(s).length>0){const n=[s.email?`email ${s.email}`:null,s.phone?`phone ${s.phone}`:null,s.address?`address ${s.address}`:null,s.hours?`hours ${s.hours}`:null,s.social?.length?`social: ${s.social.map(a=>`${a.platform} ${a.url}`).join(", ")}`:null].filter(Boolean);n.length>0&&o.push(`Contact: ${n.join("; ")}`)}return t.ecommerce?.enabled&&o.push(`Ecommerce: yes${t.ecommerce.products_summary?` — ${t.ecommerce.products_summary}`:""}`),t.keep_existing_content&&o.push("Keep existing content: yes (do not wipe pages or posts)."),t.notes&&o.push(`Notes: ${t.notes}`),o.push(`Brief status: ${t.status}.`),o.join(`
|
|
2
|
+
`)}exports.CORTEX_AI_BUILD_SESSION_SETTING_KEY=m;exports.CORTEX_AI_SITE_BRIEF_SETTING_KEY=c;exports.cortexBuildSessionSchema=h;exports.cortexSiteBriefInputSchema=g;exports.cortexSiteBriefSchema=r;exports.formatCortexSiteBriefForPrompt=f;exports.isCortexBuildSessionUsable=x;exports.safeParseCortexSiteBrief=_;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { z } from './zod-config';
|
|
2
|
+
/**
|
|
3
|
+
* The site brief: what the client wants their website to be.
|
|
4
|
+
*
|
|
5
|
+
* Cortex fills this in during the site-builder interview and persists it in
|
|
6
|
+
* `site_settings` so that every later conversation — dashboard chat, MCP client,
|
|
7
|
+
* a follow-up "make the whole site feel warmer" prompt weeks later — starts from
|
|
8
|
+
* the same understanding of the business instead of re-asking. It is plain,
|
|
9
|
+
* non-secret JSON: readable by any admin session and by every tool context.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CORTEX_AI_SITE_BRIEF_SETTING_KEY = "cortex_ai_site_brief";
|
|
12
|
+
/**
|
|
13
|
+
* A time-boxed authorisation for Cortex to apply an approved build plan without a
|
|
14
|
+
* confirmation click per tool call. Written only by `start_site_build` after the
|
|
15
|
+
* operator confirms the plan; the chat route honours it for the actor who opened it
|
|
16
|
+
* and only until `expiresAt`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const CORTEX_AI_BUILD_SESSION_SETTING_KEY = "cortex_ai_build_session";
|
|
19
|
+
/** The complete brief as stored. Every field a build step may need has a default. */
|
|
20
|
+
export declare const cortexSiteBriefSchema: z.ZodObject<{
|
|
21
|
+
audience: z.ZodOptional<z.ZodString>;
|
|
22
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
23
|
+
accent_color: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
24
|
+
color_scheme: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
25
|
+
light: "light";
|
|
26
|
+
dark: "dark";
|
|
27
|
+
}>>>;
|
|
28
|
+
font_style: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
29
|
+
logo_notes: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
30
|
+
primary_color: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
31
|
+
secondary_color: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
32
|
+
style_keywords: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
33
|
+
tone: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
34
|
+
}, z.core.$strict>>;
|
|
35
|
+
business_name: z.ZodString;
|
|
36
|
+
contact: z.ZodOptional<z.ZodObject<{
|
|
37
|
+
address: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
38
|
+
email: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
39
|
+
hours: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
40
|
+
phone: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
41
|
+
social: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
42
|
+
platform: z.ZodString;
|
|
43
|
+
url: z.ZodString;
|
|
44
|
+
}, z.core.$strict>>>>;
|
|
45
|
+
}, z.core.$strict>>;
|
|
46
|
+
description: z.ZodOptional<z.ZodString>;
|
|
47
|
+
ecommerce: z.ZodOptional<z.ZodObject<{
|
|
48
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
49
|
+
products_summary: z.ZodOptional<z.ZodString>;
|
|
50
|
+
}, z.core.$strict>>;
|
|
51
|
+
goals: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
52
|
+
keep_existing_content: z.ZodDefault<z.ZodBoolean>;
|
|
53
|
+
languages: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
54
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
55
|
+
pages: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
56
|
+
purpose: z.ZodOptional<z.ZodString>;
|
|
57
|
+
sections: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
58
|
+
slug: z.ZodString;
|
|
59
|
+
title: z.ZodString;
|
|
60
|
+
}, z.core.$strict>>>;
|
|
61
|
+
primary_language: z.ZodDefault<z.ZodString>;
|
|
62
|
+
site_type: z.ZodDefault<z.ZodEnum<{
|
|
63
|
+
other: "other";
|
|
64
|
+
"landing-page": "landing-page";
|
|
65
|
+
"multi-page": "multi-page";
|
|
66
|
+
blog: "blog";
|
|
67
|
+
store: "store";
|
|
68
|
+
portfolio: "portfolio";
|
|
69
|
+
}>>;
|
|
70
|
+
status: z.ZodDefault<z.ZodEnum<{
|
|
71
|
+
draft: "draft";
|
|
72
|
+
confirmed: "confirmed";
|
|
73
|
+
built: "built";
|
|
74
|
+
}>>;
|
|
75
|
+
tagline: z.ZodOptional<z.ZodString>;
|
|
76
|
+
updated_at: z.ZodOptional<z.ZodString>;
|
|
77
|
+
}, z.core.$strict>;
|
|
78
|
+
export type CortexSiteBrief = z.infer<typeof cortexSiteBriefSchema>;
|
|
79
|
+
/** What a tool may pass to save or merge: every field optional. */
|
|
80
|
+
export declare const cortexSiteBriefInputSchema: z.ZodObject<{
|
|
81
|
+
audience: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
82
|
+
brand: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
83
|
+
accent_color: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
84
|
+
color_scheme: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
85
|
+
light: "light";
|
|
86
|
+
dark: "dark";
|
|
87
|
+
}>>>;
|
|
88
|
+
font_style: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
89
|
+
logo_notes: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
90
|
+
primary_color: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
91
|
+
secondary_color: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
92
|
+
style_keywords: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
93
|
+
tone: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
94
|
+
}, z.core.$strict>>>;
|
|
95
|
+
business_name: z.ZodOptional<z.ZodString>;
|
|
96
|
+
contact: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
97
|
+
address: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
98
|
+
email: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
99
|
+
hours: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
100
|
+
phone: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
101
|
+
social: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
102
|
+
platform: z.ZodString;
|
|
103
|
+
url: z.ZodString;
|
|
104
|
+
}, z.core.$strict>>>>;
|
|
105
|
+
}, z.core.$strict>>>;
|
|
106
|
+
description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
107
|
+
ecommerce: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
108
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
109
|
+
products_summary: z.ZodOptional<z.ZodString>;
|
|
110
|
+
}, z.core.$strict>>>;
|
|
111
|
+
goals: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
112
|
+
keep_existing_content: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
113
|
+
languages: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
|
|
114
|
+
notes: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
115
|
+
pages: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
116
|
+
purpose: z.ZodOptional<z.ZodString>;
|
|
117
|
+
sections: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
118
|
+
slug: z.ZodString;
|
|
119
|
+
title: z.ZodString;
|
|
120
|
+
}, z.core.$strict>>>>;
|
|
121
|
+
primary_language: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
122
|
+
site_type: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
|
|
123
|
+
other: "other";
|
|
124
|
+
"landing-page": "landing-page";
|
|
125
|
+
"multi-page": "multi-page";
|
|
126
|
+
blog: "blog";
|
|
127
|
+
store: "store";
|
|
128
|
+
portfolio: "portfolio";
|
|
129
|
+
}>>>;
|
|
130
|
+
status: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
|
|
131
|
+
draft: "draft";
|
|
132
|
+
confirmed: "confirmed";
|
|
133
|
+
built: "built";
|
|
134
|
+
}>>>;
|
|
135
|
+
tagline: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
136
|
+
updated_at: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
137
|
+
}, z.core.$strict>;
|
|
138
|
+
export type CortexSiteBriefInput = z.infer<typeof cortexSiteBriefInputSchema>;
|
|
139
|
+
export declare const cortexBuildSessionSchema: z.ZodObject<{
|
|
140
|
+
actorUserId: z.ZodString;
|
|
141
|
+
createdAt: z.ZodString;
|
|
142
|
+
expiresAt: z.ZodString;
|
|
143
|
+
id: z.ZodString;
|
|
144
|
+
summary: z.ZodString;
|
|
145
|
+
}, z.core.$strict>;
|
|
146
|
+
export type CortexBuildSession = z.infer<typeof cortexBuildSessionSchema>;
|
|
147
|
+
/** Parse a stored brief leniently: an unreadable row is treated as "no brief". */
|
|
148
|
+
export declare function safeParseCortexSiteBrief(value: unknown): CortexSiteBrief | null;
|
|
149
|
+
/** A build session is only usable by the admin who opened it, and only until it expires. */
|
|
150
|
+
export declare function isCortexBuildSessionUsable(session: CortexBuildSession | null | undefined, params: {
|
|
151
|
+
actorUserId: string | null | undefined;
|
|
152
|
+
now?: Date;
|
|
153
|
+
}): session is CortexBuildSession;
|
|
154
|
+
/**
|
|
155
|
+
* Compact, prompt-ready rendering of the brief. The model reads this at the top of
|
|
156
|
+
* every conversation once a brief exists, so it is deliberately short: facts, not
|
|
157
|
+
* prose, and never the raw JSON (which wastes tokens on braces and quotes).
|
|
158
|
+
*/
|
|
159
|
+
export declare function formatCortexSiteBriefForPrompt(brief: CortexSiteBrief): string;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import "./zod-config.es.js";
|
|
2
|
+
import { z as e } from "zod";
|
|
3
|
+
const d = "cortex_ai_site_brief", h = "cortex_ai_build_session", r = e.string().trim().min(2).max(10), c = e.strictObject({
|
|
4
|
+
purpose: e.string().trim().max(600).optional().describe("What this page is for and what a visitor should do there."),
|
|
5
|
+
sections: e.array(e.string().trim().min(1).max(200)).max(20).optional().describe('Planned sections, top to bottom, e.g. ["hero", "services", "testimonials", "contact form"].'),
|
|
6
|
+
slug: e.string().trim().min(1).max(120).describe('URL slug in the primary language, e.g. "home", "services", "contact".'),
|
|
7
|
+
title: e.string().trim().min(1).max(200)
|
|
8
|
+
}), m = e.strictObject({
|
|
9
|
+
accent_color: e.string().trim().max(40).optional(),
|
|
10
|
+
color_scheme: e.enum(["light", "dark"]).optional().describe("Overall look the client prefers."),
|
|
11
|
+
font_style: e.string().trim().max(120).optional().describe('e.g. "clean sans-serif", "elegant serif".'),
|
|
12
|
+
logo_notes: e.string().trim().max(600).optional().describe("Whether they have a logo, where it is, or what to do meanwhile."),
|
|
13
|
+
primary_color: e.string().trim().max(40).optional().describe('Hex or plain colour name, e.g. "#1e6f5c" or "forest green".'),
|
|
14
|
+
secondary_color: e.string().trim().max(40).optional(),
|
|
15
|
+
style_keywords: e.array(e.string().trim().min(1).max(60)).max(12).optional().describe('e.g. ["minimal", "warm", "premium"].'),
|
|
16
|
+
tone: e.string().trim().max(200).optional().describe('Voice for the copy, e.g. "friendly and plain-spoken".')
|
|
17
|
+
}).partial(), p = e.strictObject({
|
|
18
|
+
address: e.string().trim().max(400).optional(),
|
|
19
|
+
email: e.string().trim().max(200).optional(),
|
|
20
|
+
hours: e.string().trim().max(400).optional(),
|
|
21
|
+
phone: e.string().trim().max(60).optional(),
|
|
22
|
+
social: e.array(
|
|
23
|
+
e.strictObject({
|
|
24
|
+
platform: e.string().trim().min(1).max(60),
|
|
25
|
+
url: e.string().trim().min(1).max(2048)
|
|
26
|
+
})
|
|
27
|
+
).max(12).optional()
|
|
28
|
+
}).partial(), l = e.strictObject({
|
|
29
|
+
audience: e.string().trim().max(1e3).optional().describe("Who the site is for."),
|
|
30
|
+
brand: m.optional(),
|
|
31
|
+
business_name: e.string().trim().min(1).max(160),
|
|
32
|
+
contact: p.optional(),
|
|
33
|
+
description: e.string().trim().max(3e3).optional().describe("What the business does, in the client's own words where possible."),
|
|
34
|
+
ecommerce: e.strictObject({
|
|
35
|
+
enabled: e.boolean().default(!1),
|
|
36
|
+
products_summary: e.string().trim().max(1e3).optional()
|
|
37
|
+
}).optional(),
|
|
38
|
+
goals: e.array(e.string().trim().min(1).max(300)).max(10).optional().describe('What the site must achieve, e.g. ["get quote requests", "show the menu"].'),
|
|
39
|
+
keep_existing_content: e.boolean().default(!1).describe("true when the client wants existing pages/posts kept rather than replaced."),
|
|
40
|
+
languages: e.array(r).min(1).max(10).default(["en"]).describe("Locale codes the public site must offer, primary first."),
|
|
41
|
+
notes: e.string().trim().max(4e3).optional().describe("Anything else the client said that matters."),
|
|
42
|
+
pages: e.array(c).max(30).default([]).describe('The pages to build. A one-page landing site is a single "home" entry.'),
|
|
43
|
+
primary_language: r.default("en"),
|
|
44
|
+
site_type: e.enum(["landing-page", "multi-page", "blog", "store", "portfolio", "other"]).default("multi-page"),
|
|
45
|
+
status: e.enum(["draft", "confirmed", "built"]).default("draft").describe("draft while interviewing, confirmed once the client approved the plan, built when the build finished."),
|
|
46
|
+
tagline: e.string().trim().max(200).optional(),
|
|
47
|
+
updated_at: e.string().max(80).optional()
|
|
48
|
+
}), _ = l.partial(), x = e.strictObject({
|
|
49
|
+
actorUserId: e.string().min(1),
|
|
50
|
+
createdAt: e.string().min(1),
|
|
51
|
+
expiresAt: e.string().min(1),
|
|
52
|
+
id: e.string().min(1),
|
|
53
|
+
summary: e.string().max(4e3)
|
|
54
|
+
});
|
|
55
|
+
function y(t) {
|
|
56
|
+
const o = l.safeParse(t);
|
|
57
|
+
return o.success ? o.data : null;
|
|
58
|
+
}
|
|
59
|
+
function f(t, o) {
|
|
60
|
+
if (!t || !o.actorUserId || t.actorUserId !== o.actorUserId)
|
|
61
|
+
return !1;
|
|
62
|
+
const i = Date.parse(t.expiresAt), n = (o.now ?? /* @__PURE__ */ new Date()).getTime();
|
|
63
|
+
return Number.isFinite(i) && i > n;
|
|
64
|
+
}
|
|
65
|
+
function $(t) {
|
|
66
|
+
const o = [
|
|
67
|
+
`Business: ${t.business_name}${t.tagline ? ` — ${t.tagline}` : ""}`
|
|
68
|
+
];
|
|
69
|
+
t.description && o.push(`What they do: ${t.description}`), t.audience && o.push(`Audience: ${t.audience}`), t.goals?.length && o.push(`Goals: ${t.goals.join("; ")}`), o.push(`Site type: ${t.site_type}. Languages: ${t.languages.join(", ")} (primary ${t.primary_language}).`), t.pages.length > 0 && o.push(
|
|
70
|
+
`Pages: ${t.pages.map((s) => `${s.title} (/${s.slug})${s.purpose ? ` — ${s.purpose}` : ""}`).join("; ")}`
|
|
71
|
+
);
|
|
72
|
+
const i = t.brand;
|
|
73
|
+
if (i && Object.keys(i).length > 0) {
|
|
74
|
+
const s = [
|
|
75
|
+
i.primary_color ? `primary ${i.primary_color}` : null,
|
|
76
|
+
i.secondary_color ? `secondary ${i.secondary_color}` : null,
|
|
77
|
+
i.accent_color ? `accent ${i.accent_color}` : null,
|
|
78
|
+
i.color_scheme ? `${i.color_scheme} scheme` : null,
|
|
79
|
+
i.font_style ? `fonts: ${i.font_style}` : null,
|
|
80
|
+
i.tone ? `tone: ${i.tone}` : null,
|
|
81
|
+
i.style_keywords?.length ? `style: ${i.style_keywords.join(", ")}` : null,
|
|
82
|
+
i.logo_notes ? `logo: ${i.logo_notes}` : null
|
|
83
|
+
].filter(Boolean);
|
|
84
|
+
s.length > 0 && o.push(`Brand: ${s.join("; ")}`);
|
|
85
|
+
}
|
|
86
|
+
const n = t.contact;
|
|
87
|
+
if (n && Object.keys(n).length > 0) {
|
|
88
|
+
const s = [
|
|
89
|
+
n.email ? `email ${n.email}` : null,
|
|
90
|
+
n.phone ? `phone ${n.phone}` : null,
|
|
91
|
+
n.address ? `address ${n.address}` : null,
|
|
92
|
+
n.hours ? `hours ${n.hours}` : null,
|
|
93
|
+
n.social?.length ? `social: ${n.social.map((a) => `${a.platform} ${a.url}`).join(", ")}` : null
|
|
94
|
+
].filter(Boolean);
|
|
95
|
+
s.length > 0 && o.push(`Contact: ${s.join("; ")}`);
|
|
96
|
+
}
|
|
97
|
+
return t.ecommerce?.enabled && o.push(`Ecommerce: yes${t.ecommerce.products_summary ? ` — ${t.ecommerce.products_summary}` : ""}`), t.keep_existing_content && o.push("Keep existing content: yes (do not wipe pages or posts)."), t.notes && o.push(`Notes: ${t.notes}`), o.push(`Brief status: ${t.status}.`), o.join(`
|
|
98
|
+
`);
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
h as CORTEX_AI_BUILD_SESSION_SETTING_KEY,
|
|
102
|
+
d as CORTEX_AI_SITE_BRIEF_SETTING_KEY,
|
|
103
|
+
x as cortexBuildSessionSchema,
|
|
104
|
+
_ as cortexSiteBriefInputSchema,
|
|
105
|
+
l as cortexSiteBriefSchema,
|
|
106
|
+
$ as formatCortexSiteBriefForPrompt,
|
|
107
|
+
f as isCortexBuildSessionUsable,
|
|
108
|
+
y as safeParseCortexSiteBrief
|
|
109
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextblock-cms/cortex",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.8",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@ai-sdk/openai-compatible": "^2.0.42",
|
|
31
|
-
"@nextblock-cms/db": "^0.17.
|
|
32
|
-
"@nextblock-cms/ecommerce": "npm:@nextblock-cms/ecom@^0.17.
|
|
33
|
-
"@nextblock-cms/utils": "^0.17.
|
|
31
|
+
"@nextblock-cms/db": "^0.17.8",
|
|
32
|
+
"@nextblock-cms/ecommerce": "npm:@nextblock-cms/ecom@^0.17.8",
|
|
33
|
+
"@nextblock-cms/utils": "^0.17.8",
|
|
34
34
|
"ai": "^6.0.170",
|
|
35
35
|
"zod": "^4.3.6"
|
|
36
36
|
},
|