@nextblock-cms/cortex 0.14.5 → 0.15.0
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 +190 -138
- package/lib/ai-global-agent-content-ops-tools.cjs.js +1 -0
- package/lib/ai-global-agent-content-ops-tools.d.ts +175 -0
- package/lib/ai-global-agent-content-ops-tools.es.js +211 -0
- package/lib/ai-global-agent-custom-block-tools.cjs.js +1 -1
- package/lib/ai-global-agent-custom-block-tools.es.js +69 -59
- package/lib/ai-global-agent-db-tools.cjs.js +1 -1
- package/lib/ai-global-agent-db-tools.d.ts +8 -8
- package/lib/ai-global-agent-db-tools.es.js +143 -127
- package/lib/ai-global-agent-theming-tools.cjs.js +3 -0
- package/lib/ai-global-agent-theming-tools.d.ts +264 -0
- package/lib/ai-global-agent-theming-tools.es.js +412 -0
- package/lib/ai-global-agent-tools.cjs.js +2 -2
- package/lib/ai-global-agent-tools.d.ts +688 -63
- package/lib/ai-global-agent-tools.es.js +2180 -1758
- package/lib/cortex-widget-schema.cjs.js +1 -1
- package/lib/cortex-widget-schema.d.ts +10 -0
- package/lib/cortex-widget-schema.es.js +1 -1
- package/lib/mcp-server.cjs.js +1 -0
- package/lib/mcp-server.d.ts +76 -0
- package/lib/mcp-server.es.js +157 -0
- package/lib/mcp-tokens.cjs.js +1 -0
- package/lib/mcp-tokens.d.ts +95 -0
- package/lib/mcp-tokens.es.js +104 -0
- package/lib/mcp-tool-registry.cjs.js +4 -0
- package/lib/mcp-tool-registry.d.ts +249 -0
- package/lib/mcp-tool-registry.es.js +315 -0
- package/package.json +4 -4
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { tool as m } from "ai";
|
|
2
|
+
import "./zod-config.es.js";
|
|
3
|
+
import { z as r } from "zod";
|
|
4
|
+
const $ = "id, file_name, object_key, file_type, width, height, description, folder, created_at";
|
|
5
|
+
function f(e) {
|
|
6
|
+
if (!e?.supabase)
|
|
7
|
+
throw new Error("No database connection is available for this tool.");
|
|
8
|
+
return e.supabase;
|
|
9
|
+
}
|
|
10
|
+
function c(e) {
|
|
11
|
+
return e ? e instanceof Error ? e.message : typeof e == "object" && "message" in e ? String(e.message) : String(e) : "unknown error";
|
|
12
|
+
}
|
|
13
|
+
function _(e) {
|
|
14
|
+
return e.normalize("NFD").replace(new RegExp("\\p{Diacritic}", "gu"), "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
15
|
+
}
|
|
16
|
+
const x = r.strictObject({
|
|
17
|
+
limit: r.number().int().min(1).max(100).default(30),
|
|
18
|
+
query: r.string().trim().max(120).optional().describe('Filter on file name or alt text, e.g. "wormwood" or "laboratory".')
|
|
19
|
+
}), E = r.strictObject({
|
|
20
|
+
altText: r.string().trim().max(300).optional().describe("Alt text describing the image. Write real alt text — it is what screen readers announce and what search engines index."),
|
|
21
|
+
url: r.string().trim().min(1).max(2048).describe("Public https URL of the image to bring into the media library.")
|
|
22
|
+
});
|
|
23
|
+
async function v(e, i) {
|
|
24
|
+
const t = x.parse(e);
|
|
25
|
+
let a = f(i).from("media").select($).order("created_at", { ascending: !1 }).limit(t.limit);
|
|
26
|
+
if (t.query) {
|
|
27
|
+
const s = `%${t.query}%`;
|
|
28
|
+
a = a.or(`file_name.ilike.${s},description.ilike.${s}`);
|
|
29
|
+
}
|
|
30
|
+
const { data: u, error: l } = await a;
|
|
31
|
+
if (l)
|
|
32
|
+
throw new Error(`Could not read the media library: ${c(l)}`);
|
|
33
|
+
return { count: (u ?? []).length, media: u ?? [], success: !0 };
|
|
34
|
+
}
|
|
35
|
+
async function I(e, i) {
|
|
36
|
+
const t = E.parse(e), o = i?.importExternalImage;
|
|
37
|
+
if (!o)
|
|
38
|
+
throw new Error("Media import is not available on this connection.");
|
|
39
|
+
const a = await o({
|
|
40
|
+
...t.altText ? { altText: t.altText } : {},
|
|
41
|
+
url: t.url
|
|
42
|
+
});
|
|
43
|
+
if ("error" in a)
|
|
44
|
+
throw new Error(`Could not import that image: ${a.error}`);
|
|
45
|
+
return { mediaId: a.id, mutationExecuted: !0, success: !0 };
|
|
46
|
+
}
|
|
47
|
+
const C = r.strictObject({
|
|
48
|
+
code: r.string().trim().min(2).max(10).describe('Locale code, e.g. "fr", "es", "de". Re-using an existing code updates that language.'),
|
|
49
|
+
is_active: r.boolean().default(!0).describe("Inactive languages are hidden from the public switcher."),
|
|
50
|
+
is_default: r.boolean().default(!1).describe("Make this the site default. Only one language can be default; setting it clears the others."),
|
|
51
|
+
name: r.string().trim().min(1).max(80).describe('Display name, e.g. "Français".')
|
|
52
|
+
});
|
|
53
|
+
async function L(e, i) {
|
|
54
|
+
const t = C.parse(e), o = f(i), a = t.code.toLowerCase(), { data: u } = await o.from("languages").select("id, code").eq("code", a).maybeSingle(), l = {
|
|
55
|
+
code: a,
|
|
56
|
+
is_active: t.is_active,
|
|
57
|
+
name: t.name
|
|
58
|
+
};
|
|
59
|
+
let s = u?.id;
|
|
60
|
+
if (s) {
|
|
61
|
+
const { error: d } = await o.from("languages").update(l).eq("id", s);
|
|
62
|
+
if (d)
|
|
63
|
+
throw new Error(`Could not update the "${a}" language: ${c(d)}`);
|
|
64
|
+
} else {
|
|
65
|
+
const { data: d, error: n } = await o.from("languages").insert(l).select("id").single();
|
|
66
|
+
if (n || !d?.id)
|
|
67
|
+
throw new Error(`Could not create the "${a}" language: ${c(n)}`);
|
|
68
|
+
s = d.id;
|
|
69
|
+
}
|
|
70
|
+
if (t.is_default) {
|
|
71
|
+
const { error: d } = await o.from("languages").update({ is_default: !1 }).neq("id", s);
|
|
72
|
+
if (d)
|
|
73
|
+
throw new Error(`Could not clear the previous default language: ${c(d)}`);
|
|
74
|
+
const { error: n } = await o.from("languages").update({ is_default: !0 }).eq("id", s);
|
|
75
|
+
if (n)
|
|
76
|
+
throw new Error(`Could not set the default language: ${c(n)}`);
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
i?.revalidatePath?.("/", "layout");
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
code: a,
|
|
84
|
+
created: !u,
|
|
85
|
+
languageId: s,
|
|
86
|
+
mutationExecuted: !0,
|
|
87
|
+
success: !0
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const S = r.strictObject({
|
|
91
|
+
action: r.enum(["upsert", "delete"]).default("upsert"),
|
|
92
|
+
description: r.string().max(1e3).nullable().optional(),
|
|
93
|
+
name: r.string().trim().min(1).max(120).describe('Category name, e.g. "Digestive Health".'),
|
|
94
|
+
name_translations: r.record(r.string(), r.string()).optional().describe('Translated names keyed by locale code, e.g. { "fr": "Santé digestive" }.'),
|
|
95
|
+
productSlugs: r.array(r.string().trim().min(1).max(300)).max(100).optional().describe("Product slugs to place in this category. Replaces the category's current membership."),
|
|
96
|
+
slug: r.string().trim().min(1).max(120).optional().describe("URL slug. Derived from the name when omitted. Re-using a slug updates that category.")
|
|
97
|
+
});
|
|
98
|
+
async function k(e, i) {
|
|
99
|
+
const t = S.parse(e), o = f(i), a = t.slug ? _(t.slug) : _(t.name), { data: u } = await o.from("categories").select("id, name, slug").eq("slug", a).maybeSingle();
|
|
100
|
+
if (t.action === "delete") {
|
|
101
|
+
if (!u?.id)
|
|
102
|
+
return {
|
|
103
|
+
message: `No category with slug "${a}".`,
|
|
104
|
+
mutationExecuted: !1,
|
|
105
|
+
success: !1
|
|
106
|
+
};
|
|
107
|
+
const { error: n } = await o.from("categories").delete().eq("id", u.id);
|
|
108
|
+
if (n)
|
|
109
|
+
throw new Error(`Could not delete the category: ${c(n)}`);
|
|
110
|
+
return { action: "delete", mutationExecuted: !0, slug: a, success: !0 };
|
|
111
|
+
}
|
|
112
|
+
const l = {
|
|
113
|
+
name: t.name,
|
|
114
|
+
slug: a,
|
|
115
|
+
...t.description !== void 0 ? { description: t.description } : {},
|
|
116
|
+
...t.name_translations ? { name_translations: t.name_translations } : {}
|
|
117
|
+
};
|
|
118
|
+
let s = u?.id;
|
|
119
|
+
if (s) {
|
|
120
|
+
const { error: n } = await o.from("categories").update(l).eq("id", s);
|
|
121
|
+
if (n)
|
|
122
|
+
throw new Error(`Could not update the category: ${c(n)}`);
|
|
123
|
+
} else {
|
|
124
|
+
const { data: n, error: g } = await o.from("categories").insert(l).select("id").single();
|
|
125
|
+
if (g || !n?.id)
|
|
126
|
+
throw new Error(`Could not create the category: ${c(g)}`);
|
|
127
|
+
s = n.id;
|
|
128
|
+
}
|
|
129
|
+
let d = 0;
|
|
130
|
+
if (t.productSlugs) {
|
|
131
|
+
const { data: n, error: g } = await o.from("products").select("id, slug").in("slug", t.productSlugs);
|
|
132
|
+
if (g)
|
|
133
|
+
throw new Error(`Could not resolve those products: ${c(g)}`);
|
|
134
|
+
const p = n ?? [], y = t.productSlugs.filter(
|
|
135
|
+
(h) => !p.some((w) => w.slug === h)
|
|
136
|
+
);
|
|
137
|
+
if (y.length > 0)
|
|
138
|
+
throw new Error(`No product found for slug(s): ${y.join(", ")}.`);
|
|
139
|
+
const { error: b } = await o.from("product_categories").delete().eq("category_id", s);
|
|
140
|
+
if (b)
|
|
141
|
+
throw new Error(`Could not clear the category's products: ${c(b)}`);
|
|
142
|
+
if (p.length > 0) {
|
|
143
|
+
const { error: h } = await o.from("product_categories").insert(p.map((w) => ({ category_id: s, product_id: w.id })));
|
|
144
|
+
if (h)
|
|
145
|
+
throw new Error(`Could not add products to the category: ${c(h)}`);
|
|
146
|
+
d = p.length;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
categoryId: s,
|
|
151
|
+
created: !u,
|
|
152
|
+
linkedProducts: d,
|
|
153
|
+
mutationExecuted: !0,
|
|
154
|
+
slug: a,
|
|
155
|
+
success: !0
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const q = r.strictObject({});
|
|
159
|
+
async function R(e, i) {
|
|
160
|
+
const t = f(i), { data: o, error: a } = await t.from("categories").select("id, name, slug, description, name_translations").order("name");
|
|
161
|
+
if (a)
|
|
162
|
+
throw new Error(`Could not read the categories: ${c(a)}`);
|
|
163
|
+
return { categories: o ?? [], count: (o ?? []).length, success: !0 };
|
|
164
|
+
}
|
|
165
|
+
function O(e) {
|
|
166
|
+
return {
|
|
167
|
+
list_media: m({
|
|
168
|
+
description: "Browse the media library: file names, alt text, dimensions, and media ids. Read-only. Call this BEFORE importing an image so you reuse what is already stored instead of creating a duplicate — the returned `id` can be passed anywhere a media id or image reference is accepted (feature_image_id, product images, an image block).",
|
|
169
|
+
execute: (i) => v(i, e),
|
|
170
|
+
inputSchema: x,
|
|
171
|
+
strict: !0
|
|
172
|
+
}),
|
|
173
|
+
list_product_categories: m({
|
|
174
|
+
description: "List the product categories, with their slugs and translated names. Read-only. Use before creating one so an existing category is reused, and to get ids for a product_grid block filtered by category.",
|
|
175
|
+
execute: (i) => R(i, e),
|
|
176
|
+
inputSchema: q,
|
|
177
|
+
strict: !0
|
|
178
|
+
}),
|
|
179
|
+
manage_language: m({
|
|
180
|
+
description: "Add or update a site language, so the CMS can hold content in that locale. Creating the language is the FIRST step of making a site multilingual — translate_page cannot target a locale that does not exist yet. Re-using a code updates that language; set is_default to change the site default.",
|
|
181
|
+
execute: (i) => L(i, e),
|
|
182
|
+
inputSchema: C,
|
|
183
|
+
strict: !0
|
|
184
|
+
}),
|
|
185
|
+
manage_product_category: m({
|
|
186
|
+
description: "Create, update, or delete a product category, and optionally set exactly which products belong to it by slug. Categories are what a product_grid block filters on, so create these before building a shop page that shows one section of the catalogue. Passing productSlugs REPLACES the category's membership.",
|
|
187
|
+
execute: (i) => k(i, e),
|
|
188
|
+
inputSchema: S,
|
|
189
|
+
strict: !0
|
|
190
|
+
}),
|
|
191
|
+
upload_media: m({
|
|
192
|
+
description: "Import an image from a public https URL into the media library and return its media id, without attaching it to anything. Useful when you want one asset reused across several places — otherwise passing the URL straight to create_cms_product `images` or a page/post `feature_image_id` imports it in the same call. Check list_media first to avoid duplicates.",
|
|
193
|
+
execute: (i) => I(i, e),
|
|
194
|
+
inputSchema: E,
|
|
195
|
+
strict: !0
|
|
196
|
+
})
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export {
|
|
200
|
+
O as createCortexContentOpsTools,
|
|
201
|
+
v as executeListMedia,
|
|
202
|
+
R as executeListProductCategories,
|
|
203
|
+
L as executeManageLanguage,
|
|
204
|
+
k as executeManageProductCategory,
|
|
205
|
+
I as executeUploadMedia,
|
|
206
|
+
x as listMediaInputSchema,
|
|
207
|
+
q as listProductCategoriesInputSchema,
|
|
208
|
+
C as manageLanguageInputSchema,
|
|
209
|
+
S as manageProductCategoryInputSchema,
|
|
210
|
+
E as uploadMediaInputSchema
|
|
211
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var v=Object.create;var h=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,$=Object.prototype.hasOwnProperty;var z=(e,t,s,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of D(t))!$.call(e,o)&&o!==s&&h(e,o,{get:()=>t[o],enumerable:!(i=A(t,o))||i.enumerable});return e};var L=(e,t,s)=>(s=e!=null?v(U(e)):{},z(t||!e||!e.__esModule?h(s,"default",{value:e,enumerable:!0}):s,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("ai");require("./zod-config.cjs.js");const r=require("zod"),k="id, slug, name, description, fields, layout_schema, is_original",w=r.z.strictObject({context:r.z.string().trim().max(3e3).optional().describe("Optional extra constraints or brand/style guidance for the generated block."),prompt:r.z.string().trim().min(3).max(4e3).describe('Natural-language description of the custom block, including the fields it needs and the visual style. Example: "A product card with a title, image, price, and a button that links to the product page."')}),S=r.z.strictObject({prompt:r.z.string().trim().min(3).max(4e3).describe("Description of the changes to apply. The block is regenerated using its existing definition as context."),slug:r.z.string().trim().min(1).max(120).describe("Slug of the existing custom block to edit.")}),_=r.z.strictObject({slug:r.z.string().trim().min(1).max(120).describe("Slug of the custom block definition to delete.")}),E=r.z.strictObject({query:r.z.string().trim().max(120).optional().describe("Optional text filter on name or slug.")});function l(e){if(!e?.supabase)throw new Error("A Supabase service client is required to manage custom block definitions.");return e.supabase}function f(e){if(!e?.actorUserId)throw new Error("Managing custom block definitions requires an authenticated admin actor.");return e.actorUserId}function y(e){return JSON.parse(JSON.stringify(e))}function g(e){const t=Array.isArray(e?.fields)?e.fields:[];return{fieldCount:t.length,fields:t.map(s=>({key:s?.key,label:s?.label,type:s?.type})),id:e?.id,name:e?.name,slug:e?.slug}}const x=24e3;function N(e){const t=`You are editing an existing custom block named "${e.name??"Untitled"}". Keep its overall purpose and only apply the requested changes.`,s=`${t} Existing definition: ${JSON.stringify({fields:e.fields,layout_schema:e.layout_schema,name:e.name})}`;return s.length<=x?s:`${t} Existing fields: ${JSON.stringify({fields:e.fields,name:e.name})} (The existing layout was too large to include — rebuild a layout that renders these fields.)`.slice(0,x)}function B(e,t){const s=t?.cortexAiApiKey||void 0;return{apiKey:s,context:e.context,modelSelection:s&&t?.cortexAiModelSelection?t.cortexAiModelSelection:void 0,prompt:e.prompt}}function p(e){try{const{revalidatePath:t,revalidateTag:s}=require("next/cache");s("custom-block-definitions","max"),e?.id&&s(`custom-block-definitions:${e.id}`,"max"),e?.slug&&s(`custom-block-definitions:${e.slug}`,"max"),s("dynamic-layout-engine","max"),t("/cms/custom-blocks"),t("/cms/blocks")}catch{}}function C(e){return(e||"").replace(/\s+/g," ").trim().toUpperCase()}function P(e){return`CONFIRM DELETE CUSTOM BLOCK ${e.toUpperCase()}`}function M(e,t){return e?.skipConfirmation?!0:C(e?.latestUserMessage).includes(C(t))}function m(e){if(e&&typeof e=="object"){const t=e;if(t.code==="23505")return"A custom block with that slug already exists. Choose a different name or edit the existing one.";if(typeof t.message=="string"&&t.message)return t.message}return e instanceof Error?e.message:"Unknown error."}async function q(e,t){try{const s=l(t);f(t);const[{generateCortexWidgetDefinition:i},{insertCortexWidgetDefinition:o}]=await Promise.all([Promise.resolve().then(()=>require("./ai-cortex-widget-builder.cjs.js")),Promise.resolve().then(()=>require("./cortex-widget-registry.cjs.js"))]),n=await i(B(e,t)),a=await o(s,n.definition);return p(a),{definition:g(a),editUrl:`/cms/custom-blocks/${a.id}/edit`,mutationExecuted:!0,success:!0}}catch(s){return{mutationExecuted:!1,message:m(s),success:!1}}}async function O(e,t){try{const s=l(t);f(t);const{data:i}=await s.from("custom_block_definitions").select(k).eq("slug",e.slug).maybeSingle();if(!i)return{mutationExecuted:!1,message:`No custom block found with slug "${e.slug}".`,success:!1};const[{generateCortexWidgetDefinition:o},{customBlockDefinitionCreateSchema:n}]=await Promise.all([Promise.resolve().then(()=>require("./ai-cortex-widget-builder.cjs.js")),import("@nextblock-cms/utils/custom-blocks")]),a=await o(B({context:N(i),prompt:e.prompt},t)),c=n.parse({...a.definition,is_original:i.is_original,slug:i.slug}),{data:d,error:b}=await s.from("custom_block_definitions").update({description:c.description,fields:y(c.fields),layout_schema:y(c.layout_schema),name:c.name}).eq("id",i.id).select(k).single();return b||!d?{mutationExecuted:!1,message:b?.message??"Failed to update custom block.",success:!1}:(p(d),{definition:g(d),editUrl:`/cms/custom-blocks/${i.id}/edit`,mutationExecuted:!0,success:!0})}catch(s){return{mutationExecuted:!1,message:m(s),success:!1}}}async function T(e,t){try{const s=l(t);f(t);const{data:i}=await s.from("custom_block_definitions").select("id, slug, name").eq("slug",e.slug).maybeSingle();if(!i)return{mutationExecuted:!1,message:`No custom block found with slug "${e.slug}".`,success:!1};const o=P(i.slug);if(!M(t,o))return{confirmationPhrase:o,mutationExecuted:!1,preview:{summary:`Delete the custom block "${i.name}" (${i.slug}). Pages still using it will stop rendering it until replaced. This cannot be undone.`},requiresConfirmation:!0,success:!0};const{error:n}=await s.from("custom_block_definitions").delete().eq("id",i.id);return n?{mutationExecuted:!1,message:`Failed to delete custom block: ${n.message}`,success:!1}:(p(i),{deleted:{name:i.name,slug:i.slug},mutationExecuted:!0,success:!0})}catch(s){return{mutationExecuted:!1,message:m(s),success:!1}}}async function I(e,t){try{const s=l(t),{data:i,error:o}=await s.from("custom_block_definitions").select("id, slug, name, description, fields, is_original").order("name",{ascending:!0});if(o)return{message:o.message,success:!1};let n=Array.isArray(i)?i:[];if(e.query){const a=e.query.toLowerCase();n=n.filter(c=>String(c.name||"").toLowerCase().includes(a)||String(c.slug||"").toLowerCase().includes(a))}return{blocks:n.map(a=>g(a)),count:n.length,success:!0}}catch(s){return{message:m(s),success:!1}}}function j(e){return{create_custom_block:u.tool({description:'Create a brand-new reusable custom block definition from a natural-language description (for example "a product card with title, image, price, and a button linking to the product page"). This is a GLOBAL block-library builder and does NOT require an open page, post, or product. It generates the field schema and Tailwind layout and saves it so the block can be added to any page afterward. Additive and reversible; executes immediately without a confirmation phrase.',execute:t=>q(t,e),inputSchema:w,strict:!0}),delete_custom_block:u.tool({description:"Delete a custom block definition by slug. Mutating: first returns a confirmation phrase; only executes after the user replies with the exact phrase. Use list_custom_blocks first if you are unsure of the slug.",execute:t=>T(t,e),inputSchema:_,strict:!0}),list_custom_blocks:u.tool({description:"List the existing custom block definitions (slug, name, and fields). Read-only and does not require page context. Use it to find the slug of a block to edit or delete.",execute:t=>I(t,e),inputSchema:E,strict:!0}),update_custom_block:u.tool({description:"Edit an existing custom block definition (identified by slug) from a new natural-language description. The block is regenerated with its current definition as context and keeps its slug so existing placements keep working. Executes immediately.",execute:t=>O(t,e),inputSchema:S,strict:!0})}}exports.createCortexCustomBlockTools=j;exports.createCustomBlockInputSchema=w;exports.deleteCustomBlockInputSchema=_;exports.executeCreateCustomBlock=q;exports.executeDeleteCustomBlock=T;exports.executeListCustomBlocks=I;exports.executeUpdateCustomBlock=O;exports.listCustomBlocksInputSchema=E;exports.updateCustomBlockInputSchema=S;
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { tool as u } from "ai";
|
|
2
2
|
import "./zod-config.es.js";
|
|
3
|
-
import { z as
|
|
4
|
-
const h = "id, slug, name, description, fields, layout_schema, is_original",
|
|
5
|
-
context:
|
|
6
|
-
prompt:
|
|
3
|
+
import { z as a } from "zod";
|
|
4
|
+
const h = "id, slug, name, description, fields, layout_schema, is_original", w = a.strictObject({
|
|
5
|
+
context: a.string().trim().max(3e3).optional().describe("Optional extra constraints or brand/style guidance for the generated block."),
|
|
6
|
+
prompt: a.string().trim().min(3).max(4e3).describe(
|
|
7
7
|
'Natural-language description of the custom block, including the fields it needs and the visual style. Example: "A product card with a title, image, price, and a button that links to the product page."'
|
|
8
8
|
)
|
|
9
|
-
}),
|
|
10
|
-
prompt:
|
|
11
|
-
slug:
|
|
12
|
-
}),
|
|
13
|
-
slug:
|
|
14
|
-
}), S =
|
|
15
|
-
query:
|
|
9
|
+
}), _ = a.strictObject({
|
|
10
|
+
prompt: a.string().trim().min(3).max(4e3).describe("Description of the changes to apply. The block is regenerated using its existing definition as context."),
|
|
11
|
+
slug: a.string().trim().min(1).max(120).describe("Slug of the existing custom block to edit.")
|
|
12
|
+
}), E = a.strictObject({
|
|
13
|
+
slug: a.string().trim().min(1).max(120).describe("Slug of the custom block definition to delete.")
|
|
14
|
+
}), S = a.strictObject({
|
|
15
|
+
query: a.string().trim().max(120).optional().describe("Optional text filter on name or slug.")
|
|
16
16
|
});
|
|
17
17
|
function l(e) {
|
|
18
18
|
if (!e?.supabase)
|
|
19
19
|
throw new Error("A Supabase service client is required to manage custom block definitions.");
|
|
20
20
|
return e.supabase;
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function f(e) {
|
|
23
23
|
if (!e?.actorUserId)
|
|
24
24
|
throw new Error("Managing custom block definitions requires an authenticated admin actor.");
|
|
25
25
|
return e.actorUserId;
|
|
@@ -27,7 +27,7 @@ function g(e) {
|
|
|
27
27
|
function k(e) {
|
|
28
28
|
return JSON.parse(JSON.stringify(e));
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
function g(e) {
|
|
31
31
|
const t = Array.isArray(e?.fields) ? e.fields : [];
|
|
32
32
|
return {
|
|
33
33
|
fieldCount: t.length,
|
|
@@ -37,7 +37,19 @@ function f(e) {
|
|
|
37
37
|
slug: e?.slug
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
const y = 24e3;
|
|
41
|
+
function O(e) {
|
|
42
|
+
const t = `You are editing an existing custom block named "${e.name ?? "Untitled"}". Keep its overall purpose and only apply the requested changes.`, s = `${t} Existing definition: ${JSON.stringify({
|
|
43
|
+
fields: e.fields,
|
|
44
|
+
layout_schema: e.layout_schema,
|
|
45
|
+
name: e.name
|
|
46
|
+
})}`;
|
|
47
|
+
return s.length <= y ? s : `${t} Existing fields: ${JSON.stringify({
|
|
48
|
+
fields: e.fields,
|
|
49
|
+
name: e.name
|
|
50
|
+
})} (The existing layout was too large to include — rebuild a layout that renders these fields.)`.slice(0, y);
|
|
51
|
+
}
|
|
52
|
+
function C(e, t) {
|
|
41
53
|
const s = t?.cortexAiApiKey || void 0;
|
|
42
54
|
return {
|
|
43
55
|
apiKey: s,
|
|
@@ -53,16 +65,16 @@ function p(e) {
|
|
|
53
65
|
} catch {
|
|
54
66
|
}
|
|
55
67
|
}
|
|
56
|
-
function
|
|
68
|
+
function x(e) {
|
|
57
69
|
return (e || "").replace(/\s+/g, " ").trim().toUpperCase();
|
|
58
70
|
}
|
|
59
|
-
function
|
|
71
|
+
function T(e) {
|
|
60
72
|
return `CONFIRM DELETE CUSTOM BLOCK ${e.toUpperCase()}`;
|
|
61
73
|
}
|
|
62
74
|
function q(e, t) {
|
|
63
|
-
return e?.skipConfirmation ? !0 :
|
|
75
|
+
return e?.skipConfirmation ? !0 : x(e?.latestUserMessage).includes(x(t));
|
|
64
76
|
}
|
|
65
|
-
function
|
|
77
|
+
function d(e) {
|
|
66
78
|
if (e && typeof e == "object") {
|
|
67
79
|
const t = e;
|
|
68
80
|
if (t.code === "23505")
|
|
@@ -72,72 +84,70 @@ function m(e) {
|
|
|
72
84
|
}
|
|
73
85
|
return e instanceof Error ? e.message : "Unknown error.";
|
|
74
86
|
}
|
|
75
|
-
async function
|
|
87
|
+
async function A(e, t) {
|
|
76
88
|
try {
|
|
77
89
|
const s = l(t);
|
|
78
|
-
|
|
90
|
+
f(t);
|
|
79
91
|
const [{ generateCortexWidgetDefinition: i }, { insertCortexWidgetDefinition: r }] = await Promise.all([
|
|
80
92
|
import("./ai-cortex-widget-builder.es.js"),
|
|
81
93
|
import("./cortex-widget-registry.es.js")
|
|
82
|
-
]), o = await i(
|
|
83
|
-
return p(
|
|
84
|
-
definition:
|
|
85
|
-
editUrl: `/cms/custom-blocks/${
|
|
94
|
+
]), o = await i(C(e, t)), n = await r(s, o.definition);
|
|
95
|
+
return p(n), {
|
|
96
|
+
definition: g(n),
|
|
97
|
+
editUrl: `/cms/custom-blocks/${n.id}/edit`,
|
|
86
98
|
mutationExecuted: !0,
|
|
87
99
|
success: !0
|
|
88
100
|
};
|
|
89
101
|
} catch (s) {
|
|
90
|
-
return { mutationExecuted: !1, message:
|
|
102
|
+
return { mutationExecuted: !1, message: d(s), success: !1 };
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
|
-
async function
|
|
105
|
+
async function $(e, t) {
|
|
94
106
|
try {
|
|
95
107
|
const s = l(t);
|
|
96
|
-
|
|
108
|
+
f(t);
|
|
97
109
|
const { data: i } = await s.from("custom_block_definitions").select(h).eq("slug", e.slug).maybeSingle();
|
|
98
110
|
if (!i)
|
|
99
111
|
return { mutationExecuted: !1, message: `No custom block found with slug "${e.slug}".`, success: !1 };
|
|
100
112
|
const [{ generateCortexWidgetDefinition: r }, { customBlockDefinitionCreateSchema: o }] = await Promise.all([
|
|
101
113
|
import("./ai-cortex-widget-builder.es.js"),
|
|
102
114
|
import("@nextblock-cms/utils/custom-blocks")
|
|
103
|
-
]),
|
|
104
|
-
|
|
115
|
+
]), n = await r(
|
|
116
|
+
C(
|
|
105
117
|
{
|
|
106
|
-
context:
|
|
107
|
-
{ fields: i.fields, layout_schema: i.layout_schema, name: i.name }
|
|
108
|
-
)}`,
|
|
118
|
+
context: O(i),
|
|
109
119
|
prompt: e.prompt
|
|
110
120
|
},
|
|
111
121
|
t
|
|
112
122
|
)
|
|
113
123
|
), c = o.parse({
|
|
114
|
-
...
|
|
124
|
+
...n.definition,
|
|
115
125
|
is_original: i.is_original,
|
|
116
126
|
slug: i.slug
|
|
117
|
-
}), { data:
|
|
127
|
+
}), { data: m, error: b } = await s.from("custom_block_definitions").update({
|
|
118
128
|
description: c.description,
|
|
119
129
|
fields: k(c.fields),
|
|
120
130
|
layout_schema: k(c.layout_schema),
|
|
121
131
|
name: c.name
|
|
122
132
|
}).eq("id", i.id).select(h).single();
|
|
123
|
-
return b || !
|
|
124
|
-
definition:
|
|
133
|
+
return b || !m ? { mutationExecuted: !1, message: b?.message ?? "Failed to update custom block.", success: !1 } : (p(m), {
|
|
134
|
+
definition: g(m),
|
|
125
135
|
editUrl: `/cms/custom-blocks/${i.id}/edit`,
|
|
126
136
|
mutationExecuted: !0,
|
|
127
137
|
success: !0
|
|
128
138
|
});
|
|
129
139
|
} catch (s) {
|
|
130
|
-
return { mutationExecuted: !1, message:
|
|
140
|
+
return { mutationExecuted: !1, message: d(s), success: !1 };
|
|
131
141
|
}
|
|
132
142
|
}
|
|
133
143
|
async function B(e, t) {
|
|
134
144
|
try {
|
|
135
145
|
const s = l(t);
|
|
136
|
-
|
|
146
|
+
f(t);
|
|
137
147
|
const { data: i } = await s.from("custom_block_definitions").select("id, slug, name").eq("slug", e.slug).maybeSingle();
|
|
138
148
|
if (!i)
|
|
139
149
|
return { mutationExecuted: !1, message: `No custom block found with slug "${e.slug}".`, success: !1 };
|
|
140
|
-
const r =
|
|
150
|
+
const r = T(i.slug);
|
|
141
151
|
if (!q(t, r))
|
|
142
152
|
return {
|
|
143
153
|
confirmationPhrase: r,
|
|
@@ -155,66 +165,66 @@ async function B(e, t) {
|
|
|
155
165
|
success: !0
|
|
156
166
|
});
|
|
157
167
|
} catch (s) {
|
|
158
|
-
return { mutationExecuted: !1, message:
|
|
168
|
+
return { mutationExecuted: !1, message: d(s), success: !1 };
|
|
159
169
|
}
|
|
160
170
|
}
|
|
161
|
-
async function
|
|
171
|
+
async function D(e, t) {
|
|
162
172
|
try {
|
|
163
173
|
const s = l(t), { data: i, error: r } = await s.from("custom_block_definitions").select("id, slug, name, description, fields, is_original").order("name", { ascending: !0 });
|
|
164
174
|
if (r)
|
|
165
175
|
return { message: r.message, success: !1 };
|
|
166
176
|
let o = Array.isArray(i) ? i : [];
|
|
167
177
|
if (e.query) {
|
|
168
|
-
const
|
|
178
|
+
const n = e.query.toLowerCase();
|
|
169
179
|
o = o.filter(
|
|
170
|
-
(c) => String(c.name || "").toLowerCase().includes(
|
|
180
|
+
(c) => String(c.name || "").toLowerCase().includes(n) || String(c.slug || "").toLowerCase().includes(n)
|
|
171
181
|
);
|
|
172
182
|
}
|
|
173
183
|
return {
|
|
174
|
-
blocks: o.map((
|
|
184
|
+
blocks: o.map((n) => g(n)),
|
|
175
185
|
count: o.length,
|
|
176
186
|
success: !0
|
|
177
187
|
};
|
|
178
188
|
} catch (s) {
|
|
179
|
-
return { message:
|
|
189
|
+
return { message: d(s), success: !1 };
|
|
180
190
|
}
|
|
181
191
|
}
|
|
182
|
-
function
|
|
192
|
+
function N(e) {
|
|
183
193
|
return {
|
|
184
194
|
create_custom_block: u({
|
|
185
195
|
description: 'Create a brand-new reusable custom block definition from a natural-language description (for example "a product card with title, image, price, and a button linking to the product page"). This is a GLOBAL block-library builder and does NOT require an open page, post, or product. It generates the field schema and Tailwind layout and saves it so the block can be added to any page afterward. Additive and reversible; executes immediately without a confirmation phrase.',
|
|
186
|
-
execute: (t) =>
|
|
187
|
-
inputSchema:
|
|
196
|
+
execute: (t) => A(t, e),
|
|
197
|
+
inputSchema: w,
|
|
188
198
|
strict: !0
|
|
189
199
|
}),
|
|
190
200
|
delete_custom_block: u({
|
|
191
201
|
description: "Delete a custom block definition by slug. Mutating: first returns a confirmation phrase; only executes after the user replies with the exact phrase. Use list_custom_blocks first if you are unsure of the slug.",
|
|
192
202
|
execute: (t) => B(t, e),
|
|
193
|
-
inputSchema:
|
|
203
|
+
inputSchema: E,
|
|
194
204
|
strict: !0
|
|
195
205
|
}),
|
|
196
206
|
list_custom_blocks: u({
|
|
197
207
|
description: "List the existing custom block definitions (slug, name, and fields). Read-only and does not require page context. Use it to find the slug of a block to edit or delete.",
|
|
198
|
-
execute: (t) =>
|
|
208
|
+
execute: (t) => D(t, e),
|
|
199
209
|
inputSchema: S,
|
|
200
210
|
strict: !0
|
|
201
211
|
}),
|
|
202
212
|
update_custom_block: u({
|
|
203
213
|
description: "Edit an existing custom block definition (identified by slug) from a new natural-language description. The block is regenerated with its current definition as context and keeps its slug so existing placements keep working. Executes immediately.",
|
|
204
|
-
execute: (t) =>
|
|
205
|
-
inputSchema:
|
|
214
|
+
execute: (t) => $(t, e),
|
|
215
|
+
inputSchema: _,
|
|
206
216
|
strict: !0
|
|
207
217
|
})
|
|
208
218
|
};
|
|
209
219
|
}
|
|
210
220
|
export {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
221
|
+
N as createCortexCustomBlockTools,
|
|
222
|
+
w as createCustomBlockInputSchema,
|
|
223
|
+
E as deleteCustomBlockInputSchema,
|
|
224
|
+
A as executeCreateCustomBlock,
|
|
215
225
|
B as executeDeleteCustomBlock,
|
|
216
|
-
|
|
217
|
-
|
|
226
|
+
D as executeListCustomBlocks,
|
|
227
|
+
$ as executeUpdateCustomBlock,
|
|
218
228
|
S as listCustomBlocksInputSchema,
|
|
219
|
-
|
|
229
|
+
_ as updateCustomBlockInputSchema
|
|
220
230
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("ai");require("./zod-config.cjs.js");const n=require("zod"),G=100,W=20,k=100,z=20,R="[REDACTED]",g="cortex_ai_openrouter_api_key",$={blocks:{columns:["id","page_id","post_id","language_id","block_type","content","order","created_at","updated_at"],description:"CMS page/post content blocks.",primaryKey:["id"]},coupon_freemius_mappings:{columns:["id","coupon_id","product_id","freemius_product_id","freemius_coupon_id","freemius_coupon_code","sync_status","sync_error","remote_payload","last_synced_at","created_at","updated_at"],description:"Freemius coupon sync mappings.",primaryKey:["id"]},coupon_products:{columns:["coupon_id","product_id","created_at"],description:"Coupon product allow-list rows.",primaryKey:["coupon_id","product_id"]},coupon_redemptions:{columns:["id","coupon_id","order_id","coupon_code","provider","discount_total","user_id","customer_email","metadata","redeemed_at"],description:"Coupon redemption records.",primaryKey:["id"]},coupons:{columns:["id","code","name","internal_note","provider_scope","discount_type","discount_amount","is_active","starts_at","ends_at","redemption_limit","redemptions_count","freemius_sync_status","freemius_sync_error","created_at","updated_at"],description:"Commerce coupons.",primaryKey:["id"]},cortex_ai_db_mutation_audit:{columns:["id","actor_user_id","tool_name","action_name","target_tables","operation_summary","payload_hash","payload","preview","status","error_message","created_at"],description:"Read-only Cortex AI database mutation audit trail.",primaryKey:["id"],readOnly:!0},currencies:{columns:["id","code","symbol","exchange_rate","is_default","is_active","rounding_mode","rounding_increment","rounding_charm_amount","auto_update_exchange_rate","exchange_rate_updated_at","exchange_rate_source","auto_sync_product_prices","created_at","updated_at"],description:"Storefront currencies and FX settings.",primaryKey:["id"]},freemius_plans:{columns:["id","product_id","name","title","created_at","updated_at"],description:"Freemius plan metadata.",primaryKey:["id"]},freemius_pricing:{columns:["id","plan_id","api_monthly_price","api_annual_price","api_lifetime_price","override_monthly_price","override_annual_price","override_lifetime_price","license_quota","is_active","created_at","updated_at"],description:"Freemius pricing metadata.",primaryKey:["id"]},inventory_items:{columns:["sku","quantity","created_at","updated_at"],description:"Source-of-truth inventory by SKU.",primaryKey:["sku"]},languages:{columns:["id","code","name","is_default","is_active","created_at","updated_at"],description:"CMS languages/locales.",primaryKey:["id"]},logos:{columns:["id","name","media_id","created_at"],description:"Brand logos.",primaryKey:["id"]},media:{columns:["id","uploader_id","file_name","object_key","file_type","size_bytes","description","width","height","blur_data_url","variants","file_path","folder","created_at","updated_at"],description:"Media metadata for uploaded assets.",primaryKey:["id"]},navigation_items:{columns:["id","language_id","menu_key","label","url","parent_id","order","page_id","translation_group_id","created_at","updated_at"],description:"CMS navigation tree items.",primaryKey:["id"]},order_items:{columns:["id","order_id","product_id","variant_id","quantity","price_at_purchase"],description:"Commerce order line items.",primaryKey:["id"]},orders:{columns:["id","user_id","status","total","stripe_session_id","payment_intent_id","customer_details","provider","freemius_product_id","freemius_plan_id","freemius_license_id","freemius_subscription_id","freemius_trial_id","freemius_user_id","freemius_trial_ends_at","freemius_last_event_type","freemius_last_synced_at","currency","subtotal","shipping_total","tax_total","tax_details","exchange_rate_at_purchase","inventory_deducted_at","invoice_number","paid_at","created_at","coupon_id","coupon_code","discount_total","discount_details"],description:"Commerce orders.",primaryKey:["id"]},package_activations:{columns:["id","license_key","instance_name","package_id","status","meta","last_validated_at","created_at"],description:"NextBlock package activations.",primaryKey:["id"]},page_revisions:{columns:["id","page_id","author_id","version","revision_type","content","created_at"],description:"Page revision history.",primaryKey:["id"],readOnly:!0},pages:{columns:["id","language_id","author_id","title","slug","status","meta_title","meta_description","feature_image_id","version","translation_group_id","created_at","updated_at"],description:"CMS pages.",primaryKey:["id"]},post_revisions:{columns:["id","post_id","author_id","version","revision_type","content","created_at"],description:"Post revision history.",primaryKey:["id"],readOnly:!0},product_revisions:{columns:["id","product_id","author_id","version","revision_type","content","created_at"],description:"Product revision history.",primaryKey:["id"],readOnly:!0},posts:{columns:["id","language_id","author_id","title","slug","label","excerpt","subtitle","status","published_at","meta_title","meta_description","feature_image_id","version","translation_group_id","created_at","updated_at"],description:"CMS posts/articles.",primaryKey:["id"]},product_attribute_terms:{columns:["id","attribute_id","value","slug","sort_order","value_translations","created_at","updated_at"],description:"Product attribute terms.",primaryKey:["id"]},product_attributes:{columns:["id","name","slug","name_translations","created_at","updated_at"],description:"Product attribute definitions.",primaryKey:["id"]},product_media:{columns:["product_id","media_id","sort_order"],description:"Product media join rows.",primaryKey:["product_id","media_id"]},product_variants:{columns:["id","product_id","sku","price_adjustment","price","prices","sale_price","sale_prices","stock_quantity","upc","main_media_id","created_at","updated_at"],description:"Sellable product variants.",primaryKey:["id"]},products:{columns:["id","language_id","translation_group_id","sku","title","slug","product_type","payment_provider","price","prices","sale_price","sale_prices","stock","status","meta_title","meta_description","short_description","description_json","metadata","freemius_plan_id","freemius_product_id","trial_period_days","trial_requires_payment_method","upc","is_taxable","created_at","updated_at"],description:"Product catalog records.",primaryKey:["id"]},profiles:{columns:["id","updated_at","full_name","avatar_url","website","phone","role"],description:"Read-only user profiles. Never mutate through Cortex AI.",primaryKey:["id"],readOnly:!0},shipping_zone_locations:{columns:["id","zone_id","country_code","state_code","postal_code","created_at"],description:"Shipping zone location match rules.",primaryKey:["id"]},shipping_zone_methods:{columns:["id","zone_id","method_type","cost_amount","cost_currency","min_order_amount","name","name_translations","currency_pricing_mode","cost_amounts","min_order_amounts","created_at","updated_at"],description:"Shipping methods/rates.",primaryKey:["id"]},shipping_zones:{columns:["id","name","priority_order","created_at","updated_at"],description:"Shipping zones.",primaryKey:["id"]},site_settings:{columns:["key","value"],description:"Global site settings. Cortex AI API-key setting row is protected.",primaryKey:["key"]},tax_rates:{columns:["id","country_code","state_code","tax_name","tax_rate","created_at","updated_at"],description:"Manual tax rates.",primaryKey:["id"]},translations:{columns:["key","translations","created_at","updated_at"],description:"Shared translation strings.",primaryKey:["key"]},user_addresses:{columns:["id","user_id","address_type","is_default","recipient_name","company_name","line1","line2","city","state","postal_code","country_code","created_at","updated_at"],description:"Read-only user addresses. Never mutate through Cortex AI.",primaryKey:["id"],readOnly:!0},variant_attribute_mapping:{columns:["variant_id","attribute_term_id"],description:"Variant to attribute-term join rows.",primaryKey:["variant_id","attribute_term_id"]}},Y=n.z.enum(["eq","neq","gt","gte","lt","lte","ilike","in","is"]),N=n.z.strictObject({column:n.z.string().trim().min(1).max(120),operator:Y.default("eq"),value:n.z.unknown()}),H=n.z.strictObject({ascending:n.z.boolean().default(!0),column:n.z.string().trim().min(1).max(120)}),j=n.z.strictObject({includeReadOnly:n.z.boolean().default(!0),table:n.z.string().trim().min(1).max(120).optional()}),T=n.z.strictObject({columns:n.z.array(n.z.string().trim().min(1).max(120)).min(1).max(40).optional(),filters:n.z.array(N).max(12).default([]),limit:n.z.number().int().min(1).max(G).default(W),offset:n.z.number().int().min(0).max(1e4).default(0),orderBy:H.optional(),table:n.z.string().trim().min(1).max(120)}),S=n.z.strictObject({filters:n.z.array(N).max(12).optional(),operation:n.z.enum(["delete","insert","update","upsert"]),rows:n.z.array(n.z.record(n.z.string(),n.z.unknown())).min(1).max(z).optional(),summary:n.z.string().trim().min(1).max(500).optional(),table:n.z.string().trim().min(1).max(120),values:n.z.record(n.z.string(),n.z.unknown()).optional()}),D=n.z.strictObject({actions:n.z.array(S).min(1).max(8),summary:n.z.string().trim().min(1).max(500).optional()});function E(e){if(!e?.supabase)throw new Error("Cortex AI database tools require a Supabase client.");return e.supabase}function f(e){return e?typeof e=="object"&&"message"in e?String(e.message||"Unknown database error."):String(e):"Unknown database error."}function K(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(t=>K(t)).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${K(e[t])}`).join(",")}}`}function L(e){let t=2166136261;const r=K(e);for(let a=0;a<r.length;a++)t^=r.charCodeAt(a),t=Math.imul(t,16777619);return(t>>>0).toString(16).padStart(8,"0")}function I(e){return e.replace(/\s+/g," ").trim().toUpperCase()}function Z(e,t,r){return`${I(`CONFIRM ${e} ${t}`)} #${L(r)}`}function ee(e){return{confirmationPhrase:Z(e.action,e.subject,e.payload),mutationExecuted:!1,preview:e.preview,requiresConfirmation:!0,success:!0}}function q(e){if(e.context?.skipConfirmation)return null;const t=ee(e),r=I(e.context?.latestUserMessage||""),a=I(t.confirmationPhrase);return r&&!r.includes(a)?{...t,message:"The database target changed or the confirmation did not match. Please review and confirm again."}:r.includes(a)?null:t}function te(e){return Object.prototype.hasOwnProperty.call($,e)}function M(e){const t=e.trim();if(t.includes(".")||t.toLowerCase().startsWith("auth"))throw new Error("Cortex AI cannot access auth schema tables.");if(!te(t))throw new Error(`Table "${t}" is not available to Cortex AI database tools.`);return t}function _(e){return $[e]}function w(e){return/(?:password|token|secret|api[_-]?key|private[_-]?key|credential)/i.test(e)}function b(e,t){if(!_(e).columns.includes(t))throw new Error(`Column "${t}" is not available on table "${e}".`);if(w(t))throw new Error(`Column "${t}" is protected and cannot be used through Cortex AI.`)}function O(e,t="payload"){if(Array.isArray(e)){e.forEach((r,a)=>O(r,`${t}[${a}]`));return}if(!(!e||typeof e!="object"))for(const[r,a]of Object.entries(e)){if(w(r))throw new Error(`Protected field "${t}.${r}" cannot be written through Cortex AI.`);O(a,`${t}.${r}`)}}function h(e){if(Array.isArray(e))return e.map(r=>h(r));if(!e||typeof e!="object")return e;const t={};for(const[r,a]of Object.entries(e))t[r]=w(r)?R:h(a);return t}function re(e){return e.map(t=>t&&typeof t=="object"&&!Array.isArray(t)&&t.key===g?{key:g,value:R}:h(t))}function ae(e,t){if(e!=="site_settings")return;if(JSON.stringify(t).toLowerCase().includes(g))throw new Error("The Cortex AI OpenRouter API key site setting is protected.")}function ie(e,t){switch(t.operator){case"eq":return e.eq(t.column,t.value);case"neq":return e.neq(t.column,t.value);case"gt":return e.gt(t.column,t.value);case"gte":return e.gte(t.column,t.value);case"lt":return e.lt(t.column,t.value);case"lte":return e.lte(t.column,t.value);case"ilike":if(typeof t.value!="string")throw new Error(`Filter "${t.column}" with ilike requires a string value.`);return e.ilike(t.column,t.value);case"in":if(!Array.isArray(t.value))throw new Error(`Filter "${t.column}" with in requires an array value.`);return e.in(t.column,t.value);case"is":return e.is(t.column,t.value)}}function x(e,t,r){let a=t;for(const o of r)b(e,o.column),a=ie(a,o);return e==="site_settings"&&(a=a.neq("key",g)),a}function oe(e,t){const r=_(e),a=t?.length?t:r.columns.filter(o=>!w(o));return a.forEach(o=>b(e,o)),a.join(",")}function F(e,t){return t.map(r=>`${r}=${String(e[r]??"")}`).join("|")}async function U(e,t,r){const a=_(t);let o=e.from(t).select(a.primaryKey.join(",")).limit(k+1);o=x(t,o,r);const{data:i,error:s}=await o;if(s)throw new Error(`Failed to preview database mutation: ${f(s)}`);const d=Array.isArray(i)?i:[];if(d.length>k)throw new Error(`Refusing to mutate more than ${k} rows.`);return d}function B(e){const t=M(e.table),r=_(t),a=e.operation;if(r.readOnly)throw new Error(`Table "${t}" is read-only for Cortex AI.`);if(ae(t,e),(a==="update"||a==="delete")&&(!e.filters||e.filters.length===0))throw new Error(`${a} operations require at least one filter.`);if((a==="insert"||a==="upsert")&&(!e.rows||e.rows.length===0))throw new Error(`${a} operations require rows.`);if(a==="update"&&(!e.values||Object.keys(e.values).length===0))throw new Error("update operations require values.");if(e.rows&&e.rows.length>z)throw new Error(`Refusing to mutate more than ${z} input rows.`);const o=e.rows??(e.values?[e.values]:[]);for(const i of o){O(i);for(const s of Object.keys(i))b(t,s)}for(const i of e.filters??[])b(t,i.column);return t}function X(e){const{input:t,table:r,targetRows:a}=e,o=_(r),i=a.map(m=>F(m,o.primaryKey)).sort(),s=t.rows?.length??0,d=t.operation==="insert"||t.operation==="upsert"?s:a.length,l=t.summary||`${t.operation} ${d} ${d===1?"row":"rows"} in ${r}.`;return{affectedCount:d,operation:t.operation,sampleTargetIds:i.slice(0,10),summary:l,table:r,targetIds:i}}async function A(e){const t=e.context?.actorUserId;if(!t)return{auditError:"Missing actor user id.",auditLogged:!1};try{const{error:r}=await E(e.context).from("cortex_ai_db_mutation_audit").insert({action_name:e.toolName,actor_user_id:t,error_message:e.errorMessage??null,operation_summary:e.summary,payload:h(e.input),payload_hash:L(e.input),preview:h(e.preview),status:e.status,target_tables:e.targetTables,tool_name:e.toolName});return r?{auditError:f(r),auditLogged:!1}:{auditLogged:!0}}catch(r){return{auditError:f(r),auditLogged:!1}}}async function ne(e,t,r){const o=_(t).primaryKey.join(",");let i;e.operation==="insert"?i=r.from(t).insert(e.rows).select(o):e.operation==="upsert"?i=r.from(t).upsert(e.rows).select(o):e.operation==="update"?(i=r.from(t).update(e.values),i=x(t,i,e.filters??[]),i=i.select(o)):(i=r.from(t).delete(),i=x(t,i,e.filters??[]),i=i.select(o));const{data:s,error:d}=await i;if(d)throw new Error(f(d));return Array.isArray(s)?s:[]}async function V(e){const t=j.parse(e),a=(t.table?[M(t.table)]:Object.keys($)).filter(o=>t.includeReadOnly||!_(o).readOnly).map(o=>{const i=_(o);return{columns:i.columns.filter(s=>!w(s)),description:i.description,primaryKey:i.primaryKey,readOnly:!!i.readOnly,table:o}});return{safetyNotes:["Use read_database_records for reads and execute_database_mutation/action_plan for writes.","No auth schema access, no arbitrary SQL, no password/secret/API-key fields.","profiles, user_addresses, and cortex_ai_db_mutation_audit are read-only.",`site_settings.${g} is protected.`],success:!0,tables:a}}async function J(e,t){const r=T.parse(e),a=M(r.table),o=oe(a,r.columns);let i=E(t).from(a).select(o).range(r.offset,r.offset+r.limit-1);i=x(a,i,r.filters),r.orderBy&&(b(a,r.orderBy.column),i=i.order(r.orderBy.column,{ascending:r.orderBy.ascending}));const{data:s,error:d}=await i;if(d)throw new Error(`Failed to read database records: ${f(d)}`);const l=Array.isArray(s)?s:[];return{columns:o.split(","),limit:r.limit,offset:r.offset,rows:re(l),success:!0,table:a}}async function P(e,t){const r=S.parse(e),a=B(r),o=E(t),i=r.operation==="update"||r.operation==="delete"?await U(o,a,r.filters??[]):[],s=X({input:r,table:a,targetRows:i}),d={input:r,targetIds:s.targetIds},l=q({action:`DATABASE ${r.operation}`,context:t,payload:d,preview:s,subject:a});if(l)return l;try{const m=await ne(r,a,o),p=t?.skipAudit?{auditLogged:!1}:await A({context:t,input:d,preview:s,status:"success",summary:String(s.summary),targetTables:[a],toolName:"execute_database_mutation"});return{affectedCount:m.length||s.affectedCount,auditLogged:p.auditLogged,...p.auditError?{auditError:p.auditError}:{},mutationExecuted:!0,operation:r.operation,sampleTargetIds:m.map(c=>F(c,_(a).primaryKey)).slice(0,10),success:!0,summary:s.summary,table:a}}catch(m){const p=f(m),c=t?.skipAudit?{auditLogged:!1}:await A({context:t,errorMessage:p,input:d,preview:s,status:"failure",summary:String(s.summary),targetTables:[a],toolName:"execute_database_mutation"});return{auditLogged:c.auditLogged,...c.auditError?{auditError:c.auditError}:{},message:`Database mutation failed: ${p}`,mutationExecuted:!1,operation:r.operation,success:!1,table:a}}}async function Q(e,t){const r=D.parse(e),a=[];for(const c of r.actions){const y=B(c),u=c.operation==="update"||c.operation==="delete"?await U(E(t),y,c.filters??[]):[];a.push({input:c,preview:X({input:c,table:y,targetRows:u}),table:y})}const o=a.map(({preview:c})=>String(c.summary)),i={actionCount:a.length,actionSummaries:o,summary:r.summary||`Run ${a.length} confirmed database actions.`,tables:Array.from(new Set(a.map(c=>c.table)))},s=a.map(({input:c,preview:y,table:u})=>({input:c,table:u,targetIds:y.targetIds})),d=q({action:"DATABASE ACTION PLAN",context:t,payload:s,preview:i,subject:`${a.length} actions`});if(d)return d;const l=[];let m=!1;for(const[c,y]of a.entries()){const u=await P(y.input,{...t,skipAudit:!0,skipConfirmation:!0});if(l.push(u),u&&typeof u=="object"&&u.mutationExecuted===!0&&(m=!0),!u||typeof u!="object"||u.success===!1){const C=await A({context:t,errorMessage:u&&typeof u=="object"&&typeof u.message=="string"?u.message:`Database action ${c+1} failed.`,input:s,preview:i,status:"failure",summary:String(i.summary),targetTables:i.tables,toolName:"execute_database_action_plan"});return{actionCount:a.length,auditLogged:C.auditLogged,...C.auditError?{auditError:C.auditError}:{},failedActionIndex:c,message:u&&typeof u=="object"&&typeof u.message=="string"?u.message:`Database action ${c+1} failed.`,mutationExecuted:m,results:l,success:!1}}}const p=await A({context:t,input:s,preview:i,status:"success",summary:String(i.summary),targetTables:i.tables,toolName:"execute_database_action_plan"});return{actionCount:a.length,auditLogged:p.auditLogged,...p.auditError?{auditError:p.auditError}:{},mutationExecuted:m,results:l,success:!0,summary:r.summary??null}}function se(e){return{describe_database_schema:v.tool({description:"Describe the public database tables Cortex AI can read or mutate, including read-only tables, primary keys, and writable columns. Use before broad database tasks when unsure about schema.",execute:t=>V(t),inputSchema:j,strict:!0}),execute_database_action_plan:v.tool({description:"Execute up to 8 typed database mutations as one confirmed plan. Mutating: first returns one combined confirmation preview and Confirm button; after confirmation, runs each action in order and stops on the first failure.",execute:t=>Q(t,e),inputSchema:D,strict:!0}),execute_database_mutation:v.tool({description:"Run one typed insert, update, upsert, or delete against an allowed public database table. Mutating: always previews affected rows and requires exact user confirmation before execution. Does not support arbitrary SQL.",execute:t=>P(t,e),inputSchema:S,strict:!0}),read_database_records:v.tool({description:"Read records from an allowed public database table with validated columns, filters, ordering, limit, and offset. Read-only and redacts protected secret-like values.",execute:t=>J(t,e),inputSchema:T,strict:!0})}}exports.createCortexDatabaseAgentTools=se;exports.describeDatabaseSchemaInputSchema=j;exports.executeDatabaseActionPlan=Q;exports.executeDatabaseActionPlanInputSchema=D;exports.executeDatabaseMutation=P;exports.executeDatabaseMutationInputSchema=S;exports.executeDescribeDatabaseSchema=V;exports.executeReadDatabaseRecords=J;exports.readDatabaseRecordsInputSchema=T;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("ai");require("./zod-config.cjs.js");const n=require("zod"),W=100,Y=20,k=100,C=20,R="[REDACTED]",N="cortex_ai_openrouter_api_key",T=new Set([N,"bot_protection_secret","cortex_ai_pexels_api_key","cortex_ai_unsplash_access_key","email_secret","payment_secret"]),$={blocks:{columns:["id","page_id","post_id","language_id","block_type","content","order","created_at","updated_at"],description:"CMS page/post content blocks.",primaryKey:["id"]},coupon_freemius_mappings:{columns:["id","coupon_id","product_id","freemius_product_id","freemius_coupon_id","freemius_coupon_code","sync_status","sync_error","remote_payload","last_synced_at","created_at","updated_at"],description:"Freemius coupon sync mappings.",primaryKey:["id"]},coupon_products:{columns:["coupon_id","product_id","created_at"],description:"Coupon product allow-list rows.",primaryKey:["coupon_id","product_id"]},coupon_redemptions:{columns:["id","coupon_id","order_id","coupon_code","provider","discount_total","user_id","customer_email","metadata","redeemed_at"],description:"Coupon redemption records.",primaryKey:["id"]},coupons:{columns:["id","code","name","internal_note","provider_scope","discount_type","discount_amount","is_active","starts_at","ends_at","redemption_limit","redemptions_count","freemius_sync_status","freemius_sync_error","created_at","updated_at"],description:"Commerce coupons.",primaryKey:["id"]},cortex_ai_db_mutation_audit:{columns:["id","actor_user_id","tool_name","action_name","target_tables","operation_summary","payload_hash","payload","preview","status","error_message","created_at"],description:"Read-only Cortex AI database mutation audit trail.",primaryKey:["id"],readOnly:!0},currencies:{columns:["id","code","symbol","exchange_rate","is_default","is_active","rounding_mode","rounding_increment","rounding_charm_amount","auto_update_exchange_rate","exchange_rate_updated_at","exchange_rate_source","auto_sync_product_prices","created_at","updated_at"],description:"Storefront currencies and FX settings.",primaryKey:["id"]},freemius_plans:{columns:["id","product_id","name","title","created_at","updated_at"],description:"Freemius plan metadata.",primaryKey:["id"]},freemius_pricing:{columns:["id","plan_id","api_monthly_price","api_annual_price","api_lifetime_price","override_monthly_price","override_annual_price","override_lifetime_price","license_quota","is_active","created_at","updated_at"],description:"Freemius pricing metadata.",primaryKey:["id"]},inventory_items:{columns:["sku","quantity","created_at","updated_at"],description:"Source-of-truth inventory by SKU.",primaryKey:["sku"]},languages:{columns:["id","code","name","is_default","is_active","created_at","updated_at"],description:"CMS languages/locales.",primaryKey:["id"]},logos:{columns:["id","name","media_id","created_at"],description:"Brand logos.",primaryKey:["id"]},media:{columns:["id","uploader_id","file_name","object_key","file_type","size_bytes","description","width","height","blur_data_url","variants","file_path","folder","created_at","updated_at"],description:"Media metadata for uploaded assets.",primaryKey:["id"]},navigation_items:{columns:["id","language_id","menu_key","label","url","parent_id","order","page_id","translation_group_id","created_at","updated_at"],description:"CMS navigation tree items.",primaryKey:["id"]},order_items:{columns:["id","order_id","product_id","variant_id","quantity","price_at_purchase"],description:"Commerce order line items.",primaryKey:["id"]},orders:{columns:["id","user_id","status","total","stripe_session_id","payment_intent_id","customer_details","provider","freemius_product_id","freemius_plan_id","freemius_license_id","freemius_subscription_id","freemius_trial_id","freemius_user_id","freemius_trial_ends_at","freemius_last_event_type","freemius_last_synced_at","currency","subtotal","shipping_total","tax_total","tax_details","exchange_rate_at_purchase","inventory_deducted_at","invoice_number","paid_at","created_at","coupon_id","coupon_code","discount_total","discount_details"],description:"Commerce orders.",primaryKey:["id"]},package_activations:{columns:["id","license_key","instance_name","package_id","status","meta","last_validated_at","created_at"],description:"NextBlock package activations.",primaryKey:["id"]},page_revisions:{columns:["id","page_id","author_id","version","revision_type","content","created_at"],description:"Page revision history.",primaryKey:["id"],readOnly:!0},pages:{columns:["id","language_id","author_id","title","slug","status","meta_title","meta_description","feature_image_id","version","translation_group_id","created_at","updated_at"],description:"CMS pages.",primaryKey:["id"]},post_revisions:{columns:["id","post_id","author_id","version","revision_type","content","created_at"],description:"Post revision history.",primaryKey:["id"],readOnly:!0},product_revisions:{columns:["id","product_id","author_id","version","revision_type","content","created_at"],description:"Product revision history.",primaryKey:["id"],readOnly:!0},posts:{columns:["id","language_id","author_id","title","slug","label","excerpt","subtitle","status","published_at","meta_title","meta_description","feature_image_id","version","translation_group_id","created_at","updated_at"],description:"CMS posts/articles.",primaryKey:["id"]},product_attribute_terms:{columns:["id","attribute_id","value","slug","sort_order","value_translations","created_at","updated_at"],description:"Product attribute terms.",primaryKey:["id"]},product_attributes:{columns:["id","name","slug","name_translations","created_at","updated_at"],description:"Product attribute definitions.",primaryKey:["id"]},product_media:{columns:["product_id","media_id","sort_order"],description:"Product media join rows.",primaryKey:["product_id","media_id"]},product_variants:{columns:["id","product_id","sku","price_adjustment","price","prices","sale_price","sale_prices","stock_quantity","upc","main_media_id","created_at","updated_at"],description:"Sellable product variants.",primaryKey:["id"]},products:{columns:["id","language_id","translation_group_id","sku","title","slug","product_type","payment_provider","price","prices","sale_price","sale_prices","stock","status","meta_title","meta_description","short_description","description_json","metadata","freemius_plan_id","freemius_product_id","trial_period_days","trial_requires_payment_method","upc","is_taxable","created_at","updated_at"],description:"Product catalog records.",primaryKey:["id"]},profiles:{columns:["id","updated_at","full_name","avatar_url","website","phone","role"],description:"Read-only user profiles. Never mutate through Cortex AI.",primaryKey:["id"],readOnly:!0},shipping_zone_locations:{columns:["id","zone_id","country_code","state_code","postal_code","created_at"],description:"Shipping zone location match rules.",primaryKey:["id"]},shipping_zone_methods:{columns:["id","zone_id","method_type","cost_amount","cost_currency","min_order_amount","name","name_translations","currency_pricing_mode","cost_amounts","min_order_amounts","created_at","updated_at"],description:"Shipping methods/rates.",primaryKey:["id"]},shipping_zones:{columns:["id","name","priority_order","created_at","updated_at"],description:"Shipping zones.",primaryKey:["id"]},site_settings:{columns:["key","value"],description:"Global site settings. Cortex AI API-key setting row is protected.",primaryKey:["key"]},tax_rates:{columns:["id","country_code","state_code","tax_name","tax_rate","created_at","updated_at"],description:"Manual tax rates.",primaryKey:["id"]},translations:{columns:["key","translations","created_at","updated_at"],description:"Shared translation strings.",primaryKey:["key"]},user_addresses:{columns:["id","user_id","address_type","is_default","recipient_name","company_name","line1","line2","city","state","postal_code","country_code","created_at","updated_at"],description:"Read-only user addresses. Never mutate through Cortex AI.",primaryKey:["id"],readOnly:!0},variant_attribute_mapping:{columns:["variant_id","attribute_term_id"],description:"Variant to attribute-term join rows.",primaryKey:["variant_id","attribute_term_id"]}},H=n.z.enum(["eq","neq","gt","gte","lt","lte","ilike","in","is"]),L=n.z.strictObject({column:n.z.string().trim().min(1).max(120),operator:H.default("eq"),value:n.z.unknown()}),Z=n.z.strictObject({ascending:n.z.boolean().default(!0),column:n.z.string().trim().min(1).max(120)}),O=n.z.strictObject({includeReadOnly:n.z.boolean().default(!0),table:n.z.string().trim().min(1).max(120).optional()}),j=n.z.strictObject({columns:n.z.array(n.z.string().trim().min(1).max(120)).min(1).max(40).optional(),filters:n.z.array(L).max(12).default([]),limit:n.z.number().int().min(1).max(W).default(Y),offset:n.z.number().int().min(0).max(1e4).default(0),orderBy:Z.optional(),table:n.z.string().trim().min(1).max(120)}),S=n.z.strictObject({filters:n.z.array(L).max(12).optional(),operation:n.z.enum(["delete","insert","update","upsert"]),rows:n.z.array(n.z.record(n.z.string(),n.z.unknown())).min(1).max(C).optional(),summary:n.z.string().trim().min(1).max(500).optional(),table:n.z.string().trim().min(1).max(120),values:n.z.record(n.z.string(),n.z.unknown()).optional()}),D=n.z.strictObject({actions:n.z.array(S).min(1).max(8),summary:n.z.string().trim().min(1).max(500).optional()});function A(e){if(!e?.supabase)throw new Error("Cortex AI database tools require a Supabase client.");return e.supabase}function f(e){return e?typeof e=="object"&&"message"in e?String(e.message||"Unknown database error."):String(e):"Unknown database error."}function K(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(t=>K(t)).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${K(e[t])}`).join(",")}}`}function q(e){let t=2166136261;const r=K(e);for(let a=0;a<r.length;a++)t^=r.charCodeAt(a),t=Math.imul(t,16777619);return(t>>>0).toString(16).padStart(8,"0")}function z(e){return e.replace(/\s+/g," ").trim().toUpperCase()}function ee(e,t,r){return`${z(`CONFIRM ${e} ${t}`)} #${q(r)}`}function te(e){return{confirmationPhrase:ee(e.action,e.subject,e.payload),mutationExecuted:!1,preview:e.preview,requiresConfirmation:!0,success:!0}}function F(e){if(e.context?.skipConfirmation)return null;const t=te(e),r=z(e.context?.latestUserMessage||""),a=z(t.confirmationPhrase);return r&&!r.includes(a)?{...t,message:"The database target changed or the confirmation did not match. Please review and confirm again."}:r.includes(a)?null:t}function re(e){return Object.prototype.hasOwnProperty.call($,e)}function P(e){const t=e.trim();if(t.includes(".")||t.toLowerCase().startsWith("auth"))throw new Error("Cortex AI cannot access auth schema tables.");if(!re(t))throw new Error(`Table "${t}" is not available to Cortex AI database tools.`);return t}function _(e){return $[e]}function h(e){return/(?:password|token|secret|api[_-]?key|private[_-]?key|credential)/i.test(e)}function g(e,t){if(!_(e).columns.includes(t))throw new Error(`Column "${t}" is not available on table "${e}".`);if(h(t))throw new Error(`Column "${t}" is protected and cannot be used through Cortex AI.`)}function I(e,t="payload"){if(Array.isArray(e)){e.forEach((r,a)=>I(r,`${t}[${a}]`));return}if(!(!e||typeof e!="object"))for(const[r,a]of Object.entries(e)){if(h(r))throw new Error(`Protected field "${t}.${r}" cannot be written through Cortex AI.`);I(a,`${t}.${r}`)}}function b(e){if(Array.isArray(e))return e.map(r=>b(r));if(!e||typeof e!="object")return e;const t={};for(const[r,a]of Object.entries(e))t[r]=h(r)?R:b(a);return t}function ae(e){return e.map(t=>{if(t&&typeof t=="object"&&!Array.isArray(t)){const r=t.key;if(typeof r=="string"&&T.has(r))return{key:r,value:R}}return b(t)})}function ie(e,t){if(e!=="site_settings")return;const r=JSON.stringify(t).toLowerCase();for(const a of T)if(r.includes(a))throw new Error(`The "${a}" site setting is protected.`)}function oe(e,t){switch(t.operator){case"eq":return e.eq(t.column,t.value);case"neq":return e.neq(t.column,t.value);case"gt":return e.gt(t.column,t.value);case"gte":return e.gte(t.column,t.value);case"lt":return e.lt(t.column,t.value);case"lte":return e.lte(t.column,t.value);case"ilike":if(typeof t.value!="string")throw new Error(`Filter "${t.column}" with ilike requires a string value.`);return e.ilike(t.column,t.value);case"in":if(!Array.isArray(t.value))throw new Error(`Filter "${t.column}" with in requires an array value.`);return e.in(t.column,t.value);case"is":return e.is(t.column,t.value)}}function v(e,t,r){let a=t;for(const o of r)g(e,o.column),a=oe(a,o);return e==="site_settings"&&(a=a.neq("key",N)),a}function ne(e,t){const r=_(e),a=t?.length?t:r.columns.filter(o=>!h(o));return a.forEach(o=>g(e,o)),a.join(",")}function U(e,t){return t.map(r=>`${r}=${String(e[r]??"")}`).join("|")}async function B(e,t,r){const a=_(t);let o=e.from(t).select(a.primaryKey.join(",")).limit(k+1);o=v(t,o,r);const{data:i,error:s}=await o;if(s)throw new Error(`Failed to preview database mutation: ${f(s)}`);const d=Array.isArray(i)?i:[];if(d.length>k)throw new Error(`Refusing to mutate more than ${k} rows.`);return d}function X(e){const t=P(e.table),r=_(t),a=e.operation;if(r.readOnly)throw new Error(`Table "${t}" is read-only for Cortex AI.`);if(ie(t,e),(a==="update"||a==="delete")&&(!e.filters||e.filters.length===0))throw new Error(`${a} operations require at least one filter.`);if((a==="insert"||a==="upsert")&&(!e.rows||e.rows.length===0))throw new Error(`${a} operations require rows.`);if(a==="update"&&(!e.values||Object.keys(e.values).length===0))throw new Error("update operations require values.");if(e.rows&&e.rows.length>C)throw new Error(`Refusing to mutate more than ${C} input rows.`);const o=e.rows??(e.values?[e.values]:[]);for(const i of o){I(i);for(const s of Object.keys(i))g(t,s)}for(const i of e.filters??[])g(t,i.column);return t}function V(e){const{input:t,table:r,targetRows:a}=e,o=_(r),i=a.map(p=>U(p,o.primaryKey)).sort(),s=t.rows?.length??0,d=t.operation==="insert"||t.operation==="upsert"?s:a.length,l=t.summary||`${t.operation} ${d} ${d===1?"row":"rows"} in ${r}.`;return{affectedCount:d,operation:t.operation,sampleTargetIds:i.slice(0,10),summary:l,table:r,targetIds:i}}async function x(e){const t=e.context?.actorUserId;if(!t)return{auditError:"Missing actor user id.",auditLogged:!1};try{const{error:r}=await A(e.context).from("cortex_ai_db_mutation_audit").insert({action_name:e.toolName,actor_user_id:t,error_message:e.errorMessage??null,operation_summary:e.summary,payload:b(e.input),payload_hash:q(e.input),preview:b(e.preview),status:e.status,target_tables:e.targetTables,tool_name:e.toolName});return r?{auditError:f(r),auditLogged:!1}:{auditLogged:!0}}catch(r){return{auditError:f(r),auditLogged:!1}}}async function se(e,t,r){const o=_(t).primaryKey.join(",");let i;e.operation==="insert"?i=r.from(t).insert(e.rows).select(o):e.operation==="upsert"?i=r.from(t).upsert(e.rows).select(o):e.operation==="update"?(i=r.from(t).update(e.values),i=v(t,i,e.filters??[]),i=i.select(o)):(i=r.from(t).delete(),i=v(t,i,e.filters??[]),i=i.select(o));const{data:s,error:d}=await i;if(d)throw new Error(f(d));return Array.isArray(s)?s:[]}async function G(e){const t=O.parse(e),a=(t.table?[P(t.table)]:Object.keys($)).filter(o=>t.includeReadOnly||!_(o).readOnly).map(o=>{const i=_(o);return{columns:i.columns.filter(s=>!h(s)),description:i.description,primaryKey:i.primaryKey,readOnly:!!i.readOnly,table:o}});return{safetyNotes:["Use read_database_records for reads and execute_database_mutation/action_plan for writes.","No auth schema access, no arbitrary SQL, no password/secret/API-key fields.","profiles, user_addresses, and cortex_ai_db_mutation_audit are read-only.",`Protected site_settings keys (never readable or writable): ${[...T].sort().join(", ")}.`],success:!0,tables:a}}async function J(e,t){const r=j.parse(e),a=P(r.table),o=ne(a,r.columns);let i=A(t).from(a).select(o).range(r.offset,r.offset+r.limit-1);i=v(a,i,r.filters),r.orderBy&&(g(a,r.orderBy.column),i=i.order(r.orderBy.column,{ascending:r.orderBy.ascending}));const{data:s,error:d}=await i;if(d)throw new Error(`Failed to read database records: ${f(d)}`);const l=Array.isArray(s)?s:[];return{columns:o.split(","),limit:r.limit,offset:r.offset,rows:ae(l),success:!0,table:a}}async function M(e,t){const r=S.parse(e),a=X(r),o=A(t),i=r.operation==="update"||r.operation==="delete"?await B(o,a,r.filters??[]):[],s=V({input:r,table:a,targetRows:i}),d={input:r,targetIds:s.targetIds},l=F({action:`DATABASE ${r.operation}`,context:t,payload:d,preview:s,subject:a});if(l)return l;try{const p=await se(r,a,o),m=t?.skipAudit?{auditLogged:!1}:await x({context:t,input:d,preview:s,status:"success",summary:String(s.summary),targetTables:[a],toolName:"execute_database_mutation"});return{affectedCount:p.length||s.affectedCount,auditLogged:m.auditLogged,...m.auditError?{auditError:m.auditError}:{},mutationExecuted:!0,operation:r.operation,sampleTargetIds:p.map(c=>U(c,_(a).primaryKey)).slice(0,10),success:!0,summary:s.summary,table:a}}catch(p){const m=f(p),c=t?.skipAudit?{auditLogged:!1}:await x({context:t,errorMessage:m,input:d,preview:s,status:"failure",summary:String(s.summary),targetTables:[a],toolName:"execute_database_mutation"});return{auditLogged:c.auditLogged,...c.auditError?{auditError:c.auditError}:{},message:`Database mutation failed: ${m}`,mutationExecuted:!1,operation:r.operation,success:!1,table:a}}}async function Q(e,t){const r=D.parse(e),a=[];for(const c of r.actions){const y=X(c),u=c.operation==="update"||c.operation==="delete"?await B(A(t),y,c.filters??[]):[];a.push({input:c,preview:V({input:c,table:y,targetRows:u}),table:y})}const o=a.map(({preview:c})=>String(c.summary)),i={actionCount:a.length,actionSummaries:o,summary:r.summary||`Run ${a.length} confirmed database actions.`,tables:Array.from(new Set(a.map(c=>c.table)))},s=a.map(({input:c,preview:y,table:u})=>({input:c,table:u,targetIds:y.targetIds})),d=F({action:"DATABASE ACTION PLAN",context:t,payload:s,preview:i,subject:`${a.length} actions`});if(d)return d;const l=[];let p=!1;for(const[c,y]of a.entries()){const u=await M(y.input,{...t,skipAudit:!0,skipConfirmation:!0});if(l.push(u),u&&typeof u=="object"&&u.mutationExecuted===!0&&(p=!0),!u||typeof u!="object"||u.success===!1){const E=await x({context:t,errorMessage:u&&typeof u=="object"&&typeof u.message=="string"?u.message:`Database action ${c+1} failed.`,input:s,preview:i,status:"failure",summary:String(i.summary),targetTables:i.tables,toolName:"execute_database_action_plan"});return{actionCount:a.length,auditLogged:E.auditLogged,...E.auditError?{auditError:E.auditError}:{},failedActionIndex:c,message:u&&typeof u=="object"&&typeof u.message=="string"?u.message:`Database action ${c+1} failed.`,mutationExecuted:p,results:l,success:!1}}}const m=await x({context:t,input:s,preview:i,status:"success",summary:String(i.summary),targetTables:i.tables,toolName:"execute_database_action_plan"});return{actionCount:a.length,auditLogged:m.auditLogged,...m.auditError?{auditError:m.auditError}:{},mutationExecuted:p,results:l,success:!0,summary:r.summary??null}}function ce(e){return{describe_database_schema:w.tool({description:"Describe the public database tables Cortex AI can read or mutate, including read-only tables, primary keys, and writable columns. Use before broad database tasks when unsure about schema.",execute:t=>G(t),inputSchema:O,strict:!0}),execute_database_action_plan:w.tool({description:"Execute up to 8 typed database mutations as one confirmed plan. Mutating: first returns one combined confirmation preview and Confirm button; after confirmation, runs each action in order and stops on the first failure.",execute:t=>Q(t,e),inputSchema:D,strict:!0}),execute_database_mutation:w.tool({description:"Run one typed insert, update, upsert, or delete against an allowed public database table. Mutating: always previews affected rows and requires exact user confirmation before execution. Does not support arbitrary SQL.",execute:t=>M(t,e),inputSchema:S,strict:!0}),read_database_records:w.tool({description:"Read records from an allowed public database table with validated columns, filters, ordering, limit, and offset. Read-only and redacts protected secret-like values.",execute:t=>J(t,e),inputSchema:j,strict:!0})}}exports.createCortexDatabaseAgentTools=ce;exports.describeDatabaseSchemaInputSchema=O;exports.executeDatabaseActionPlan=Q;exports.executeDatabaseActionPlanInputSchema=D;exports.executeDatabaseMutation=M;exports.executeDatabaseMutationInputSchema=S;exports.executeDescribeDatabaseSchema=G;exports.executeReadDatabaseRecords=J;exports.readDatabaseRecordsInputSchema=j;
|