Completan la feature del endpoint /api/creator/hook-entryparams, que ya
estaba desplegado en el server (commit fe81765 del repo de Forge) pero sin
tools que lo usaran desde el agente.
Los entryParams son los parametros de entrada declarados de un hook global
({variable, value?, valueType?}), que se le pasan al invocarlo.
259 lines
11 KiB
JavaScript
259 lines
11 KiB
JavaScript
import { z } from "zod";
|
|
import { withAuth } from "../../auth/index.js";
|
|
import { withAuthParams } from "../helpers/authSchema.js";
|
|
import { handleToolError } from "../helpers/errorHandler.js";
|
|
import { pythonGet, pythonPost } from "../helpers/pythonServerClient.js";
|
|
import { getCurrentProjectInfo } from "../files/helpers.js";
|
|
import { canEditCode } from "../helpers/roleCheck.js";
|
|
|
|
/**
|
|
* Tools para leer/escribir el `middleWare` de hooks globales del layout.
|
|
*
|
|
* El middleware vive en `layout.json["hooks"][i].middleWare` y determina cuando
|
|
* un hook global se ejecuta automaticamente antes de renderizar paginas:
|
|
* - [] → solo cuando se llama explicitamente.
|
|
* - ["allurls"] → antes de cada URL del sitio.
|
|
* - ["<tableName>-<num>", ...] → solo antes de ciertos registros.
|
|
*/
|
|
|
|
function registerGetHookMiddlewareTool(server) {
|
|
server.tool(
|
|
"get_hook_middleware",
|
|
`Check which pages trigger a global hook as middleware. Middleware config determines whether the hook runs automatically BEFORE rendering specific pages (or all pages). Returns the list of middleware entries.
|
|
|
|
Use this when the user asks about hook behavior or to verify config before changing it.
|
|
|
|
hookEndPoint format: starts and ends with '/', with '/' as separator. E.g. file "hooks/hooks.parse_styles.php" → endPoint "/hooks/parse_styles/".
|
|
|
|
Returns:
|
|
- middleWare: [] → hook only runs on explicit call (<hook>, Twig filter, CmsApi).
|
|
- middleWare: ["allurls"] → runs before every page.
|
|
- middleWare: ["cms_apartados-8", ...] → runs before those specific records ("<tableName>-<num>").`,
|
|
withAuthParams({
|
|
hookEndPoint: z.string().describe('Hook endpoint path, e.g. "/hooks/parse_styles/"'),
|
|
}),
|
|
{ readOnlyHint: true, destructiveHint: false },
|
|
withAuth(async ({ hookEndPoint }, extra) => {
|
|
try {
|
|
const { projectSlug } = getCurrentProjectInfo();
|
|
const result = await pythonGet("/api/creator/hook-middleware", {
|
|
project: projectSlug,
|
|
endPoint: hookEndPoint,
|
|
});
|
|
if (!result?.success) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: false,
|
|
error: result?.error || "No se pudo leer el middleware",
|
|
}),
|
|
}],
|
|
isError: true,
|
|
};
|
|
}
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: true,
|
|
exists: !!result.exists,
|
|
middleWare: result.middleWare || [],
|
|
hookEndPoint,
|
|
}, null, 2),
|
|
}],
|
|
};
|
|
} catch (error) {
|
|
return handleToolError(error, "get_hook_middleware", { hookEndPoint });
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
function registerSetHookMiddlewareTool(server) {
|
|
server.tool(
|
|
"set_hook_middleware",
|
|
`Configure when a global hook runs automatically (middleware). This updates layout.json['hooks'][i].middleWare for the hook matching hookEndPoint.
|
|
|
|
Use this AFTER creating or editing a hook file (via acai-write) if the hook should execute BEFORE rendering specific pages. The hook file alone is not enough — the file exists but won't auto-run as middleware without this config.
|
|
|
|
middleWare values:
|
|
- [] → hook runs only when called explicitly (default for new hooks).
|
|
- ["allurls"] → runs before every page of the site.
|
|
- ["<tableName>-<num>", ...] → runs before specific records. Get num+tableName from the CMS records.
|
|
|
|
Examples:
|
|
- Redirect logic that must run on the homepage only: middleWare=["cms_apartados-2"] (assuming num=2 is home).
|
|
- Global analytics injection: middleWare=["allurls"].
|
|
- Just a reusable utility hook called from modules/twig: middleWare=[] (default).`,
|
|
withAuthParams({
|
|
hookEndPoint: z.string().describe('Hook endpoint path, e.g. "/hooks/parse_styles/"'),
|
|
middleWare: z.array(z.string()).describe('Array de strings. Vacio, ["allurls"], o ["<tableName>-<num>", ...]'),
|
|
}),
|
|
{ readOnlyHint: false, destructiveHint: false },
|
|
withAuth(async ({ hookEndPoint, middleWare }, extra) => {
|
|
try {
|
|
const { projectSlug } = getCurrentProjectInfo();
|
|
const result = await pythonPost("/api/creator/hook-middleware", {
|
|
project: projectSlug,
|
|
endPoint: hookEndPoint,
|
|
middleWare,
|
|
});
|
|
if (!result?.success) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: false,
|
|
error: result?.error || "No se pudo guardar",
|
|
}),
|
|
}],
|
|
isError: true,
|
|
};
|
|
}
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: true,
|
|
message: "Middleware actualizado",
|
|
middleWare: result.middleWare || [],
|
|
hookEndPoint,
|
|
}, null, 2),
|
|
}],
|
|
};
|
|
} catch (error) {
|
|
return handleToolError(error, "set_hook_middleware", { hookEndPoint, middleWare });
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
function registerGetHookEntryParamsTool(server) {
|
|
server.tool(
|
|
"get_hook_entryparams",
|
|
`Read the declared entry parameters (entryParams) of a global hook. entryParams are the input parameters a hook expects — each has a required 'variable' name plus optional 'value' and 'valueType'. They are passed to the hook when it is invoked.
|
|
|
|
Use this when the user asks about a hook's inputs, or to inspect the current params before editing them.
|
|
|
|
hookEndPoint format: starts and ends with '/', with '/' as separator. E.g. "/hooks/appListado/".
|
|
|
|
Returns:
|
|
- entryParams: array of { variable, value?, valueType? }.
|
|
Example: [{ "variable": "action" }, { "variable": "data", "value": "", "valueType": "Integer" }].`,
|
|
withAuthParams({
|
|
hookEndPoint: z.string().describe('Hook endpoint path, e.g. "/hooks/appListado/"'),
|
|
}),
|
|
{ readOnlyHint: true, destructiveHint: false },
|
|
withAuth(async ({ hookEndPoint }, extra) => {
|
|
try {
|
|
const { projectSlug } = getCurrentProjectInfo();
|
|
const result = await pythonGet("/api/creator/hook-entryparams", {
|
|
project: projectSlug,
|
|
endPoint: hookEndPoint,
|
|
});
|
|
if (!result?.success) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: false,
|
|
error: result?.error || "No se pudieron leer los entryParams",
|
|
}),
|
|
}],
|
|
isError: true,
|
|
};
|
|
}
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: true,
|
|
exists: !!result.exists,
|
|
entryParams: result.entryParams || [],
|
|
hookEndPoint,
|
|
}, null, 2),
|
|
}],
|
|
};
|
|
} catch (error) {
|
|
return handleToolError(error, "get_hook_entryparams", { hookEndPoint });
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
function registerSetHookEntryParamsTool(server) {
|
|
server.tool(
|
|
"set_hook_entryparams",
|
|
`Set the declared entry parameters (entryParams) of a global hook. entryParams describe the inputs a hook expects — each has a required 'variable' name plus optional 'value' and 'valueType'.
|
|
|
|
IMPORTANT: this OVERWRITES the entire entryParams list of the hook (it does NOT merge). You must pass the COMPLETE set of params every time, because the whole array is replaced.
|
|
|
|
Use this AFTER creating or editing the hook file (via acai-write) to declare which inputs it accepts.
|
|
|
|
entryParams format: array of { variable, value?, valueType? }.
|
|
Example: [{ "variable": "action" }, { "variable": "data", "value": "", "valueType": "Integer" }].`,
|
|
withAuthParams({
|
|
hookEndPoint: z.string().describe('Hook endpoint path, e.g. "/hooks/appListado/"'),
|
|
entryParams: z.array(z.object({
|
|
variable: z.string(),
|
|
value: z.string().optional(),
|
|
valueType: z.string().optional(),
|
|
})).describe('Complete list of entry params. Each item: { variable (required), value? (string), valueType? (string) }. Replaces the whole array. E.g. [{"variable":"action"},{"variable":"data","value":"","valueType":"Integer"}]'),
|
|
}),
|
|
{ readOnlyHint: false, destructiveHint: false },
|
|
withAuth(async ({ hookEndPoint, entryParams }, extra) => {
|
|
try {
|
|
const { projectSlug } = getCurrentProjectInfo();
|
|
const result = await pythonPost("/api/creator/hook-entryparams", {
|
|
project: projectSlug,
|
|
endPoint: hookEndPoint,
|
|
entryParams,
|
|
});
|
|
if (!result?.success) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: false,
|
|
error: result?.error || "No se pudo guardar",
|
|
}),
|
|
}],
|
|
isError: true,
|
|
};
|
|
}
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
success: true,
|
|
message: result.message || "entryParams actualizados",
|
|
entryParams: result.entryParams || [],
|
|
hookEndPoint,
|
|
}, null, 2),
|
|
}],
|
|
};
|
|
} catch (error) {
|
|
return handleToolError(error, "set_hook_entryparams", { hookEndPoint, entryParams });
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Registra las tools de configuracion de hooks globales.
|
|
*
|
|
* Las tools de solo lectura (`get_hook_middleware`, `get_hook_entryparams`) se
|
|
* registran siempre. Las de escritura modifican el layout y solo se exponen si
|
|
* el rol puede editar codigo — sigue el mismo criterio que otras tools de
|
|
* escritura (ver project/index.js).
|
|
*/
|
|
export function registerHookTools(server) {
|
|
registerGetHookMiddlewareTool(server);
|
|
registerGetHookEntryParamsTool(server);
|
|
if (canEditCode()) {
|
|
registerSetHookMiddlewareTool(server);
|
|
registerSetHookEntryParamsTool(server);
|
|
}
|
|
}
|