Files
mesh-catalog/modules/nodered/tools/index.ts
T

65 lines
2.7 KiB
TypeScript

// nodered's tools — importing nodered's own admin-API client (novox/hq ADR 0039). They return
// structured data; the mesh serves them through the sdk's tool harness.
//
// The deploy tool is nodered's one event source (novox/hq ADR 0041/0042): a successful deploy
// emits module.nodered.flows.deployed. nodered has nothing to observe on a timer, so there is no
// separate events entrypoint — the emit rides the action that causes it. The emit is best-effort:
// if no broker is bound, the deploy still succeeds and the announcement is simply skipped.
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
import { emit } from "@novox/mesh-sdk/events";
import { NodeRedClient } from "../client.js";
export function getNodeRedTools(nodered: NodeRedClient): ToolDefinition[] {
return [
{
name: "nodered_list_flows",
description: "List Node-RED flows (tabs) — id, label, disabled state — and the total node count.",
input: {},
run: async () => nodered.listFlows(),
},
{
name: "nodered_list_nodes",
description: "List the Node-RED node modules installed in the runtime and their versions.",
input: {},
run: async () => {
const nodes = await nodered.listNodes();
return { count: nodes.length, nodes };
},
},
{
name: "nodered_deploy",
description:
"Replace the whole Node-RED flow configuration and deploy it. `flows` is the full node " +
"array (as GET /flows returns). Emits module.nodered.flows.deployed on success.",
input: {
flows: { type: "array", description: "the full flow configuration — every node across every tab" },
type: { type: "string", description: "deployment type: full (default), nodes, or flows" },
},
run: async (args) => {
const flows = args.flows as unknown[];
if (!Array.isArray(flows)) throw new Error("flows must be an array of Node-RED nodes");
const type = args.type ? String(args.type) : "full";
const result = await nodered.deployFlows(flows, type);
// Best-effort announcement — a deploy must not fail because the broker is unbound here.
try {
await emit("module.nodered.flows.deployed", { rev: result.rev, nodeCount: result.nodeCount, type });
} catch (err) {
console.error(`[nodered] deployed but could not emit: ${err}`);
}
return result;
},
},
];
}
// The tools exist only when a Node-RED URL is configured; without one, nodered contributes none
// rather than failing the whole tool runtime.
registerModuleTools("nodered", (env) => {
try {
return getNodeRedTools(NodeRedClient.fromEnv(env));
} catch {
return [];
}
});