mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-09 05:10:45 +08:00
Add listBizMonitors catalog tool so agents can discover projects/tasks without an id.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
5a2ab85d14
commit
c1d07f7509
12 changed files with 984 additions and 19 deletions
|
|
@ -40,6 +40,7 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__sinkTopologyDualUnits"
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
"netx__listBizMonitors",
|
||||
"netx__getBizMonitorContext",
|
||||
"netx__getBizMonitorBoard",
|
||||
"netx__listBizMonitorReds",
|
||||
|
|
@ -1469,6 +1470,116 @@ async function findTopologyPaths(client, args, signal) {
|
|||
body.to_managed_ne_id = toMid;
|
||||
return client.post("/v1/topology/fabric/paths", body, signal, 30000);
|
||||
}
|
||||
function taskBriefNe(brief) {
|
||||
const rec = asRecord(brief);
|
||||
const name = typeof rec.ne_name === "string" ? rec.ne_name.trim() : "";
|
||||
const ip = typeof rec.ne_ip === "string" ? rec.ne_ip.trim() : "";
|
||||
if (name && ip)
|
||||
return `${name} (${ip})`;
|
||||
return name || ip;
|
||||
}
|
||||
function slimMigrationProject(row) {
|
||||
const mt = asRecord(row.monitor_template);
|
||||
return {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
status: row.status,
|
||||
note: row.note,
|
||||
monitor_template_id: row.monitor_template_id,
|
||||
monitor_template_name: typeof mt.name === "string" ? mt.name : "",
|
||||
old_task_id: row.old_task_id,
|
||||
new_task_id: row.new_task_id,
|
||||
old_hf_task_id: row.old_hf_task_id,
|
||||
new_hf_task_id: row.new_hf_task_id,
|
||||
old_ne: taskBriefNe(row.old_task),
|
||||
new_ne: taskBriefNe(row.new_task),
|
||||
hf_interval_sec: row.hf_interval_sec,
|
||||
hf_start_at: row.hf_start_at,
|
||||
hf_end_at: row.hf_end_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
}
|
||||
function matchesBizMonitorQ(row, q) {
|
||||
if (!q)
|
||||
return true;
|
||||
const hay = [
|
||||
row.id,
|
||||
row.name,
|
||||
row.note,
|
||||
row.status,
|
||||
row.ne_name,
|
||||
row.ne_ip,
|
||||
row.ne_id,
|
||||
row.old_ne,
|
||||
row.new_ne,
|
||||
row.monitor_template_name,
|
||||
row.purpose,
|
||||
row.vendor
|
||||
].map((v) => typeof v === "string" || typeof v === "number" ? String(v).toLowerCase() : "").join(" ");
|
||||
return hay.includes(q);
|
||||
}
|
||||
async function listBizMonitors(client, args, signal) {
|
||||
const kindRaw = str2(args, "kind", "all").trim().toLowerCase() || "all";
|
||||
const kind = kindRaw === "project" ? "projects" : kindRaw === "task" ? "tasks" : kindRaw;
|
||||
if (kind !== "all" && kind !== "projects" && kind !== "tasks") {
|
||||
return { ok: false, error: "kind_invalid", detail: "kind must be all|projects|tasks" };
|
||||
}
|
||||
const purpose = str2(args, "purpose").trim();
|
||||
const status = str2(args, "status").trim().toLowerCase();
|
||||
const q = str2(args, "q").trim().toLowerCase();
|
||||
const limit = clampInt(num2(args, "limit") ?? 100, 100, 1, 500);
|
||||
const wantProjects = kind === "all" || kind === "projects";
|
||||
const wantTasks = kind === "all" || kind === "tasks";
|
||||
const fetches = [];
|
||||
if (wantProjects)
|
||||
fetches.push(client.get("/v1/biz-migration/projects", {}, signal));
|
||||
else
|
||||
fetches.push(Promise.resolve({ ok: true, data: { items: [] } }));
|
||||
if (wantTasks) {
|
||||
const params = {};
|
||||
if (purpose)
|
||||
params.purpose = purpose;
|
||||
fetches.push(client.get("/v1/biz-state/tasks", params, signal));
|
||||
} else {
|
||||
fetches.push(Promise.resolve({ ok: true, data: { items: [] } }));
|
||||
}
|
||||
const [projRes, taskRes] = await Promise.all(fetches);
|
||||
if (wantProjects && projRes.ok === false)
|
||||
return projRes;
|
||||
if (wantTasks && taskRes.ok === false)
|
||||
return taskRes;
|
||||
const projItems = Array.isArray(asRecord(projRes.data).items) ? asRecord(projRes.data).items : [];
|
||||
const taskItems = Array.isArray(asRecord(taskRes.data).items) ? asRecord(taskRes.data).items : [];
|
||||
let projects = projItems.map((row) => slimMigrationProject(asRecord(row))).filter((row) => {
|
||||
if (status && String(row.status || "").toLowerCase() !== status)
|
||||
return false;
|
||||
return matchesBizMonitorQ(row, q);
|
||||
});
|
||||
let tasks = taskItems.map((row) => asRecord(row)).filter((row) => {
|
||||
if (status && String(row.status || "").toLowerCase() !== status)
|
||||
return false;
|
||||
return matchesBizMonitorQ(row, q);
|
||||
});
|
||||
const projectsTotal = projects.length;
|
||||
const tasksTotal = tasks.length;
|
||||
projects = projects.slice(0, limit);
|
||||
tasks = tasks.slice(0, limit);
|
||||
return {
|
||||
ok: true,
|
||||
data: {
|
||||
kind,
|
||||
projects: wantProjects ? projects : undefined,
|
||||
tasks: wantTasks ? tasks : undefined,
|
||||
counts: {
|
||||
projects: wantProjects ? projectsTotal : 0,
|
||||
tasks: wantTasks ? tasksTotal : 0,
|
||||
projects_returned: wantProjects ? projects.length : 0,
|
||||
tasks_returned: wantTasks ? tasks.length : 0
|
||||
},
|
||||
next: "Pass project_id (or task_id) to netx__getBizMonitorContext for the full definition bundle."
|
||||
}
|
||||
};
|
||||
}
|
||||
async function getBizMonitorContext(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
const taskId = str2(args, "task_id").trim();
|
||||
|
|
@ -2355,7 +2466,14 @@ function registerNetxTools(ctx, connection, options) {
|
|||
layer: str4(),
|
||||
detail: str4("summary | full")
|
||||
}, findTopologyPaths, getClient, t),
|
||||
tool2("netx__getBizMonitorContext", "Biz-monitor definition bundle: cutover project + monitor/compare templates + iface normalize + port mapping + related tasks/commands. Prefer project_id. Display keys are device-raw A/B.", {
|
||||
tool2("netx__listBizMonitors", "Catalog: list cutover/monitor-compare projects and biz_state monitor tasks. No project_id needed — call this first when the user did not give an id, then use getBizMonitorContext.", {
|
||||
kind: str4("all | projects | tasks (default all)"),
|
||||
purpose: str4("Filter tasks: portrait | cutover_hf | empty=all"),
|
||||
status: str4("Filter by status (e.g. active / paused)"),
|
||||
q: str4("Keyword on name / ne_name / note / id"),
|
||||
limit: num4("Max rows per list (default 100, max 500)")
|
||||
}, listBizMonitors, getClient, t),
|
||||
tool2("netx__getBizMonitorContext", "Biz-monitor definition bundle: cutover project + monitor/compare templates + iface normalize + port mapping + related tasks/commands. Prefer project_id from listBizMonitors. Display keys are device-raw A/B.", {
|
||||
project_id: str4("Cutover / biz-migration project id"),
|
||||
task_id: str4("biz_state task id (optional if project_id set)")
|
||||
}, getBizMonitorContext, getClient, t),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue