mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-08 23:50:48 +08:00
Add bizMonitor host tools for cutover analysis via netx REST (0.1.40).
Expose getBizMonitor* / getBizCollect* on the Ops preset with skill netx-biz-monitor; keep tooling in netxops instead of netx-mcp. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f7801db296
commit
0934e10409
23 changed files with 1022 additions and 62 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Install Netx Ops on DeepSeek Harness
|
||||
# Install Netx Ops on DeepSeek Harness
|
||||
|
||||
## What `dsh plugin add` gives you
|
||||
|
||||
|
|
@ -16,9 +16,9 @@ You do **not** run `link-preset.ps1` for normal use. That script is only a manua
|
|||
## Still required outside the npm package
|
||||
|
||||
1. **netx API** reachable (default `http://127.0.0.1:8890`) — the data plane.
|
||||
2. **API token** with scopes matching the tools you use (`alarms:read`, `ne:read`, `ne:exec`, `sql:query`, …).
|
||||
2. **API token** with scopes matching the tools you use (`alarms:read`, `ne:read`, `ne:exec`, `biz-monitor:read`, `sql:query`, …).
|
||||
|
||||
No local Python / `netx_mcp` install is required for DSH. (OpenClaw and other MCP hosts can still use `python -m netx_mcp` separately.)
|
||||
No local Python / `netx_mcp` install is required for DSH. (OpenClaw and other MCP hosts can still use `python -m netx_mcp` separately for NMS/CLI — **not** for biz-monitor; that is netxops host tools only.)
|
||||
|
||||
## Clone / `pnpm install`(仓库源码)
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ One command, three **direct** profile bundles. Do **not** nest IM/cron under net
|
|||
1. Plugins card **Netx Ops** visible.
|
||||
2. With key-alarm push on: card badge shows **Connected** (or reconnecting / auth failed with detail).
|
||||
3. Agent presets → Custom → **Netx Ops**.
|
||||
4. Tools include `netx__queryNmsAlarms` / `netx__execManagedNe` / `netx__findTopologyPaths` (**ops** → skill `netx-ops`). Canvas / dual_unit need **topology** (`netx-topology`).
|
||||
4. Tools include `netx__queryNmsAlarms` / `netx__execManagedNe` / `netx__findTopologyPaths` (**ops** → skill `netx-ops`). Cutover analysis: `netx__getBizMonitorContext` … (**bizMonitor** → `netx-biz-monitor`). Canvas / dual_unit need **topology** (`netx-topology`).
|
||||
5. Export section shows a session count; **下载 ZIP** starts a browser download.
|
||||
6. Optional knowledge-base badge shows operator/country/version when `kbRoot` + MANIFEST are valid.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ Tools register by capability group. **One group ↔ one skill.**
|
|||
|-------|-------|------------------------|
|
||||
| **ops** | `netx-ops` | on |
|
||||
| **topology** | `netx-topology` | off |
|
||||
| **bizMonitor** | `netx-biz-monitor` | on |
|
||||
|
||||
Public flags mount the same group on the host for other presets.
|
||||
Forced mounts: `dsh-netxops/tools-ops` | `tools-topology` (legacy aliases: `tools-nms` / `tools-common` → ops).
|
||||
Forced mounts: `dsh-netxops/tools-ops` | `tools-topology` | `tools-biz-monitor` (legacy aliases: `tools-nms` / `tools-common` → ops).
|
||||
|
||||
Model names: `netx__<stem>`. NMS tools use `Nms`; adapter `nmsProvider=zte-ume` still hits `/v1/ume/*`.
|
||||
|
||||
|
|
@ -20,6 +21,17 @@ Model names: `netx__<stem>`. NMS tools use `Nms`; adapter `nmsProvider=zte-ume`
|
|||
| `listManagedNe` / `getManagedNe` / `execManagedNe` / `listCliTargets` | Managed CLI (login / show) |
|
||||
| `findTopologyPaths` | Fabric path lookup |
|
||||
|
||||
## bizMonitor → `netx-biz-monitor`
|
||||
|
||||
Host tools call netx REST directly (**not** netx-mcp). Token needs `biz-monitor:read`.
|
||||
|
||||
| Tool | Role |
|
||||
|------|------|
|
||||
| `getBizMonitorContext` | Project + templates + normalize + port map + tasks |
|
||||
| `getBizMonitorBoard` | Batch / evaluate progress |
|
||||
| `listBizMonitorReds` / `getBizMonitorDiffs` | Reds & diffs with evidence (raw A/B + show cmd) |
|
||||
| `getBizCollectBatch` / `getBizCollectCommandRaw` | Collect audit + full CLI raw |
|
||||
|
||||
## topology → `netx-topology`
|
||||
|
||||
| Tool | Role |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
bizMonitor: Object.freeze({ inPreset: true, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
ops: Object.freeze([
|
||||
|
|
@ -37,15 +38,25 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__suggestSinkHubs",
|
||||
"netx__analyzeTopologyViewLayout",
|
||||
"netx__sinkTopologyDualUnits"
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
"netx__getBizMonitorContext",
|
||||
"netx__getBizMonitorBoard",
|
||||
"netx__listBizMonitorReds",
|
||||
"netx__getBizMonitorDiffs",
|
||||
"netx__getBizCollectBatch",
|
||||
"netx__getBizCollectCommandRaw"
|
||||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
topology: "topology",
|
||||
bizMonitor: "biz-monitor"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"ops",
|
||||
"topology"
|
||||
"topology",
|
||||
"bizMonitor"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
const policy = groups ?? DEFAULT_CAPABILITY_GROUPS;
|
||||
|
|
@ -60,12 +71,12 @@ function groupsForced(only) {
|
|||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
const out = new Set;
|
||||
for (const id of groupIds) {
|
||||
for (const tool of TOOLS_BY_GROUP[id])
|
||||
names.add(tool);
|
||||
for (const name of TOOLS_BY_GROUP[id] ?? [])
|
||||
out.add(name);
|
||||
}
|
||||
return names;
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/netx/group-skills.ts
|
||||
|
|
@ -1458,6 +1469,68 @@ async function findTopologyPaths(client, args, signal) {
|
|||
body.to_managed_ne_id = toMid;
|
||||
return client.post("/v1/topology/fabric/paths", body, signal, 30000);
|
||||
}
|
||||
async function getBizMonitorContext(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
const taskId = str2(args, "task_id").trim();
|
||||
if (!projectId && !taskId) {
|
||||
return { ok: false, error: "project_id_or_task_id_required" };
|
||||
}
|
||||
const params = {};
|
||||
if (projectId)
|
||||
params.project_id = projectId;
|
||||
if (taskId)
|
||||
params.task_id = taskId;
|
||||
return client.get("/v1/biz-migration/monitor-context", params, signal);
|
||||
}
|
||||
async function getBizMonitorBoard(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
const params = {};
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (runId)
|
||||
params.run_id = runId;
|
||||
return client.get(`/v1/biz-migration/batches/${encodeURIComponent(batchId)}/board`, params, signal);
|
||||
}
|
||||
async function listBizMonitorReds(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
if (!projectId)
|
||||
return { ok: false, error: "project_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["status"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
return client.get(`/v1/biz-migration/projects/${encodeURIComponent(projectId)}/red-tickets`, params, signal);
|
||||
}
|
||||
async function getBizMonitorDiffs(client, args, signal) {
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (!runId)
|
||||
return { ok: false, error: "run_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["metric_id", "sheet_id", "verdict", "color", "q"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
const offset = num2(args, "offset");
|
||||
if (offset !== undefined)
|
||||
params.offset = clampInt(offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
||||
return client.get(`/v1/biz-migration/runs/${encodeURIComponent(runId)}/diffs`, params, signal);
|
||||
}
|
||||
async function getBizCollectBatch(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}`, {}, signal);
|
||||
}
|
||||
async function getBizCollectCommandRaw(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
const commandId = str2(args, "command_id").trim();
|
||||
if (!batchId || !commandId) {
|
||||
return { ok: false, error: "batch_id_and_command_id_required" };
|
||||
}
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}/commands/${encodeURIComponent(commandId)}`, {}, signal);
|
||||
}
|
||||
|
||||
// src/netx/topology-handlers.ts
|
||||
function asRecord2(value) {
|
||||
|
|
@ -2282,6 +2355,36 @@ 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.", {
|
||||
project_id: str4("Cutover / biz-migration project id"),
|
||||
task_id: str4("biz_state task id (optional if project_id set)")
|
||||
}, getBizMonitorContext, getClient, t),
|
||||
tool2("netx__getBizMonitorBoard", "Cutover batch board: project/batch status, evaluate run summary, sheet cards, verdict counts, metric_batches.", {
|
||||
batch_id: { type: "string", required: true },
|
||||
run_id: str4("Optional evaluate run id")
|
||||
}, getBizMonitorBoard, getClient, t),
|
||||
tool2("netx__listBizMonitorReds", "Open/carried red tickets. Each item has old_key/new_key (raw A/B) and evidence (device, collect time, show command, iface raw→norm→map).", {
|
||||
project_id: { type: "string", required: true },
|
||||
status: str4("open|carried|resolved or empty=all"),
|
||||
limit: num4()
|
||||
}, listBizMonitorReds, getClient, t),
|
||||
tool2("netx__getBizMonitorDiffs", "Paged evaluate diffs. Rows expose old_key/new_key (raw A/B), match_* keys, status, evidence with show commands.", {
|
||||
run_id: { type: "string", required: true },
|
||||
metric_id: str4(),
|
||||
sheet_id: str4(),
|
||||
verdict: str4(),
|
||||
color: str4("e.g. red"),
|
||||
q: str4(),
|
||||
limit: num4(),
|
||||
offset: num4()
|
||||
}, getBizMonitorDiffs, getClient, t),
|
||||
tool2("netx__getBizCollectBatch", "One biz_state collect batch: commands (raw_command, parse_status, preview), parsed metrics.", {
|
||||
batch_id: { type: "string", required: true }
|
||||
}, getBizCollectBatch, getClient, t),
|
||||
tool2("netx__getBizCollectCommandRaw", "Full CLI raw_text for one collect command inside a batch (deep dive).", {
|
||||
batch_id: { type: "string", required: true },
|
||||
command_id: { type: "string", required: true }
|
||||
}, getBizCollectCommandRaw, getClient, t),
|
||||
tool2("netx__getTopologyTree", "Get topology folder tree (nav roots + Root map canvases). Start here before createTopologyFolder.", { compact: bool4(), max_depth: num4() }, getTopologyTree, getClient, t),
|
||||
tool2("netx__getTopologyView", "Get a topology view by view_id. Default detail=summary with sample_nodes + links[].", {
|
||||
view_id: { type: "string", required: true },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
bizMonitor: Object.freeze({ inPreset: true, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
ops: Object.freeze([
|
||||
|
|
@ -37,15 +38,25 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__suggestSinkHubs",
|
||||
"netx__analyzeTopologyViewLayout",
|
||||
"netx__sinkTopologyDualUnits"
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
"netx__getBizMonitorContext",
|
||||
"netx__getBizMonitorBoard",
|
||||
"netx__listBizMonitorReds",
|
||||
"netx__getBizMonitorDiffs",
|
||||
"netx__getBizCollectBatch",
|
||||
"netx__getBizCollectCommandRaw"
|
||||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
topology: "topology",
|
||||
bizMonitor: "biz-monitor"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"ops",
|
||||
"topology"
|
||||
"topology",
|
||||
"bizMonitor"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
const policy = groups ?? DEFAULT_CAPABILITY_GROUPS;
|
||||
|
|
@ -60,12 +71,12 @@ function groupsForced(only) {
|
|||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
const out = new Set;
|
||||
for (const id of groupIds) {
|
||||
for (const tool of TOOLS_BY_GROUP[id])
|
||||
names.add(tool);
|
||||
for (const name of TOOLS_BY_GROUP[id] ?? [])
|
||||
out.add(name);
|
||||
}
|
||||
return names;
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/netx/group-skills.ts
|
||||
|
|
@ -1458,6 +1469,68 @@ async function findTopologyPaths(client, args, signal) {
|
|||
body.to_managed_ne_id = toMid;
|
||||
return client.post("/v1/topology/fabric/paths", body, signal, 30000);
|
||||
}
|
||||
async function getBizMonitorContext(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
const taskId = str2(args, "task_id").trim();
|
||||
if (!projectId && !taskId) {
|
||||
return { ok: false, error: "project_id_or_task_id_required" };
|
||||
}
|
||||
const params = {};
|
||||
if (projectId)
|
||||
params.project_id = projectId;
|
||||
if (taskId)
|
||||
params.task_id = taskId;
|
||||
return client.get("/v1/biz-migration/monitor-context", params, signal);
|
||||
}
|
||||
async function getBizMonitorBoard(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
const params = {};
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (runId)
|
||||
params.run_id = runId;
|
||||
return client.get(`/v1/biz-migration/batches/${encodeURIComponent(batchId)}/board`, params, signal);
|
||||
}
|
||||
async function listBizMonitorReds(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
if (!projectId)
|
||||
return { ok: false, error: "project_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["status"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
return client.get(`/v1/biz-migration/projects/${encodeURIComponent(projectId)}/red-tickets`, params, signal);
|
||||
}
|
||||
async function getBizMonitorDiffs(client, args, signal) {
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (!runId)
|
||||
return { ok: false, error: "run_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["metric_id", "sheet_id", "verdict", "color", "q"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
const offset = num2(args, "offset");
|
||||
if (offset !== undefined)
|
||||
params.offset = clampInt(offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
||||
return client.get(`/v1/biz-migration/runs/${encodeURIComponent(runId)}/diffs`, params, signal);
|
||||
}
|
||||
async function getBizCollectBatch(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}`, {}, signal);
|
||||
}
|
||||
async function getBizCollectCommandRaw(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
const commandId = str2(args, "command_id").trim();
|
||||
if (!batchId || !commandId) {
|
||||
return { ok: false, error: "batch_id_and_command_id_required" };
|
||||
}
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}/commands/${encodeURIComponent(commandId)}`, {}, signal);
|
||||
}
|
||||
|
||||
// src/netx/topology-handlers.ts
|
||||
function asRecord2(value) {
|
||||
|
|
@ -2282,6 +2355,36 @@ 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.", {
|
||||
project_id: str4("Cutover / biz-migration project id"),
|
||||
task_id: str4("biz_state task id (optional if project_id set)")
|
||||
}, getBizMonitorContext, getClient, t),
|
||||
tool2("netx__getBizMonitorBoard", "Cutover batch board: project/batch status, evaluate run summary, sheet cards, verdict counts, metric_batches.", {
|
||||
batch_id: { type: "string", required: true },
|
||||
run_id: str4("Optional evaluate run id")
|
||||
}, getBizMonitorBoard, getClient, t),
|
||||
tool2("netx__listBizMonitorReds", "Open/carried red tickets. Each item has old_key/new_key (raw A/B) and evidence (device, collect time, show command, iface raw→norm→map).", {
|
||||
project_id: { type: "string", required: true },
|
||||
status: str4("open|carried|resolved or empty=all"),
|
||||
limit: num4()
|
||||
}, listBizMonitorReds, getClient, t),
|
||||
tool2("netx__getBizMonitorDiffs", "Paged evaluate diffs. Rows expose old_key/new_key (raw A/B), match_* keys, status, evidence with show commands.", {
|
||||
run_id: { type: "string", required: true },
|
||||
metric_id: str4(),
|
||||
sheet_id: str4(),
|
||||
verdict: str4(),
|
||||
color: str4("e.g. red"),
|
||||
q: str4(),
|
||||
limit: num4(),
|
||||
offset: num4()
|
||||
}, getBizMonitorDiffs, getClient, t),
|
||||
tool2("netx__getBizCollectBatch", "One biz_state collect batch: commands (raw_command, parse_status, preview), parsed metrics.", {
|
||||
batch_id: { type: "string", required: true }
|
||||
}, getBizCollectBatch, getClient, t),
|
||||
tool2("netx__getBizCollectCommandRaw", "Full CLI raw_text for one collect command inside a batch (deep dive).", {
|
||||
batch_id: { type: "string", required: true },
|
||||
command_id: { type: "string", required: true }
|
||||
}, getBizCollectCommandRaw, getClient, t),
|
||||
tool2("netx__getTopologyTree", "Get topology folder tree (nav roots + Root map canvases). Start here before createTopologyFolder.", { compact: bool4(), max_depth: num4() }, getTopologyTree, getClient, t),
|
||||
tool2("netx__getTopologyView", "Get a topology view by view_id. Default detail=summary with sample_nodes + links[].", {
|
||||
view_id: { type: "string", required: true },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
bizMonitor: Object.freeze({ inPreset: true, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
ops: Object.freeze([
|
||||
|
|
@ -37,15 +38,25 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__suggestSinkHubs",
|
||||
"netx__analyzeTopologyViewLayout",
|
||||
"netx__sinkTopologyDualUnits"
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
"netx__getBizMonitorContext",
|
||||
"netx__getBizMonitorBoard",
|
||||
"netx__listBizMonitorReds",
|
||||
"netx__getBizMonitorDiffs",
|
||||
"netx__getBizCollectBatch",
|
||||
"netx__getBizCollectCommandRaw"
|
||||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
topology: "topology",
|
||||
bizMonitor: "biz-monitor"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"ops",
|
||||
"topology"
|
||||
"topology",
|
||||
"bizMonitor"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
const policy = groups ?? DEFAULT_CAPABILITY_GROUPS;
|
||||
|
|
@ -60,12 +71,12 @@ function groupsForced(only) {
|
|||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
const out = new Set;
|
||||
for (const id of groupIds) {
|
||||
for (const tool of TOOLS_BY_GROUP[id])
|
||||
names.add(tool);
|
||||
for (const name of TOOLS_BY_GROUP[id] ?? [])
|
||||
out.add(name);
|
||||
}
|
||||
return names;
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/netx/group-skills.ts
|
||||
|
|
@ -1458,6 +1469,68 @@ async function findTopologyPaths(client, args, signal) {
|
|||
body.to_managed_ne_id = toMid;
|
||||
return client.post("/v1/topology/fabric/paths", body, signal, 30000);
|
||||
}
|
||||
async function getBizMonitorContext(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
const taskId = str2(args, "task_id").trim();
|
||||
if (!projectId && !taskId) {
|
||||
return { ok: false, error: "project_id_or_task_id_required" };
|
||||
}
|
||||
const params = {};
|
||||
if (projectId)
|
||||
params.project_id = projectId;
|
||||
if (taskId)
|
||||
params.task_id = taskId;
|
||||
return client.get("/v1/biz-migration/monitor-context", params, signal);
|
||||
}
|
||||
async function getBizMonitorBoard(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
const params = {};
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (runId)
|
||||
params.run_id = runId;
|
||||
return client.get(`/v1/biz-migration/batches/${encodeURIComponent(batchId)}/board`, params, signal);
|
||||
}
|
||||
async function listBizMonitorReds(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
if (!projectId)
|
||||
return { ok: false, error: "project_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["status"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
return client.get(`/v1/biz-migration/projects/${encodeURIComponent(projectId)}/red-tickets`, params, signal);
|
||||
}
|
||||
async function getBizMonitorDiffs(client, args, signal) {
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (!runId)
|
||||
return { ok: false, error: "run_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["metric_id", "sheet_id", "verdict", "color", "q"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
const offset = num2(args, "offset");
|
||||
if (offset !== undefined)
|
||||
params.offset = clampInt(offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
||||
return client.get(`/v1/biz-migration/runs/${encodeURIComponent(runId)}/diffs`, params, signal);
|
||||
}
|
||||
async function getBizCollectBatch(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}`, {}, signal);
|
||||
}
|
||||
async function getBizCollectCommandRaw(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
const commandId = str2(args, "command_id").trim();
|
||||
if (!batchId || !commandId) {
|
||||
return { ok: false, error: "batch_id_and_command_id_required" };
|
||||
}
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}/commands/${encodeURIComponent(commandId)}`, {}, signal);
|
||||
}
|
||||
|
||||
// src/netx/topology-handlers.ts
|
||||
function asRecord2(value) {
|
||||
|
|
@ -2282,6 +2355,36 @@ 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.", {
|
||||
project_id: str4("Cutover / biz-migration project id"),
|
||||
task_id: str4("biz_state task id (optional if project_id set)")
|
||||
}, getBizMonitorContext, getClient, t),
|
||||
tool2("netx__getBizMonitorBoard", "Cutover batch board: project/batch status, evaluate run summary, sheet cards, verdict counts, metric_batches.", {
|
||||
batch_id: { type: "string", required: true },
|
||||
run_id: str4("Optional evaluate run id")
|
||||
}, getBizMonitorBoard, getClient, t),
|
||||
tool2("netx__listBizMonitorReds", "Open/carried red tickets. Each item has old_key/new_key (raw A/B) and evidence (device, collect time, show command, iface raw→norm→map).", {
|
||||
project_id: { type: "string", required: true },
|
||||
status: str4("open|carried|resolved or empty=all"),
|
||||
limit: num4()
|
||||
}, listBizMonitorReds, getClient, t),
|
||||
tool2("netx__getBizMonitorDiffs", "Paged evaluate diffs. Rows expose old_key/new_key (raw A/B), match_* keys, status, evidence with show commands.", {
|
||||
run_id: { type: "string", required: true },
|
||||
metric_id: str4(),
|
||||
sheet_id: str4(),
|
||||
verdict: str4(),
|
||||
color: str4("e.g. red"),
|
||||
q: str4(),
|
||||
limit: num4(),
|
||||
offset: num4()
|
||||
}, getBizMonitorDiffs, getClient, t),
|
||||
tool2("netx__getBizCollectBatch", "One biz_state collect batch: commands (raw_command, parse_status, preview), parsed metrics.", {
|
||||
batch_id: { type: "string", required: true }
|
||||
}, getBizCollectBatch, getClient, t),
|
||||
tool2("netx__getBizCollectCommandRaw", "Full CLI raw_text for one collect command inside a batch (deep dive).", {
|
||||
batch_id: { type: "string", required: true },
|
||||
command_id: { type: "string", required: true }
|
||||
}, getBizCollectCommandRaw, getClient, t),
|
||||
tool2("netx__getTopologyTree", "Get topology folder tree (nav roots + Root map canvases). Start here before createTopologyFolder.", { compact: bool4(), max_depth: num4() }, getTopologyTree, getClient, t),
|
||||
tool2("netx__getTopologyView", "Get a topology view by view_id. Default detail=summary with sample_nodes + links[].", {
|
||||
view_id: { type: "string", required: true },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
bizMonitor: Object.freeze({ inPreset: true, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
ops: Object.freeze([
|
||||
|
|
@ -37,15 +38,25 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__suggestSinkHubs",
|
||||
"netx__analyzeTopologyViewLayout",
|
||||
"netx__sinkTopologyDualUnits"
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
"netx__getBizMonitorContext",
|
||||
"netx__getBizMonitorBoard",
|
||||
"netx__listBizMonitorReds",
|
||||
"netx__getBizMonitorDiffs",
|
||||
"netx__getBizCollectBatch",
|
||||
"netx__getBizCollectCommandRaw"
|
||||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
topology: "topology",
|
||||
bizMonitor: "biz-monitor"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"ops",
|
||||
"topology"
|
||||
"topology",
|
||||
"bizMonitor"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
const policy = groups ?? DEFAULT_CAPABILITY_GROUPS;
|
||||
|
|
@ -60,12 +71,12 @@ function groupsForced(only) {
|
|||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
const out = new Set;
|
||||
for (const id of groupIds) {
|
||||
for (const tool of TOOLS_BY_GROUP[id])
|
||||
names.add(tool);
|
||||
for (const name of TOOLS_BY_GROUP[id] ?? [])
|
||||
out.add(name);
|
||||
}
|
||||
return names;
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/netx/group-skills.ts
|
||||
|
|
@ -1458,6 +1469,68 @@ async function findTopologyPaths(client, args, signal) {
|
|||
body.to_managed_ne_id = toMid;
|
||||
return client.post("/v1/topology/fabric/paths", body, signal, 30000);
|
||||
}
|
||||
async function getBizMonitorContext(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
const taskId = str2(args, "task_id").trim();
|
||||
if (!projectId && !taskId) {
|
||||
return { ok: false, error: "project_id_or_task_id_required" };
|
||||
}
|
||||
const params = {};
|
||||
if (projectId)
|
||||
params.project_id = projectId;
|
||||
if (taskId)
|
||||
params.task_id = taskId;
|
||||
return client.get("/v1/biz-migration/monitor-context", params, signal);
|
||||
}
|
||||
async function getBizMonitorBoard(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
const params = {};
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (runId)
|
||||
params.run_id = runId;
|
||||
return client.get(`/v1/biz-migration/batches/${encodeURIComponent(batchId)}/board`, params, signal);
|
||||
}
|
||||
async function listBizMonitorReds(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
if (!projectId)
|
||||
return { ok: false, error: "project_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["status"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
return client.get(`/v1/biz-migration/projects/${encodeURIComponent(projectId)}/red-tickets`, params, signal);
|
||||
}
|
||||
async function getBizMonitorDiffs(client, args, signal) {
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (!runId)
|
||||
return { ok: false, error: "run_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["metric_id", "sheet_id", "verdict", "color", "q"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
const offset = num2(args, "offset");
|
||||
if (offset !== undefined)
|
||||
params.offset = clampInt(offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
||||
return client.get(`/v1/biz-migration/runs/${encodeURIComponent(runId)}/diffs`, params, signal);
|
||||
}
|
||||
async function getBizCollectBatch(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}`, {}, signal);
|
||||
}
|
||||
async function getBizCollectCommandRaw(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
const commandId = str2(args, "command_id").trim();
|
||||
if (!batchId || !commandId) {
|
||||
return { ok: false, error: "batch_id_and_command_id_required" };
|
||||
}
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}/commands/${encodeURIComponent(commandId)}`, {}, signal);
|
||||
}
|
||||
|
||||
// src/netx/topology-handlers.ts
|
||||
function asRecord2(value) {
|
||||
|
|
@ -2282,6 +2355,36 @@ 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.", {
|
||||
project_id: str4("Cutover / biz-migration project id"),
|
||||
task_id: str4("biz_state task id (optional if project_id set)")
|
||||
}, getBizMonitorContext, getClient, t),
|
||||
tool2("netx__getBizMonitorBoard", "Cutover batch board: project/batch status, evaluate run summary, sheet cards, verdict counts, metric_batches.", {
|
||||
batch_id: { type: "string", required: true },
|
||||
run_id: str4("Optional evaluate run id")
|
||||
}, getBizMonitorBoard, getClient, t),
|
||||
tool2("netx__listBizMonitorReds", "Open/carried red tickets. Each item has old_key/new_key (raw A/B) and evidence (device, collect time, show command, iface raw→norm→map).", {
|
||||
project_id: { type: "string", required: true },
|
||||
status: str4("open|carried|resolved or empty=all"),
|
||||
limit: num4()
|
||||
}, listBizMonitorReds, getClient, t),
|
||||
tool2("netx__getBizMonitorDiffs", "Paged evaluate diffs. Rows expose old_key/new_key (raw A/B), match_* keys, status, evidence with show commands.", {
|
||||
run_id: { type: "string", required: true },
|
||||
metric_id: str4(),
|
||||
sheet_id: str4(),
|
||||
verdict: str4(),
|
||||
color: str4("e.g. red"),
|
||||
q: str4(),
|
||||
limit: num4(),
|
||||
offset: num4()
|
||||
}, getBizMonitorDiffs, getClient, t),
|
||||
tool2("netx__getBizCollectBatch", "One biz_state collect batch: commands (raw_command, parse_status, preview), parsed metrics.", {
|
||||
batch_id: { type: "string", required: true }
|
||||
}, getBizCollectBatch, getClient, t),
|
||||
tool2("netx__getBizCollectCommandRaw", "Full CLI raw_text for one collect command inside a batch (deep dive).", {
|
||||
batch_id: { type: "string", required: true },
|
||||
command_id: { type: "string", required: true }
|
||||
}, getBizCollectCommandRaw, getClient, t),
|
||||
tool2("netx__getTopologyTree", "Get topology folder tree (nav roots + Root map canvases). Start here before createTopologyFolder.", { compact: bool4(), max_depth: num4() }, getTopologyTree, getClient, t),
|
||||
tool2("netx__getTopologyView", "Get a topology view by view_id. Default detail=summary with sample_nodes + links[].", {
|
||||
view_id: { type: "string", required: true },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
bizMonitor: Object.freeze({ inPreset: true, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
ops: Object.freeze([
|
||||
|
|
@ -37,15 +38,25 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__suggestSinkHubs",
|
||||
"netx__analyzeTopologyViewLayout",
|
||||
"netx__sinkTopologyDualUnits"
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
"netx__getBizMonitorContext",
|
||||
"netx__getBizMonitorBoard",
|
||||
"netx__listBizMonitorReds",
|
||||
"netx__getBizMonitorDiffs",
|
||||
"netx__getBizCollectBatch",
|
||||
"netx__getBizCollectCommandRaw"
|
||||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
topology: "topology",
|
||||
bizMonitor: "biz-monitor"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"ops",
|
||||
"topology"
|
||||
"topology",
|
||||
"bizMonitor"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
const policy = groups ?? DEFAULT_CAPABILITY_GROUPS;
|
||||
|
|
@ -60,12 +71,12 @@ function groupsForced(only) {
|
|||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
const out = new Set;
|
||||
for (const id of groupIds) {
|
||||
for (const tool of TOOLS_BY_GROUP[id])
|
||||
names.add(tool);
|
||||
for (const name of TOOLS_BY_GROUP[id] ?? [])
|
||||
out.add(name);
|
||||
}
|
||||
return names;
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/netx/group-skills.ts
|
||||
|
|
@ -1458,6 +1469,68 @@ async function findTopologyPaths(client, args, signal) {
|
|||
body.to_managed_ne_id = toMid;
|
||||
return client.post("/v1/topology/fabric/paths", body, signal, 30000);
|
||||
}
|
||||
async function getBizMonitorContext(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
const taskId = str2(args, "task_id").trim();
|
||||
if (!projectId && !taskId) {
|
||||
return { ok: false, error: "project_id_or_task_id_required" };
|
||||
}
|
||||
const params = {};
|
||||
if (projectId)
|
||||
params.project_id = projectId;
|
||||
if (taskId)
|
||||
params.task_id = taskId;
|
||||
return client.get("/v1/biz-migration/monitor-context", params, signal);
|
||||
}
|
||||
async function getBizMonitorBoard(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
const params = {};
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (runId)
|
||||
params.run_id = runId;
|
||||
return client.get(`/v1/biz-migration/batches/${encodeURIComponent(batchId)}/board`, params, signal);
|
||||
}
|
||||
async function listBizMonitorReds(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
if (!projectId)
|
||||
return { ok: false, error: "project_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["status"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
return client.get(`/v1/biz-migration/projects/${encodeURIComponent(projectId)}/red-tickets`, params, signal);
|
||||
}
|
||||
async function getBizMonitorDiffs(client, args, signal) {
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (!runId)
|
||||
return { ok: false, error: "run_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["metric_id", "sheet_id", "verdict", "color", "q"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
const offset = num2(args, "offset");
|
||||
if (offset !== undefined)
|
||||
params.offset = clampInt(offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
||||
return client.get(`/v1/biz-migration/runs/${encodeURIComponent(runId)}/diffs`, params, signal);
|
||||
}
|
||||
async function getBizCollectBatch(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}`, {}, signal);
|
||||
}
|
||||
async function getBizCollectCommandRaw(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
const commandId = str2(args, "command_id").trim();
|
||||
if (!batchId || !commandId) {
|
||||
return { ok: false, error: "batch_id_and_command_id_required" };
|
||||
}
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}/commands/${encodeURIComponent(commandId)}`, {}, signal);
|
||||
}
|
||||
|
||||
// src/netx/topology-handlers.ts
|
||||
function asRecord2(value) {
|
||||
|
|
@ -2282,6 +2355,36 @@ 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.", {
|
||||
project_id: str4("Cutover / biz-migration project id"),
|
||||
task_id: str4("biz_state task id (optional if project_id set)")
|
||||
}, getBizMonitorContext, getClient, t),
|
||||
tool2("netx__getBizMonitorBoard", "Cutover batch board: project/batch status, evaluate run summary, sheet cards, verdict counts, metric_batches.", {
|
||||
batch_id: { type: "string", required: true },
|
||||
run_id: str4("Optional evaluate run id")
|
||||
}, getBizMonitorBoard, getClient, t),
|
||||
tool2("netx__listBizMonitorReds", "Open/carried red tickets. Each item has old_key/new_key (raw A/B) and evidence (device, collect time, show command, iface raw→norm→map).", {
|
||||
project_id: { type: "string", required: true },
|
||||
status: str4("open|carried|resolved or empty=all"),
|
||||
limit: num4()
|
||||
}, listBizMonitorReds, getClient, t),
|
||||
tool2("netx__getBizMonitorDiffs", "Paged evaluate diffs. Rows expose old_key/new_key (raw A/B), match_* keys, status, evidence with show commands.", {
|
||||
run_id: { type: "string", required: true },
|
||||
metric_id: str4(),
|
||||
sheet_id: str4(),
|
||||
verdict: str4(),
|
||||
color: str4("e.g. red"),
|
||||
q: str4(),
|
||||
limit: num4(),
|
||||
offset: num4()
|
||||
}, getBizMonitorDiffs, getClient, t),
|
||||
tool2("netx__getBizCollectBatch", "One biz_state collect batch: commands (raw_command, parse_status, preview), parsed metrics.", {
|
||||
batch_id: { type: "string", required: true }
|
||||
}, getBizCollectBatch, getClient, t),
|
||||
tool2("netx__getBizCollectCommandRaw", "Full CLI raw_text for one collect command inside a batch (deep dive).", {
|
||||
batch_id: { type: "string", required: true },
|
||||
command_id: { type: "string", required: true }
|
||||
}, getBizCollectCommandRaw, getClient, t),
|
||||
tool2("netx__getTopologyTree", "Get topology folder tree (nav roots + Root map canvases). Start here before createTopologyFolder.", { compact: bool4(), max_depth: num4() }, getTopologyTree, getClient, t),
|
||||
tool2("netx__getTopologyView", "Get a topology view by view_id. Default detail=summary with sample_nodes + links[].", {
|
||||
view_id: { type: "string", required: true },
|
||||
|
|
|
|||
|
|
@ -1451,6 +1451,20 @@ function NetxopsCard(props) {
|
|||
onEditPublic: (checked) => {
|
||||
props.edit("groupTopologyPublic", checked ? "true" : "false");
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(CapabilityGroupBlock, {
|
||||
title: t("groupBizMonitor"),
|
||||
inPresetLabel: t("groupInPreset"),
|
||||
publicLabel: t("groupPublic"),
|
||||
inPreset: state.groupBizMonitorInPreset,
|
||||
published: state.groupBizMonitorPublic,
|
||||
disabled,
|
||||
onEditInPreset: (checked) => {
|
||||
props.edit("groupBizMonitorInPreset", checked ? "true" : "false");
|
||||
},
|
||||
onEditPublic: (checked) => {
|
||||
props.edit("groupBizMonitorPublic", checked ? "true" : "false");
|
||||
}
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
|
@ -1995,6 +2009,8 @@ class NetxopsCardController {
|
|||
booleanField("groupOpsPublic"),
|
||||
booleanField("groupTopologyInPreset"),
|
||||
booleanField("groupTopologyPublic"),
|
||||
booleanFieldPersistFalse("groupBizMonitorInPreset"),
|
||||
booleanField("groupBizMonitorPublic"),
|
||||
booleanFieldPersistFalse("groupKbInPreset"),
|
||||
booleanField("groupKbPublic"),
|
||||
booleanField("alarmPushEnabled"),
|
||||
|
|
@ -2312,6 +2328,8 @@ class NetxopsCardController {
|
|||
groupOpsPublic: this.form.field("groupOpsPublic"),
|
||||
groupTopologyInPreset: this.form.field("groupTopologyInPreset"),
|
||||
groupTopologyPublic: this.form.field("groupTopologyPublic"),
|
||||
groupBizMonitorInPreset: this.form.field("groupBizMonitorInPreset"),
|
||||
groupBizMonitorPublic: this.form.field("groupBizMonitorPublic"),
|
||||
groupKbInPreset: this.form.field("groupKbInPreset"),
|
||||
groupKbPublic: this.form.field("groupKbPublic"),
|
||||
alarmPushEnabled: this.form.field("alarmPushEnabled"),
|
||||
|
|
@ -2471,6 +2489,7 @@ var en = {
|
|||
nmsProviderHint: "zte-ume",
|
||||
groupOps: "ops",
|
||||
groupTopology: "topology",
|
||||
groupBizMonitor: "bizMonitor",
|
||||
groupKb: "Knowledge skills",
|
||||
groupInPreset: "In Netx Ops preset",
|
||||
groupPublic: "Publish to other presets",
|
||||
|
|
@ -2548,6 +2567,7 @@ var zh = {
|
|||
nmsProviderHint: "zte-ume",
|
||||
groupOps: "ops",
|
||||
groupTopology: "topology",
|
||||
groupBizMonitor: "bizMonitor",
|
||||
groupKb: "知识库技能",
|
||||
groupInPreset: "在 Netx Ops 预设中启用",
|
||||
groupPublic: "对其他预设公开",
|
||||
|
|
|
|||
127
lib/index.js
127
lib/index.js
|
|
@ -572,7 +572,8 @@ async function dispatchAlarmToSinks(ctx, payload, options, sinks = {}) {
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
bizMonitor: Object.freeze({ inPreset: true, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
ops: Object.freeze([
|
||||
|
|
@ -608,15 +609,25 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__suggestSinkHubs",
|
||||
"netx__analyzeTopologyViewLayout",
|
||||
"netx__sinkTopologyDualUnits"
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
"netx__getBizMonitorContext",
|
||||
"netx__getBizMonitorBoard",
|
||||
"netx__listBizMonitorReds",
|
||||
"netx__getBizMonitorDiffs",
|
||||
"netx__getBizCollectBatch",
|
||||
"netx__getBizCollectCommandRaw"
|
||||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
topology: "topology",
|
||||
bizMonitor: "biz-monitor"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"ops",
|
||||
"topology"
|
||||
"topology",
|
||||
"bizMonitor"
|
||||
]);
|
||||
function capabilityGroupsFromSettings(fields) {
|
||||
const src = fields ?? {};
|
||||
|
|
@ -633,6 +644,10 @@ function capabilityGroupsFromSettings(fields) {
|
|||
opsInPreset = legacy.some((v) => v === true);
|
||||
}
|
||||
}
|
||||
let bizMonitorInPreset = true;
|
||||
if (src.groupBizMonitorInPreset !== undefined) {
|
||||
bizMonitorInPreset = src.groupBizMonitorInPreset !== false;
|
||||
}
|
||||
return {
|
||||
ops: {
|
||||
inPreset: opsInPreset,
|
||||
|
|
@ -641,6 +656,10 @@ function capabilityGroupsFromSettings(fields) {
|
|||
topology: {
|
||||
inPreset: src.groupTopologyInPreset === true || src.groupTopologyLayoutInPreset === true,
|
||||
public: src.groupTopologyPublic === true || src.groupTopologyLayoutPublic === true
|
||||
},
|
||||
bizMonitor: {
|
||||
inPreset: bizMonitorInPreset,
|
||||
public: src.groupBizMonitorPublic === true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -653,12 +672,12 @@ function groupsForPlane(groups, plane, only) {
|
|||
return enabled.filter((id) => allow.has(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
const out = new Set;
|
||||
for (const id of groupIds) {
|
||||
for (const tool of TOOLS_BY_GROUP[id])
|
||||
names.add(tool);
|
||||
for (const name of TOOLS_BY_GROUP[id] ?? [])
|
||||
out.add(name);
|
||||
}
|
||||
return names;
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/netx/group-skills.ts
|
||||
|
|
@ -3709,6 +3728,68 @@ async function findTopologyPaths(client, args, signal) {
|
|||
body.to_managed_ne_id = toMid;
|
||||
return client.post("/v1/topology/fabric/paths", body, signal, 30000);
|
||||
}
|
||||
async function getBizMonitorContext(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
const taskId = str2(args, "task_id").trim();
|
||||
if (!projectId && !taskId) {
|
||||
return { ok: false, error: "project_id_or_task_id_required" };
|
||||
}
|
||||
const params = {};
|
||||
if (projectId)
|
||||
params.project_id = projectId;
|
||||
if (taskId)
|
||||
params.task_id = taskId;
|
||||
return client.get("/v1/biz-migration/monitor-context", params, signal);
|
||||
}
|
||||
async function getBizMonitorBoard(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
const params = {};
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (runId)
|
||||
params.run_id = runId;
|
||||
return client.get(`/v1/biz-migration/batches/${encodeURIComponent(batchId)}/board`, params, signal);
|
||||
}
|
||||
async function listBizMonitorReds(client, args, signal) {
|
||||
const projectId = str2(args, "project_id").trim();
|
||||
if (!projectId)
|
||||
return { ok: false, error: "project_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["status"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
return client.get(`/v1/biz-migration/projects/${encodeURIComponent(projectId)}/red-tickets`, params, signal);
|
||||
}
|
||||
async function getBizMonitorDiffs(client, args, signal) {
|
||||
const runId = str2(args, "run_id").trim();
|
||||
if (!runId)
|
||||
return { ok: false, error: "run_id_required" };
|
||||
const params = {};
|
||||
putStr(params, args, ["metric_id", "sheet_id", "verdict", "color", "q"]);
|
||||
const limit = num2(args, "limit");
|
||||
if (limit !== undefined)
|
||||
params.limit = clampInt(limit, 200, 1, 500);
|
||||
const offset = num2(args, "offset");
|
||||
if (offset !== undefined)
|
||||
params.offset = clampInt(offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
||||
return client.get(`/v1/biz-migration/runs/${encodeURIComponent(runId)}/diffs`, params, signal);
|
||||
}
|
||||
async function getBizCollectBatch(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
if (!batchId)
|
||||
return { ok: false, error: "batch_id_required" };
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}`, {}, signal);
|
||||
}
|
||||
async function getBizCollectCommandRaw(client, args, signal) {
|
||||
const batchId = str2(args, "batch_id").trim();
|
||||
const commandId = str2(args, "command_id").trim();
|
||||
if (!batchId || !commandId) {
|
||||
return { ok: false, error: "batch_id_and_command_id_required" };
|
||||
}
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}/commands/${encodeURIComponent(commandId)}`, {}, signal);
|
||||
}
|
||||
|
||||
// src/netx/topology-handlers.ts
|
||||
function asRecord2(value) {
|
||||
|
|
@ -4533,6 +4614,36 @@ 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.", {
|
||||
project_id: str4("Cutover / biz-migration project id"),
|
||||
task_id: str4("biz_state task id (optional if project_id set)")
|
||||
}, getBizMonitorContext, getClient, t),
|
||||
tool2("netx__getBizMonitorBoard", "Cutover batch board: project/batch status, evaluate run summary, sheet cards, verdict counts, metric_batches.", {
|
||||
batch_id: { type: "string", required: true },
|
||||
run_id: str4("Optional evaluate run id")
|
||||
}, getBizMonitorBoard, getClient, t),
|
||||
tool2("netx__listBizMonitorReds", "Open/carried red tickets. Each item has old_key/new_key (raw A/B) and evidence (device, collect time, show command, iface raw→norm→map).", {
|
||||
project_id: { type: "string", required: true },
|
||||
status: str4("open|carried|resolved or empty=all"),
|
||||
limit: num4()
|
||||
}, listBizMonitorReds, getClient, t),
|
||||
tool2("netx__getBizMonitorDiffs", "Paged evaluate diffs. Rows expose old_key/new_key (raw A/B), match_* keys, status, evidence with show commands.", {
|
||||
run_id: { type: "string", required: true },
|
||||
metric_id: str4(),
|
||||
sheet_id: str4(),
|
||||
verdict: str4(),
|
||||
color: str4("e.g. red"),
|
||||
q: str4(),
|
||||
limit: num4(),
|
||||
offset: num4()
|
||||
}, getBizMonitorDiffs, getClient, t),
|
||||
tool2("netx__getBizCollectBatch", "One biz_state collect batch: commands (raw_command, parse_status, preview), parsed metrics.", {
|
||||
batch_id: { type: "string", required: true }
|
||||
}, getBizCollectBatch, getClient, t),
|
||||
tool2("netx__getBizCollectCommandRaw", "Full CLI raw_text for one collect command inside a batch (deep dive).", {
|
||||
batch_id: { type: "string", required: true },
|
||||
command_id: { type: "string", required: true }
|
||||
}, getBizCollectCommandRaw, getClient, t),
|
||||
tool2("netx__getTopologyTree", "Get topology folder tree (nav roots + Root map canvases). Start here before createTopologyFolder.", { compact: bool4(), max_depth: num4() }, getTopologyTree, getClient, t),
|
||||
tool2("netx__getTopologyView", "Get a topology view by view_id. Default detail=summary with sample_nodes + links[].", {
|
||||
view_id: { type: "string", required: true },
|
||||
|
|
@ -4734,6 +4845,8 @@ var Config = z.object({
|
|||
groupOpsPublic: z.boolean().default(false),
|
||||
groupTopologyInPreset: z.boolean().default(false),
|
||||
groupTopologyPublic: z.boolean().default(false),
|
||||
groupBizMonitorInPreset: z.boolean().default(true),
|
||||
groupBizMonitorPublic: z.boolean().default(false),
|
||||
kbRoot: z.string().default(""),
|
||||
groupKbInPreset: z.boolean().default(true),
|
||||
groupKbPublic: z.boolean().default(false)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dsh-netxops",
|
||||
"version": "0.1.39",
|
||||
"version": "0.1.40",
|
||||
"description": "DeepSeek Harness Netx Ops: ops/topology + IM delivery + session export + knowledge-base MANIFEST/_skills + localSkills",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
"./tools-nms": "./lib/agent-tools-nms.js",
|
||||
"./tools-common": "./lib/agent-tools-common.js",
|
||||
"./tools-topology": "./lib/agent-tools-topology.js",
|
||||
"./tools-biz-monitor": "./lib/agent-tools-biz-monitor.js",
|
||||
"./client": "./lib/client.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
"lib/agent-tools-nms.js",
|
||||
"lib/agent-tools-common.js",
|
||||
"lib/agent-tools-topology.js",
|
||||
"lib/agent-tools-biz-monitor.js",
|
||||
"lib/client.js",
|
||||
"presets/",
|
||||
"cordis.patch.yml",
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# Mirrored from netx/skills — do not long-edit here
|
||||
# Mirrored from netx/skills 鈥?do not long-edit here
|
||||
|
||||
Canonical: `D:\project\chatgpt\netx\skills`
|
||||
|
||||
```powershell
|
||||
`powershell
|
||||
powershell -File .\scripts\sync-skills-from-netx.ps1
|
||||
```
|
||||
`
|
||||
|
||||
Runtime prefers `NETX_SKILLS_ROOT` or sibling `../netx/skills`.
|
||||
Groups: **ops** (`netx-ops`) + **topology** (`netx-topology`). One group ↔ one skill.
|
||||
Groups: **ops** (`netx-ops`) + **topology** (`netx-topology`). One group 鈫?one skill.
|
||||
|
||||
**Keep netx-ops pure netx** (alarms / inventory / CLI). Knowledge-base triage belongs in
|
||||
operator-subset `_skills/kb-*` packs registered by the plugin — do **not** write KB linkage
|
||||
operator-subset `_skills/kb-*` packs registered by the plugin 鈥?do **not** write KB linkage
|
||||
back into `netx-ops`.
|
||||
|
|
|
|||
61
presets/netxops/skills/biz-monitor/netx-biz-monitor/SKILL.md
Normal file
61
presets/netxops/skills/biz-monitor/netx-biz-monitor/SKILL.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
name: netx-biz-monitor
|
||||
description: >-
|
||||
Overnight cutover / biz_state monitor analysis via netxops host tools
|
||||
(netx__getBizMonitor*). Given a project_id or task_id, read definitions,
|
||||
board, reds/diffs with evidence (device-raw A/B, show commands), and judge
|
||||
tool false-positive vs real business fault. Does not create templates/tasks.
|
||||
---
|
||||
|
||||
# netx-biz-monitor(割接 / 业务监控只读分析)
|
||||
|
||||
人配好监控与任务;AI 当晚用 **project_id**(或 task_id)做归因。
|
||||
工具在 **netxops**(`netx__*`),直连 netx REST,**不是** netx-mcp。
|
||||
|
||||
## 工具(能力组 `bizMonitor`)
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `netx__getBizMonitorContext` | 任务定义:项目、监控/对比模板、归一化规则、端口映射、关联任务与命令项 |
|
||||
| `netx__getBizMonitorBoard` | 批次进度、evaluate run、sheet 卡、verdict 统计 |
|
||||
| `netx__listBizMonitorReds` | 红单 + **evidence** |
|
||||
| `netx__getBizMonitorDiffs` | run 明细(可滤 color=red) |
|
||||
| `netx__getBizCollectBatch` | 某次采集:commands / metrics |
|
||||
| `netx__getBizCollectCommandRaw` | 单命令全量 show 输出 |
|
||||
|
||||
交叉验证真障时再用 ops:`netx__execManagedNe` / 拓扑路径。
|
||||
|
||||
## 关键约定(必守)
|
||||
|
||||
- **`old_key` / `new_key`**:设备采集原文 **A/B**(大屏与结论用这个)
|
||||
- **`match_*` / evidence.iface.normalized|map_***:内部配对,**不要**把映射后的 BB 说成两侧端口
|
||||
- **evidence.*.command.raw_command**:实际 show 命令
|
||||
- **evidence.*.device / collect**:哪台设备、何时采的
|
||||
|
||||
## 推荐顺序
|
||||
|
||||
1. `getBizMonitorContext(project_id)` — 弄清盯什么
|
||||
2. `getBizMonitorBoard(batch_id)` — 进度 / missing / skipped
|
||||
3. `listBizMonitorReds` 或 `getBizMonitorDiffs(color=red)`
|
||||
4. 看 evidence:parse 失败?current_missing?映射 miss?归一化误伤?
|
||||
5. 需要原文 → `getBizCollectCommandRaw`
|
||||
6. 像真障 → `execManagedNe` 现场核对
|
||||
|
||||
## Verdict 速查(skill 解释,工具不硬编码)
|
||||
|
||||
| verdict | 含义(窗口内) |
|
||||
|---------|----------------|
|
||||
| migrated | expect 项已迁到新侧 |
|
||||
| anomaly | 异常(多为红) |
|
||||
| unfinished | expect 未完成(验收常红) |
|
||||
| migrating / expected / ok | 过程态 / 正常 |
|
||||
| lost / not_involved | 丢失或无关 |
|
||||
|
||||
## 误报优先查
|
||||
|
||||
1. `parse_status` 非 ok / 行数为 0
|
||||
2. board:`current_missing` / `collect_skipped`
|
||||
3. 端口映射漏配、归一化前后不一致导致假 anomaly
|
||||
4. 再谈真实业务问题
|
||||
|
||||
配置整改与关红单由人做;本 skill **只读分析**。
|
||||
|
|
@ -34,6 +34,7 @@ const entries = [
|
|||
{ entry: join(root, 'src/agent-tools-nms.ts'), out: join(outDir, 'agent-tools-nms.js') },
|
||||
{ entry: join(root, 'src/agent-tools-common.ts'), out: join(outDir, 'agent-tools-common.js') },
|
||||
{ entry: join(root, 'src/agent-tools-topology.ts'), out: join(outDir, 'agent-tools-topology.js') },
|
||||
{ entry: join(root, 'src/agent-tools-biz-monitor.ts'), out: join(outDir, 'agent-tools-biz-monitor.js') },
|
||||
]
|
||||
|
||||
mkdirSync(outDir, { recursive: true })
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ $src = Join-Path $NetxRoot "skills"
|
|||
$dst = Join-Path $opsRoot "presets\netxops\skills"
|
||||
if (-not (Test-Path $src)) { throw "missing $src" }
|
||||
|
||||
foreach ($group in @("ops", "topology")) {
|
||||
foreach ($group in @("ops", "topology", "biz-monitor")) {
|
||||
$from = Join-Path $src $group
|
||||
$to = Join-Path $dst $group
|
||||
if (-not (Test-Path $from)) { throw "missing group $from" }
|
||||
|
|
|
|||
15
src/agent-tools-biz-monitor.ts
Normal file
15
src/agent-tools-biz-monitor.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Forced bizMonitor-group tools + skill (cutover / biz_state read analysis).
|
||||
*
|
||||
* @module dsh-netxops/tools-biz-monitor
|
||||
*/
|
||||
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { applyGroupToolsPlugin } from './netx/group-tools-plugin.ts'
|
||||
|
||||
export const name = 'netxops-tools-biz-monitor'
|
||||
export const inject = ['tools']
|
||||
|
||||
export function apply(ctx: Context): void {
|
||||
applyGroupToolsPlugin(ctx, { name, mode: 'forced', only: ['bizMonitor'] })
|
||||
}
|
||||
|
|
@ -564,6 +564,20 @@ export function NetxopsCard(props: NetxopsCardProps) {
|
|||
props.edit('groupTopologyPublic', checked ? 'true' : 'false')
|
||||
}}
|
||||
/>
|
||||
<CapabilityGroupBlock
|
||||
title={t('groupBizMonitor')}
|
||||
inPresetLabel={t('groupInPreset')}
|
||||
publicLabel={t('groupPublic')}
|
||||
inPreset={state.groupBizMonitorInPreset}
|
||||
published={state.groupBizMonitorPublic}
|
||||
disabled={disabled}
|
||||
onEditInPreset={(checked) => {
|
||||
props.edit('groupBizMonitorInPreset', checked ? 'true' : 'false')
|
||||
}}
|
||||
onEditPublic={(checked) => {
|
||||
props.edit('groupBizMonitorPublic', checked ? 'true' : 'false')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="dsh-nx-settings-card">
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ export interface NetxopsSettings {
|
|||
groupOpsPublic?: boolean
|
||||
groupTopologyInPreset?: boolean
|
||||
groupTopologyPublic?: boolean
|
||||
groupBizMonitorInPreset?: boolean
|
||||
groupBizMonitorPublic?: boolean
|
||||
nmsProvider?: string
|
||||
/** Operator-subset knowledge package root (Host-local path). */
|
||||
kbRoot?: string
|
||||
|
|
@ -82,6 +84,8 @@ export interface NetxopsCardState extends CardShell {
|
|||
groupOpsPublic: CardFieldState
|
||||
groupTopologyInPreset: CardFieldState
|
||||
groupTopologyPublic: CardFieldState
|
||||
groupBizMonitorInPreset: CardFieldState
|
||||
groupBizMonitorPublic: CardFieldState
|
||||
nmsProvider: CardFieldState
|
||||
kbRoot: CardFieldState
|
||||
groupKbInPreset: CardFieldState
|
||||
|
|
@ -187,6 +191,8 @@ export class NetxopsCardController {
|
|||
booleanField('groupOpsPublic'),
|
||||
booleanField('groupTopologyInPreset'),
|
||||
booleanField('groupTopologyPublic'),
|
||||
booleanFieldPersistFalse('groupBizMonitorInPreset'),
|
||||
booleanField('groupBizMonitorPublic'),
|
||||
booleanFieldPersistFalse('groupKbInPreset'),
|
||||
booleanField('groupKbPublic'),
|
||||
booleanField('alarmPushEnabled'),
|
||||
|
|
@ -599,6 +605,8 @@ export class NetxopsCardController {
|
|||
groupOpsPublic: this.form.field('groupOpsPublic'),
|
||||
groupTopologyInPreset: this.form.field('groupTopologyInPreset'),
|
||||
groupTopologyPublic: this.form.field('groupTopologyPublic'),
|
||||
groupBizMonitorInPreset: this.form.field('groupBizMonitorInPreset'),
|
||||
groupBizMonitorPublic: this.form.field('groupBizMonitorPublic'),
|
||||
groupKbInPreset: this.form.field('groupKbInPreset'),
|
||||
groupKbPublic: this.form.field('groupKbPublic'),
|
||||
alarmPushEnabled: this.form.field('alarmPushEnabled'),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export type NetxopsLocaleKey =
|
|||
| 'nmsProviderHint'
|
||||
| 'groupOps'
|
||||
| 'groupTopology'
|
||||
| 'groupBizMonitor'
|
||||
| 'groupKb'
|
||||
| 'groupInPreset'
|
||||
| 'groupPublic'
|
||||
|
|
@ -98,6 +99,7 @@ export const en: Record<NetxopsLocaleKey, string> = {
|
|||
nmsProviderHint: 'zte-ume',
|
||||
groupOps: 'ops',
|
||||
groupTopology: 'topology',
|
||||
groupBizMonitor: 'bizMonitor',
|
||||
groupKb: 'Knowledge skills',
|
||||
groupInPreset: 'In Netx Ops preset',
|
||||
groupPublic: 'Publish to other presets',
|
||||
|
|
@ -176,6 +178,7 @@ export const zh: Record<NetxopsLocaleKey, string> = {
|
|||
nmsProviderHint: 'zte-ume',
|
||||
groupOps: 'ops',
|
||||
groupTopology: 'topology',
|
||||
groupBizMonitor: 'bizMonitor',
|
||||
groupKb: '知识库技能',
|
||||
groupInPreset: '在 Netx Ops 预设中启用',
|
||||
groupPublic: '对其他预设公开',
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ export interface Config {
|
|||
groupTopologyInPreset: boolean
|
||||
/** Publish topology tools/skills to other presets (default off). */
|
||||
groupTopologyPublic: boolean
|
||||
/** biz-monitor (cutover / biz_state read) tools in the Netx Ops preset (default on). */
|
||||
groupBizMonitorInPreset: boolean
|
||||
/** Publish biz-monitor tools/skills to other presets (default off). */
|
||||
groupBizMonitorPublic: boolean
|
||||
/**
|
||||
* Absolute (or Host-local) path to an operator-subset knowledge package.
|
||||
* Empty = unconfigured. Plugin locates `MANIFEST.json` (≤3 levels) and
|
||||
|
|
@ -183,6 +187,8 @@ export const Config: z<Config> = z.object({
|
|||
groupOpsPublic: z.boolean().default(false),
|
||||
groupTopologyInPreset: z.boolean().default(false),
|
||||
groupTopologyPublic: z.boolean().default(false),
|
||||
groupBizMonitorInPreset: z.boolean().default(true),
|
||||
groupBizMonitorPublic: z.boolean().default(false),
|
||||
kbRoot: z.string().default(''),
|
||||
groupKbInPreset: z.boolean().default(true),
|
||||
groupKbPublic: z.boolean().default(false),
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@
|
|||
*
|
||||
* - `ops` → skill `netx-ops` — NMS alarms/inventory/SQL + managed CLI + findTopologyPaths
|
||||
* - `topology` → skill `netx-topology` — canvas / fabric / dual_unit / layout
|
||||
* - `bizMonitor` → skill `netx-biz-monitor` — cutover / biz_state read + overnight analysis
|
||||
*/
|
||||
|
||||
/** Stable group ids used in settings, skill dirs, and registration filters. */
|
||||
export type NetxCapabilityGroupId = 'ops' | 'topology'
|
||||
export type NetxCapabilityGroupId = 'ops' | 'topology' | 'bizMonitor'
|
||||
|
||||
/** Per-group exposure knobs. */
|
||||
export interface NetxGroupExposure {
|
||||
|
|
@ -27,14 +28,17 @@ export interface NetxCapabilityGroupSettingsFields {
|
|||
groupOpsPublic: boolean
|
||||
groupTopologyInPreset: boolean
|
||||
groupTopologyPublic: boolean
|
||||
groupBizMonitorInPreset: boolean
|
||||
groupBizMonitorPublic: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Default: ops in Ops preset; topology and all public off.
|
||||
* Default: ops in Ops preset; topology / bizMonitor and all public off.
|
||||
*/
|
||||
export const DEFAULT_CAPABILITY_GROUPS: NetxCapabilityGroups = Object.freeze({
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
bizMonitor: Object.freeze({ inPreset: true, public: false }),
|
||||
})
|
||||
|
||||
/**
|
||||
|
|
@ -76,17 +80,27 @@ export const TOOLS_BY_GROUP: Readonly<Record<NetxCapabilityGroupId, readonly str
|
|||
'netx__analyzeTopologyViewLayout',
|
||||
'netx__sinkTopologyDualUnits',
|
||||
]),
|
||||
bizMonitor: Object.freeze([
|
||||
'netx__getBizMonitorContext',
|
||||
'netx__getBizMonitorBoard',
|
||||
'netx__listBizMonitorReds',
|
||||
'netx__getBizMonitorDiffs',
|
||||
'netx__getBizCollectBatch',
|
||||
'netx__getBizCollectCommandRaw',
|
||||
]),
|
||||
})
|
||||
|
||||
/** Skill directory name under skills root / `presets/netxops/skills/<group>/`. */
|
||||
export const SKILL_DIR_BY_GROUP: Readonly<Record<NetxCapabilityGroupId, string>> = Object.freeze({
|
||||
ops: 'ops',
|
||||
topology: 'topology',
|
||||
bizMonitor: 'biz-monitor',
|
||||
})
|
||||
|
||||
export const CAPABILITY_GROUP_IDS: readonly NetxCapabilityGroupId[] = Object.freeze([
|
||||
'ops',
|
||||
'topology',
|
||||
'bizMonitor',
|
||||
])
|
||||
|
||||
/**
|
||||
|
|
@ -126,6 +140,11 @@ export function capabilityGroupsFromSettings(
|
|||
}
|
||||
}
|
||||
|
||||
let bizMonitorInPreset = true
|
||||
if (src.groupBizMonitorInPreset !== undefined) {
|
||||
bizMonitorInPreset = src.groupBizMonitorInPreset !== false
|
||||
}
|
||||
|
||||
return {
|
||||
ops: {
|
||||
inPreset: opsInPreset,
|
||||
|
|
@ -140,6 +159,10 @@ export function capabilityGroupsFromSettings(
|
|||
public: src.groupTopologyPublic === true
|
||||
|| src.groupTopologyLayoutPublic === true,
|
||||
},
|
||||
bizMonitor: {
|
||||
inPreset: bizMonitorInPreset,
|
||||
public: src.groupBizMonitorPublic === true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,9 +200,9 @@ export function groupsForced(
|
|||
* Tool name allow-list for the given groups.
|
||||
*/
|
||||
export function toolNamesForGroups(groupIds: readonly NetxCapabilityGroupId[]): Set<string> {
|
||||
const names = new Set<string>()
|
||||
const out = new Set<string>()
|
||||
for (const id of groupIds) {
|
||||
for (const tool of TOOLS_BY_GROUP[id]) names.add(tool)
|
||||
for (const name of TOOLS_BY_GROUP[id] ?? []) out.add(name)
|
||||
}
|
||||
return names
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -330,3 +330,101 @@ export async function findTopologyPaths(client: NetxClient, args: NetxJson, sign
|
|||
else body.to_managed_ne_id = toMid
|
||||
return client.post('/v1/topology/fabric/paths', body, signal, 30_000)
|
||||
}
|
||||
|
||||
// ── bizMonitor group (cutover / biz_state read) ─────────────────────────────
|
||||
|
||||
/** Fat cutover / biz_state definition bundle (templates, mapping, tasks). */
|
||||
export async function getBizMonitorContext(
|
||||
client: NetxClient,
|
||||
args: NetxJson,
|
||||
signal?: AbortSignal,
|
||||
): Promise<NetxJson> {
|
||||
const projectId = str(args, 'project_id').trim()
|
||||
const taskId = str(args, 'task_id').trim()
|
||||
if (!projectId && !taskId) {
|
||||
return { ok: false, error: 'project_id_or_task_id_required' }
|
||||
}
|
||||
const params: Record<string, string | number | boolean> = {}
|
||||
if (projectId) params.project_id = projectId
|
||||
if (taskId) params.task_id = taskId
|
||||
return client.get('/v1/biz-migration/monitor-context', params, signal)
|
||||
}
|
||||
|
||||
/** Cutover batch board + latest evaluate run summary. */
|
||||
export async function getBizMonitorBoard(
|
||||
client: NetxClient,
|
||||
args: NetxJson,
|
||||
signal?: AbortSignal,
|
||||
): Promise<NetxJson> {
|
||||
const batchId = str(args, 'batch_id').trim()
|
||||
if (!batchId) return { ok: false, error: 'batch_id_required' }
|
||||
const params: Record<string, string | number | boolean> = {}
|
||||
const runId = str(args, 'run_id').trim()
|
||||
if (runId) params.run_id = runId
|
||||
return client.get(`/v1/biz-migration/batches/${encodeURIComponent(batchId)}/board`, params, signal)
|
||||
}
|
||||
|
||||
/** Red tickets with evidence (raw A/B + show command). */
|
||||
export async function listBizMonitorReds(
|
||||
client: NetxClient,
|
||||
args: NetxJson,
|
||||
signal?: AbortSignal,
|
||||
): Promise<NetxJson> {
|
||||
const projectId = str(args, 'project_id').trim()
|
||||
if (!projectId) return { ok: false, error: 'project_id_required' }
|
||||
const params: Record<string, string | number | boolean> = {}
|
||||
putStr(params, args, ['status'])
|
||||
const limit = num(args, 'limit')
|
||||
if (limit !== undefined) params.limit = clampInt(limit, 200, 1, 500)
|
||||
return client.get(
|
||||
`/v1/biz-migration/projects/${encodeURIComponent(projectId)}/red-tickets`,
|
||||
params,
|
||||
signal,
|
||||
)
|
||||
}
|
||||
|
||||
/** Paged evaluate diffs (old_key/new_key = device-raw A/B). */
|
||||
export async function getBizMonitorDiffs(
|
||||
client: NetxClient,
|
||||
args: NetxJson,
|
||||
signal?: AbortSignal,
|
||||
): Promise<NetxJson> {
|
||||
const runId = str(args, 'run_id').trim()
|
||||
if (!runId) return { ok: false, error: 'run_id_required' }
|
||||
const params: Record<string, string | number | boolean> = {}
|
||||
putStr(params, args, ['metric_id', 'sheet_id', 'verdict', 'color', 'q'])
|
||||
const limit = num(args, 'limit')
|
||||
if (limit !== undefined) params.limit = clampInt(limit, 200, 1, 500)
|
||||
const offset = num(args, 'offset')
|
||||
if (offset !== undefined) params.offset = clampInt(offset, 0, 0, Number.MAX_SAFE_INTEGER)
|
||||
return client.get(`/v1/biz-migration/runs/${encodeURIComponent(runId)}/diffs`, params, signal)
|
||||
}
|
||||
|
||||
/** One biz_state collect batch (commands + metrics). */
|
||||
export async function getBizCollectBatch(
|
||||
client: NetxClient,
|
||||
args: NetxJson,
|
||||
signal?: AbortSignal,
|
||||
): Promise<NetxJson> {
|
||||
const batchId = str(args, 'batch_id').trim()
|
||||
if (!batchId) return { ok: false, error: 'batch_id_required' }
|
||||
return client.get(`/v1/biz-state/batches/${encodeURIComponent(batchId)}`, {}, signal)
|
||||
}
|
||||
|
||||
/** Full CLI raw_text for one collect command. */
|
||||
export async function getBizCollectCommandRaw(
|
||||
client: NetxClient,
|
||||
args: NetxJson,
|
||||
signal?: AbortSignal,
|
||||
): Promise<NetxJson> {
|
||||
const batchId = str(args, 'batch_id').trim()
|
||||
const commandId = str(args, 'command_id').trim()
|
||||
if (!batchId || !commandId) {
|
||||
return { ok: false, error: 'batch_id_and_command_id_required' }
|
||||
}
|
||||
return client.get(
|
||||
`/v1/biz-state/batches/${encodeURIComponent(batchId)}/commands/${encodeURIComponent(commandId)}`,
|
||||
{},
|
||||
signal,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -316,6 +316,67 @@ export function registerNetxTools(
|
|||
},
|
||||
H.findTopologyPaths, getClient, t,
|
||||
),
|
||||
// ── bizMonitor group (cutover / biz_state read via netx REST) ─────────
|
||||
tool(
|
||||
'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.',
|
||||
{
|
||||
project_id: str('Cutover / biz-migration project id'),
|
||||
task_id: str('biz_state task id (optional if project_id set)'),
|
||||
},
|
||||
H.getBizMonitorContext, getClient, t,
|
||||
),
|
||||
tool(
|
||||
'netx__getBizMonitorBoard',
|
||||
'Cutover batch board: project/batch status, evaluate run summary, sheet cards, verdict counts, metric_batches.',
|
||||
{
|
||||
batch_id: { type: 'string' as const, required: true as const },
|
||||
run_id: str('Optional evaluate run id'),
|
||||
},
|
||||
H.getBizMonitorBoard, getClient, t,
|
||||
),
|
||||
tool(
|
||||
'netx__listBizMonitorReds',
|
||||
'Open/carried red tickets. Each item has old_key/new_key (raw A/B) and evidence (device, collect time, show command, iface raw→norm→map).',
|
||||
{
|
||||
project_id: { type: 'string' as const, required: true as const },
|
||||
status: str('open|carried|resolved or empty=all'),
|
||||
limit: num(),
|
||||
},
|
||||
H.listBizMonitorReds, getClient, t,
|
||||
),
|
||||
tool(
|
||||
'netx__getBizMonitorDiffs',
|
||||
'Paged evaluate diffs. Rows expose old_key/new_key (raw A/B), match_* keys, status, evidence with show commands.',
|
||||
{
|
||||
run_id: { type: 'string' as const, required: true as const },
|
||||
metric_id: str(),
|
||||
sheet_id: str(),
|
||||
verdict: str(),
|
||||
color: str('e.g. red'),
|
||||
q: str(),
|
||||
limit: num(),
|
||||
offset: num(),
|
||||
},
|
||||
H.getBizMonitorDiffs, getClient, t,
|
||||
),
|
||||
tool(
|
||||
'netx__getBizCollectBatch',
|
||||
'One biz_state collect batch: commands (raw_command, parse_status, preview), parsed metrics.',
|
||||
{
|
||||
batch_id: { type: 'string' as const, required: true as const },
|
||||
},
|
||||
H.getBizCollectBatch, getClient, t,
|
||||
),
|
||||
tool(
|
||||
'netx__getBizCollectCommandRaw',
|
||||
'Full CLI raw_text for one collect command inside a batch (deep dive).',
|
||||
{
|
||||
batch_id: { type: 'string' as const, required: true as const },
|
||||
command_id: { type: 'string' as const, required: true as const },
|
||||
},
|
||||
H.getBizCollectCommandRaw, getClient, t,
|
||||
),
|
||||
// ── topology group (netx-topology canvas / fabric) ─────────────────────
|
||||
tool(
|
||||
'netx__getTopologyTree',
|
||||
|
|
|
|||
|
|
@ -15,19 +15,21 @@ import {
|
|||
TOOLS_BY_GROUP,
|
||||
} from '../src/netx/capability-groups.ts'
|
||||
|
||||
test('defaults: ops in preset; topology and public off', () => {
|
||||
test('defaults: ops + bizMonitor in preset; topology and public off', () => {
|
||||
assert.deepEqual(DEFAULT_CAPABILITY_GROUPS, {
|
||||
ops: { inPreset: true, public: false },
|
||||
topology: { inPreset: false, public: false },
|
||||
bizMonitor: { inPreset: true, public: false },
|
||||
})
|
||||
assert.deepEqual(groupsForPlane(undefined, 'preset'), ['ops'])
|
||||
assert.deepEqual(groupsForPlane(undefined, 'preset'), ['ops', 'bizMonitor'])
|
||||
assert.deepEqual(groupsForPlane(undefined, 'public'), [])
|
||||
})
|
||||
|
||||
test('one group one skill dir; ops owns NMS + managed CLI; topology owns canvas', () => {
|
||||
test('one group one skill dir; ops owns NMS + managed CLI; topology owns canvas; bizMonitor owns cutover read', () => {
|
||||
assert.deepEqual(SKILL_DIR_BY_GROUP, {
|
||||
ops: 'ops',
|
||||
topology: 'topology',
|
||||
bizMonitor: 'biz-monitor',
|
||||
})
|
||||
assert.ok(TOOLS_BY_GROUP.ops.includes('netx__findTopologyPaths'))
|
||||
assert.ok(TOOLS_BY_GROUP.ops.includes('netx__execManagedNe'))
|
||||
|
|
@ -36,11 +38,14 @@ test('one group one skill dir; ops owns NMS + managed CLI; topology owns canvas'
|
|||
assert.ok(TOOLS_BY_GROUP.topology.includes('netx__layoutTopologyView'))
|
||||
assert.ok(TOOLS_BY_GROUP.topology.includes('netx__suggestSinkHubs'))
|
||||
assert.ok(TOOLS_BY_GROUP.topology.includes('netx__sinkTopologyDualUnits'))
|
||||
assert.ok(TOOLS_BY_GROUP.bizMonitor.includes('netx__getBizMonitorContext'))
|
||||
assert.ok(TOOLS_BY_GROUP.bizMonitor.includes('netx__getBizCollectCommandRaw'))
|
||||
})
|
||||
|
||||
test('capabilityGroupsFromSettings honors explicit false for inPreset defaults', () => {
|
||||
const groups = capabilityGroupsFromSettings({
|
||||
groupOpsInPreset: false,
|
||||
groupBizMonitorInPreset: false,
|
||||
groupTopologyInPreset: true,
|
||||
groupTopologyPublic: true,
|
||||
})
|
||||
|
|
@ -49,6 +54,7 @@ test('capabilityGroupsFromSettings honors explicit false for inPreset defaults',
|
|||
assert.ok(toolNamesForGroups(['topology']).has('netx__layoutTopologyView'))
|
||||
assert.ok(toolNamesForGroups(['topology']).has('netx__sinkTopologyDualUnits'))
|
||||
assert.equal(toolNamesForGroups(['topology']).has('netx__queryNmsAlarms'), false)
|
||||
assert.equal(toolNamesForGroups(['topology']).has('netx__getBizMonitorContext'), false)
|
||||
})
|
||||
|
||||
test('legacy groupNms/common/managedNe map to ops; legacy layout flags OR into topology', () => {
|
||||
|
|
@ -61,6 +67,7 @@ test('legacy groupNms/common/managedNe map to ops; legacy layout flags OR into t
|
|||
assert.equal(groups.ops.inPreset, false)
|
||||
assert.equal(groups.ops.public, true)
|
||||
assert.equal(groups.topology.inPreset, true)
|
||||
assert.equal(groups.bizMonitor.inPreset, true)
|
||||
})
|
||||
|
||||
test('legacy any-true turns ops inPreset on', () => {
|
||||
|
|
@ -74,4 +81,5 @@ test('legacy any-true turns ops inPreset on', () => {
|
|||
test('groupsForced ignores settings for per-export mounts', () => {
|
||||
assert.deepEqual(groupsForced(['ops']), ['ops'])
|
||||
assert.deepEqual(groupsForced(['ops', 'topology']), ['ops', 'topology'])
|
||||
assert.deepEqual(groupsForced(['bizMonitor']), ['bizMonitor'])
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue