mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-09 10:50:46 +08:00
Merge nms+common into ops so login questions load execManagedNe.
One capability group now ships netx-ops (alarms + managed CLI + paths); keep tools-nms/common as aliases. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6e591be2b5
commit
63f88bdf6c
33 changed files with 2133 additions and 446 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# netxops — Netx Ops for DeepSeek Harness
|
||||
# netxops — Netx Ops for DeepSeek Harness
|
||||
|
||||
Public **DeepSeek Harness** package: native `netx__*` REST tools + Plugins card + **agent preset (skills included)**.
|
||||
|
||||
|
|
@ -28,9 +28,9 @@ Also need: a reachable **netx API** (no local `pip install netx_mcp`). Details:
|
|||
|
||||
| In the plugin | Outside (data / runtime) |
|
||||
|---------------|---------------------------|
|
||||
| `netx__*` REST tools (groups: nms / common / topology; one group ↔ one skill; optional host publish + `tools-*`) + settings + credentials | netx HTTP API (URL + token) |
|
||||
| `netx__*` REST tools (groups: ops / topology; one group ↔ one skill; optional host publish + `tools-ops|topology`) + settings + credentials | netx HTTP API (URL + token) |
|
||||
| Optional key-alarm push (WSS client → sticky DSH session) | netx `/v1/integrations/dsh-alarm/ws` hub |
|
||||
| Persona + skills (`netx-nms` / `netx-common` / `netx-topology`) | |
|
||||
| Persona + skills (`netx-ops` / `netx-topology`) | |
|
||||
| Agent preset auto-install to `~/.dsh/.agent-presets` | |
|
||||
|
||||
MCP (`python -m netx_mcp`) remains available for OpenClaw / other hosts — not required for DSH.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Host-plane Netx Ops bridge: settings + credentials → connection publish.
|
||||
# Ops-scoped `netx__*` tools mount from the agent preset (`dsh-netxops/tools`).
|
||||
# Capability groups: nms + common in preset; topology / public off.
|
||||
# Rule: one group ↔ one skill (netx-nms / netx-common / netx-topology).
|
||||
# Capability groups: ops in preset; topology / public off.
|
||||
# Rule: one group ↔ one skill (netx-ops / netx-topology).
|
||||
#
|
||||
# After `dsh plugin add` / link, open Settings → Plugins to edit apiUrl / lang / groups
|
||||
# (settings card client half; see docs/INSTALL.md). Token: credential NETX_API_TOKEN.
|
||||
|
|
@ -21,9 +21,7 @@
|
|||
alarmDeliverIm: false
|
||||
imBotId: ''
|
||||
imTargetId: ''
|
||||
groupNmsInPreset: true
|
||||
groupNmsPublic: false
|
||||
groupCommonInPreset: true
|
||||
groupCommonPublic: false
|
||||
groupOpsInPreset: true
|
||||
groupOpsPublic: false
|
||||
groupTopologyInPreset: false
|
||||
groupTopologyPublic: false
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Install Netx Ops on DeepSeek Harness
|
||||
# Install Netx Ops on DeepSeek Harness
|
||||
|
||||
## What `dsh plugin add` gives you
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ One install wires **all of**:
|
|||
|
||||
| Piece | How you use it |
|
||||
|-------|----------------|
|
||||
| Host tools | Capability groups **nms / common / topology** (one group ↔ one skill). Default nms+common in Ops preset; topology and all public off. Other agents may mount `dsh-netxops/tools-*` |
|
||||
| Host tools | Capability groups **ops / topology** (one group ↔ one skill). Default ops in Ops preset; topology and all public off. Other agents may mount `dsh-netxops/tools-ops|topology` |
|
||||
| Plugins card | Settings → Plugins → **Netx Ops** (URL / lang / token / capability groups) |
|
||||
| Agent preset + skills | Settings → Agent presets → **Custom → Netx Ops** (copied into `~/.dsh/.agent-presets` on first boot); playbooks follow the same group toggles |
|
||||
|
||||
|
|
@ -31,9 +31,9 @@ dsh web # or: pnpm dsh web
|
|||
1. **Settings → Plugins → Netx Ops** → API URL (+ token if the field is enabled).
|
||||
Token fallback: `scripts/set-netx-token.ps1` / `.sh`.
|
||||
Optional: enable **关键告警推送** so this DSH dials `ws(s)://<apiUrl>/v1/integrations/dsh-alarm/ws`. Choose **投递到 DSH 会话** and/or **投递到 WhatsApp / IM**. For IM, install `dsh-im-ops`, create a delivery target, then paste `imBotId` / `imTargetId`. The card header shows live WSS status.
|
||||
Capability groups: leave defaults for nms+common, or enable **topology** / **对其他预设公开** (new sessions after save).
|
||||
Capability groups: leave default for **ops**, or enable **topology** / **对其他预设公开** (new sessions after save).
|
||||
2. Restart or open Settings → **Agent presets** → Custom → **Netx Ops** should appear after the host plugin has activated once.
|
||||
3. **New session → preset Netx Ops** → ask e.g. Critical Top / single-host alarms.
|
||||
3. **New session → preset Netx Ops** → ask e.g. Critical Top / single-host alarms / 「能否登录」.
|
||||
|
||||
## Key-alarm push
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ dsh web # or: pnpm dsh web
|
|||
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` (**nms**). `netx__findTopologyPaths` is **common**. Canvas / dual_unit need **topology** (`netx-topology` skill).
|
||||
4. Tools include `netx__queryNmsAlarms` / `netx__execManagedNe` / `netx__findTopologyPaths` (**ops** → skill `netx-ops`). Canvas / dual_unit need **topology** (`netx-topology`).
|
||||
|
||||
See [TOOL_MAP.md](TOOL_MAP.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@
|
|||
| Source (oclaw) | Destination |
|
||||
|----------------|-------------|
|
||||
| `runtime/workspaces/ops/ROLE_SYSTEM.md` | `presets/netxops/PERSONA.md` + persona in `agent.cordis.yml` (brand **Netx Ops**) |
|
||||
| `skills/_workspace/ops/ops-netx-ume-playbook/` | **Canonical:** `netx/skills/nms/netx-nms/` |
|
||||
| `skills/_workspace/ops/ops-netx-managed-ne-playbook/` | **Canonical:** `netx/skills/common/netx-common/` |
|
||||
| `skills/_workspace/ops/ops-netx-ume-playbook/` + `ops-netx-managed-ne-playbook/` | **Canonical:** `netx/skills/ops/netx-ops/` (merged ops group) |
|
||||
| `skills/.../netx-topology` (MCP) | **Canonical:** `netx/skills/topology/netx-topology/` |
|
||||
| netx MCP HTTP surface | Host plugin `src/netx/*` + `src/index.ts`: settings namespace `netxops` + credential `NETX_API_TOKEN` → native `netx__*` tools |
|
||||
|
||||
|
|
|
|||
|
|
@ -4,26 +4,20 @@ Tools register by capability group. **One group ↔ one skill.**
|
|||
|
||||
| Group | Skill | Default in Ops preset |
|
||||
|-------|-------|------------------------|
|
||||
| **nms** | `netx-nms` | on |
|
||||
| **common** | `netx-common` | on |
|
||||
| **ops** | `netx-ops` | on |
|
||||
| **topology** | `netx-topology` | off |
|
||||
|
||||
Public flags mount the same group on the host for other presets.
|
||||
Forced mounts: `dsh-netxops/tools-nms` | `tools-common` | `tools-topology`.
|
||||
Forced mounts: `dsh-netxops/tools-ops` | `tools-topology` (legacy aliases: `tools-nms` / `tools-common` → ops).
|
||||
|
||||
Model names: `netx__<stem>`. NMS tools use `Nms`; adapter `nmsProvider=zte-ume` still hits `/v1/ume/*`.
|
||||
|
||||
## nms → `netx-nms`
|
||||
## ops → `netx-ops`
|
||||
|
||||
| Tool | Role |
|
||||
|------|------|
|
||||
| `queryNmsAlarms` … `sqlQueryNms` | Alarm + inventory + SQL |
|
||||
|
||||
## common → `netx-common`
|
||||
|
||||
| Tool | Role |
|
||||
|------|------|
|
||||
| `listManagedNe` / `getManagedNe` / `execManagedNe` / `listCliTargets` | Managed CLI |
|
||||
| `listManagedNe` / `getManagedNe` / `execManagedNe` / `listCliTargets` | Managed CLI (login / show) |
|
||||
| `findTopologyPaths` | Fabric path lookup |
|
||||
|
||||
## topology → `netx-topology`
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
nms: Object.freeze({ inPreset: true, public: false }),
|
||||
common: Object.freeze({ inPreset: true, public: false }),
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
nms: Object.freeze([
|
||||
ops: Object.freeze([
|
||||
"netx__queryNmsAlarms",
|
||||
"netx__aggregateNmsAlarms",
|
||||
"netx__runNmsDiagnostics",
|
||||
|
|
@ -14,9 +13,7 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__queryNmsAlarmsRaw",
|
||||
"netx__aggregateNmsAlarmsRaw",
|
||||
"netx__listNmsAlarmFields",
|
||||
"netx__sqlQueryNms"
|
||||
]),
|
||||
common: Object.freeze([
|
||||
"netx__sqlQueryNms",
|
||||
"netx__listManagedNe",
|
||||
"netx__getManagedNe",
|
||||
"netx__execManagedNe",
|
||||
|
|
@ -43,13 +40,11 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
nms: "nms",
|
||||
common: "common",
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"nms",
|
||||
"common",
|
||||
"ops",
|
||||
"topology"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
|
|
@ -61,7 +56,8 @@ function groupsForPlane(groups, plane, only) {
|
|||
return enabled.filter((id) => allow.has(id));
|
||||
}
|
||||
function groupsForced(only) {
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => only.includes(id));
|
||||
const normalized = only.map((id) => id === "nms" || id === "common" ? "ops" : id);
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
|
|
@ -1792,7 +1788,7 @@ function applyGroupToolsPlugin(ctx, options) {
|
|||
var name = "netxops-tools-common";
|
||||
var inject = ["tools"];
|
||||
function apply(ctx) {
|
||||
applyGroupToolsPlugin(ctx, { name, mode: "forced", only: ["common"] });
|
||||
applyGroupToolsPlugin(ctx, { name, mode: "forced", only: ["ops"] });
|
||||
}
|
||||
export {
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
nms: Object.freeze({ inPreset: true, public: false }),
|
||||
common: Object.freeze({ inPreset: true, public: false }),
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
nms: Object.freeze([
|
||||
ops: Object.freeze([
|
||||
"netx__queryNmsAlarms",
|
||||
"netx__aggregateNmsAlarms",
|
||||
"netx__runNmsDiagnostics",
|
||||
|
|
@ -14,9 +13,7 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__queryNmsAlarmsRaw",
|
||||
"netx__aggregateNmsAlarmsRaw",
|
||||
"netx__listNmsAlarmFields",
|
||||
"netx__sqlQueryNms"
|
||||
]),
|
||||
common: Object.freeze([
|
||||
"netx__sqlQueryNms",
|
||||
"netx__listManagedNe",
|
||||
"netx__getManagedNe",
|
||||
"netx__execManagedNe",
|
||||
|
|
@ -43,13 +40,11 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
nms: "nms",
|
||||
common: "common",
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"nms",
|
||||
"common",
|
||||
"ops",
|
||||
"topology"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
|
|
@ -61,7 +56,8 @@ function groupsForPlane(groups, plane, only) {
|
|||
return enabled.filter((id) => allow.has(id));
|
||||
}
|
||||
function groupsForced(only) {
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => only.includes(id));
|
||||
const normalized = only.map((id) => id === "nms" || id === "common" ? "ops" : id);
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
|
|
@ -1792,7 +1788,7 @@ function applyGroupToolsPlugin(ctx, options) {
|
|||
var name = "netxops-tools-nms";
|
||||
var inject = ["tools"];
|
||||
function apply(ctx) {
|
||||
applyGroupToolsPlugin(ctx, { name, mode: "forced", only: ["nms"] });
|
||||
applyGroupToolsPlugin(ctx, { name, mode: "forced", only: ["ops"] });
|
||||
}
|
||||
export {
|
||||
name,
|
||||
|
|
|
|||
1797
lib/agent-tools-ops.js
Normal file
1797
lib/agent-tools-ops.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,10 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
nms: Object.freeze({ inPreset: true, public: false }),
|
||||
common: Object.freeze({ inPreset: true, public: false }),
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
nms: Object.freeze([
|
||||
ops: Object.freeze([
|
||||
"netx__queryNmsAlarms",
|
||||
"netx__aggregateNmsAlarms",
|
||||
"netx__runNmsDiagnostics",
|
||||
|
|
@ -14,9 +13,7 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__queryNmsAlarmsRaw",
|
||||
"netx__aggregateNmsAlarmsRaw",
|
||||
"netx__listNmsAlarmFields",
|
||||
"netx__sqlQueryNms"
|
||||
]),
|
||||
common: Object.freeze([
|
||||
"netx__sqlQueryNms",
|
||||
"netx__listManagedNe",
|
||||
"netx__getManagedNe",
|
||||
"netx__execManagedNe",
|
||||
|
|
@ -43,13 +40,11 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
nms: "nms",
|
||||
common: "common",
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"nms",
|
||||
"common",
|
||||
"ops",
|
||||
"topology"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
|
|
@ -61,7 +56,8 @@ function groupsForPlane(groups, plane, only) {
|
|||
return enabled.filter((id) => allow.has(id));
|
||||
}
|
||||
function groupsForced(only) {
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => only.includes(id));
|
||||
const normalized = only.map((id) => id === "nms" || id === "common" ? "ops" : id);
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
nms: Object.freeze({ inPreset: true, public: false }),
|
||||
common: Object.freeze({ inPreset: true, public: false }),
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
nms: Object.freeze([
|
||||
ops: Object.freeze([
|
||||
"netx__queryNmsAlarms",
|
||||
"netx__aggregateNmsAlarms",
|
||||
"netx__runNmsDiagnostics",
|
||||
|
|
@ -14,9 +13,7 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__queryNmsAlarmsRaw",
|
||||
"netx__aggregateNmsAlarmsRaw",
|
||||
"netx__listNmsAlarmFields",
|
||||
"netx__sqlQueryNms"
|
||||
]),
|
||||
common: Object.freeze([
|
||||
"netx__sqlQueryNms",
|
||||
"netx__listManagedNe",
|
||||
"netx__getManagedNe",
|
||||
"netx__execManagedNe",
|
||||
|
|
@ -43,13 +40,11 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
nms: "nms",
|
||||
common: "common",
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"nms",
|
||||
"common",
|
||||
"ops",
|
||||
"topology"
|
||||
]);
|
||||
function groupsForPlane(groups, plane, only) {
|
||||
|
|
@ -61,7 +56,8 @@ function groupsForPlane(groups, plane, only) {
|
|||
return enabled.filter((id) => allow.has(id));
|
||||
}
|
||||
function groupsForced(only) {
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => only.includes(id));
|
||||
const normalized = only.map((id) => id === "nms" || id === "common" ? "ops" : id);
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id));
|
||||
}
|
||||
function toolNamesForGroups(groupIds) {
|
||||
const names = new Set;
|
||||
|
|
|
|||
|
|
@ -630,31 +630,17 @@ function NetxopsCard(props) {
|
|||
children: t("capabilityGroupsHint")
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(CapabilityGroupBlock, {
|
||||
title: t("groupNms"),
|
||||
title: t("groupOps"),
|
||||
inPresetLabel: t("groupInPreset"),
|
||||
publicLabel: t("groupPublic"),
|
||||
inPreset: state.groupNmsInPreset,
|
||||
published: state.groupNmsPublic,
|
||||
inPreset: state.groupOpsInPreset,
|
||||
published: state.groupOpsPublic,
|
||||
disabled,
|
||||
onEditInPreset: (checked) => {
|
||||
props.edit("groupNmsInPreset", checked ? "true" : "false");
|
||||
props.edit("groupOpsInPreset", checked ? "true" : "false");
|
||||
},
|
||||
onEditPublic: (checked) => {
|
||||
props.edit("groupNmsPublic", checked ? "true" : "false");
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(CapabilityGroupBlock, {
|
||||
title: t("groupCommon"),
|
||||
inPresetLabel: t("groupInPreset"),
|
||||
publicLabel: t("groupPublic"),
|
||||
inPreset: state.groupCommonInPreset,
|
||||
published: state.groupCommonPublic,
|
||||
disabled,
|
||||
onEditInPreset: (checked) => {
|
||||
props.edit("groupCommonInPreset", checked ? "true" : "false");
|
||||
},
|
||||
onEditPublic: (checked) => {
|
||||
props.edit("groupCommonPublic", checked ? "true" : "false");
|
||||
props.edit("groupOpsPublic", checked ? "true" : "false");
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(CapabilityGroupBlock, {
|
||||
|
|
@ -1162,10 +1148,8 @@ class NetxopsCardController {
|
|||
textField("apiUrl"),
|
||||
textField("lang"),
|
||||
textField("nmsProvider"),
|
||||
booleanFieldPersistFalse("groupNmsInPreset"),
|
||||
booleanField("groupNmsPublic"),
|
||||
booleanFieldPersistFalse("groupCommonInPreset"),
|
||||
booleanField("groupCommonPublic"),
|
||||
booleanFieldPersistFalse("groupOpsInPreset"),
|
||||
booleanField("groupOpsPublic"),
|
||||
booleanField("groupTopologyInPreset"),
|
||||
booleanField("groupTopologyPublic"),
|
||||
booleanField("alarmPushEnabled"),
|
||||
|
|
@ -1269,10 +1253,8 @@ class NetxopsCardController {
|
|||
apiUrl: this.form.field("apiUrl"),
|
||||
lang: this.form.field("lang"),
|
||||
nmsProvider: this.form.field("nmsProvider"),
|
||||
groupNmsInPreset: this.form.field("groupNmsInPreset"),
|
||||
groupNmsPublic: this.form.field("groupNmsPublic"),
|
||||
groupCommonInPreset: this.form.field("groupCommonInPreset"),
|
||||
groupCommonPublic: this.form.field("groupCommonPublic"),
|
||||
groupOpsInPreset: this.form.field("groupOpsInPreset"),
|
||||
groupOpsPublic: this.form.field("groupOpsPublic"),
|
||||
groupTopologyInPreset: this.form.field("groupTopologyInPreset"),
|
||||
groupTopologyPublic: this.form.field("groupTopologyPublic"),
|
||||
alarmPushEnabled: this.form.field("alarmPushEnabled"),
|
||||
|
|
@ -1350,17 +1332,16 @@ function refOf(snapshot) {
|
|||
// src/client/locales.ts
|
||||
var en = {
|
||||
title: "Netx Ops",
|
||||
description: "netx API, bearer token, and capability groups (nms / common / topology).",
|
||||
description: "netx API, bearer token, and capability groups (ops / topology).",
|
||||
apiUrl: "API URL",
|
||||
apiUrlHint: "netx REST root, e.g. http://127.0.0.1:8890 (also used for alarm subscribe)",
|
||||
lang: "Language",
|
||||
langHint: "Response language hint (zh / en).",
|
||||
capabilityGroups: "Capability groups",
|
||||
capabilityGroupsHint: "One group ↔ one skill (netx-nms / netx-common / netx-topology). Defaults: nms + common in preset; topology and all public off. Save, then open a new session. Other agents can mount dsh-netxops/tools-nms|common|topology.",
|
||||
capabilityGroupsHint: "One group ↔ one skill. ops = netx-ops (alarms + CLI login + paths); topology = netx-topology. Defaults: ops on; topology and public off. New sessions after save. Other agents: dsh-netxops/tools-ops|topology.",
|
||||
nmsProvider: "nms provider",
|
||||
nmsProviderHint: "Vendor adapter id. Supported today: zte-ume (REST still /v1/ume/*; model tools are netx__*Nms*).",
|
||||
groupNms: "nms — skill netx-nms (alarms / inventory / SQL)",
|
||||
groupCommon: "common — skill netx-common (managed CLI + findTopologyPaths)",
|
||||
groupOps: "ops — skill netx-ops (NMS alarms/inventory + managed CLI login + paths)",
|
||||
groupTopology: "topology — skill netx-topology (canvas / fabric / dual_unit)",
|
||||
groupInPreset: "In Netx Ops preset",
|
||||
groupPublic: "Publish to other presets",
|
||||
|
|
@ -1406,17 +1387,16 @@ var en = {
|
|||
};
|
||||
var zh = {
|
||||
title: "Netx Ops",
|
||||
description: "netx API、Bearer Token,以及能力组(nms / common / topology)。",
|
||||
description: "netx API、Bearer Token,以及能力组(ops / topology)。",
|
||||
apiUrl: "API 地址",
|
||||
apiUrlHint: "netx REST 根地址,例如 http://127.0.0.1:8890(告警订阅复用同一地址)",
|
||||
lang: "语言",
|
||||
langHint: "响应语言提示(zh / en)。",
|
||||
capabilityGroups: "能力组",
|
||||
capabilityGroupsHint: "一组一个 skill(netx-nms / netx-common / netx-topology)。默认:nms + common 进入预设;topology 与全部公开关闭。保存后请新开会话。其他 Agent 可挂 dsh-netxops/tools-nms|common|topology。",
|
||||
capabilityGroupsHint: "一组一个 skill。ops = netx-ops(告警 + 纳管登录 + 路径);topology = netx-topology。默认 ops 开;topology / 公开关。保存后新会话生效。其他 Agent:dsh-netxops/tools-ops|topology。",
|
||||
nmsProvider: "nms provider",
|
||||
nmsProviderHint: "厂商适配器 id。当前支持:zte-ume(REST 仍为 /v1/ume/*;模型工具名为 netx__*Nms*)。",
|
||||
groupNms: "nms — skill netx-nms(告警 / 库存 / SQL)",
|
||||
groupCommon: "common — skill netx-common(纳管 CLI + findTopologyPaths)",
|
||||
groupOps: "ops — skill netx-ops(NMS 告警/库存 + 纳管 CLI 登录 + 路径)",
|
||||
groupTopology: "topology — skill netx-topology(画布 / Fabric / dual_unit)",
|
||||
groupInPreset: "在 Netx Ops 预设中启用",
|
||||
groupPublic: "对其他预设公开",
|
||||
|
|
|
|||
46
lib/index.js
46
lib/index.js
|
|
@ -395,12 +395,11 @@ function resetAlarmSession() {
|
|||
|
||||
// src/netx/capability-groups.ts
|
||||
var DEFAULT_CAPABILITY_GROUPS = Object.freeze({
|
||||
nms: Object.freeze({ inPreset: true, public: false }),
|
||||
common: Object.freeze({ inPreset: true, public: false }),
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false })
|
||||
});
|
||||
var TOOLS_BY_GROUP = Object.freeze({
|
||||
nms: Object.freeze([
|
||||
ops: Object.freeze([
|
||||
"netx__queryNmsAlarms",
|
||||
"netx__aggregateNmsAlarms",
|
||||
"netx__runNmsDiagnostics",
|
||||
|
|
@ -409,9 +408,7 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
"netx__queryNmsAlarmsRaw",
|
||||
"netx__aggregateNmsAlarmsRaw",
|
||||
"netx__listNmsAlarmFields",
|
||||
"netx__sqlQueryNms"
|
||||
]),
|
||||
common: Object.freeze([
|
||||
"netx__sqlQueryNms",
|
||||
"netx__listManagedNe",
|
||||
"netx__getManagedNe",
|
||||
"netx__execManagedNe",
|
||||
|
|
@ -438,27 +435,32 @@ var TOOLS_BY_GROUP = Object.freeze({
|
|||
])
|
||||
});
|
||||
var SKILL_DIR_BY_GROUP = Object.freeze({
|
||||
nms: "nms",
|
||||
common: "common",
|
||||
ops: "ops",
|
||||
topology: "topology"
|
||||
});
|
||||
var CAPABILITY_GROUP_IDS = Object.freeze([
|
||||
"nms",
|
||||
"common",
|
||||
"ops",
|
||||
"topology"
|
||||
]);
|
||||
function capabilityGroupsFromSettings(fields) {
|
||||
const src = fields ?? {};
|
||||
const commonInPreset = src.groupCommonInPreset !== undefined ? src.groupCommonInPreset !== false : src.groupManagedNeInPreset !== false;
|
||||
const commonPublic = src.groupCommonPublic === true || src.groupManagedNePublic === true;
|
||||
let opsInPreset = true;
|
||||
if (src.groupOpsInPreset !== undefined) {
|
||||
opsInPreset = src.groupOpsInPreset !== false;
|
||||
} else {
|
||||
const legacy = [
|
||||
src.groupNmsInPreset,
|
||||
src.groupCommonInPreset,
|
||||
src.groupManagedNeInPreset
|
||||
].filter((v) => v !== undefined);
|
||||
if (legacy.length > 0) {
|
||||
opsInPreset = legacy.some((v) => v === true);
|
||||
}
|
||||
}
|
||||
return {
|
||||
nms: {
|
||||
inPreset: src.groupNmsInPreset !== false,
|
||||
public: src.groupNmsPublic === true
|
||||
},
|
||||
common: {
|
||||
inPreset: commonInPreset,
|
||||
public: commonPublic
|
||||
ops: {
|
||||
inPreset: opsInPreset,
|
||||
public: src.groupOpsPublic === true || src.groupNmsPublic === true || src.groupCommonPublic === true || src.groupManagedNePublic === true
|
||||
},
|
||||
topology: {
|
||||
inPreset: src.groupTopologyInPreset === true || src.groupTopologyLayoutInPreset === true,
|
||||
|
|
@ -2152,10 +2154,8 @@ var Config = z.object({
|
|||
imBotId: z.string().default(""),
|
||||
imTargetId: z.string().default(""),
|
||||
nmsProvider: z.string().default("zte-ume"),
|
||||
groupNmsInPreset: z.boolean().default(true),
|
||||
groupNmsPublic: z.boolean().default(false),
|
||||
groupCommonInPreset: z.boolean().default(true),
|
||||
groupCommonPublic: z.boolean().default(false),
|
||||
groupOpsInPreset: z.boolean().default(true),
|
||||
groupOpsPublic: z.boolean().default(false),
|
||||
groupTopologyInPreset: z.boolean().default(false),
|
||||
groupTopologyPublic: z.boolean().default(false)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "dsh-netxops",
|
||||
"version": "0.1.21",
|
||||
"description": "DeepSeek Harness Netx Ops: capability groups nms/common/topology (one group ↔ one skill)",
|
||||
"version": "0.1.22",
|
||||
"description": "DeepSeek Harness Netx Ops: capability groups ops/topology (one group ↔ one skill)",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"private": false,
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
"exports": {
|
||||
".": "./lib/index.js",
|
||||
"./tools": "./lib/agent-tools.js",
|
||||
"./tools-ops": "./lib/agent-tools-ops.js",
|
||||
"./tools-nms": "./lib/agent-tools-nms.js",
|
||||
"./tools-common": "./lib/agent-tools-common.js",
|
||||
"./tools-topology": "./lib/agent-tools-topology.js",
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
"src/",
|
||||
"lib/index.js",
|
||||
"lib/agent-tools.js",
|
||||
"lib/agent-tools-ops.js",
|
||||
"lib/agent-tools-nms.js",
|
||||
"lib/agent-tools-common.js",
|
||||
"lib/agent-tools-topology.js",
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@ You are **Netx Ops**, a network operations specialist for NMS / netx.
|
|||
- Display NEs by **host_name** only — never bare UUID to the user.
|
||||
|
||||
## Skills (by capability group)
|
||||
- `netx-nms` — **nms**
|
||||
- `netx-common` — **common**
|
||||
- `netx-ops` — **ops** (NMS alarms/inventory + managed CLI login + paths)
|
||||
- `netx-topology` — **topology** (canvas / dual_unit / layout)
|
||||
|
||||
## Tools
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@
|
|||
- No evidence → say evidence insufficient; do not invent root cause.
|
||||
|
||||
## Skills (load before acting; only when the capability group is enabled)
|
||||
- nms alarms / inventory → `netx-nms`
|
||||
- common managed CLI + paths → `netx-common`
|
||||
- ops (alarms / inventory / 「能否登录」CLI / paths) → `netx-ops`
|
||||
- topology canvas / dual_unit / layout → `netx-topology`
|
||||
|
||||
## Tools
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@ powershell -File .\scripts\sync-skills-from-netx.ps1
|
|||
`
|
||||
|
||||
Runtime prefers `NETX_SKILLS_ROOT` or sibling `../netx/skills`.
|
||||
One topology skill covers canvas + dual_unit / layout recipes; DSH may still gate **tools** via topology vs topology-layout groups.
|
||||
Groups: **ops** (`netx-ops`) + **topology** (`netx-topology`). One group 鈫?one skill.
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
---
|
||||
name: netx-common
|
||||
description: >-
|
||||
netx common playbook (MCP + DSH): managed-NE CLI (SSH/Telnet batch-first) plus
|
||||
native findTopologyPaths. Not NMS REST alarms/inventory. Trigger: execManagedNe,
|
||||
show/display, optical, capacity A<>B, path between sites.
|
||||
---
|
||||
|
||||
# netx-common (managed CLI + paths)
|
||||
|
||||
## Naming (MCP + DSH)
|
||||
|
||||
Canonical: **`netx/skills/common/netx-common/`**. Mirrored into MCP / dsh-netxops — edit once.
|
||||
|
||||
| Host | How tools appear |
|
||||
|------|------------------|
|
||||
| **MCP** (`netx-mcp`) | Bare names: `execManagedNe`, `findTopologyPaths`, … |
|
||||
| **DSH** (`dsh-netxops`) | Prefixed: `netx__execManagedNe`, … |
|
||||
|
||||
NMS alarms/inventory → **netx-nms**. Canvas / dual_unit → **netx-topology**.
|
||||
|
||||
## Tools
|
||||
|
||||
| Purpose | Tool |
|
||||
|---------|------|
|
||||
| List managed NEs | `listManagedNe` |
|
||||
| Managed detail | `getManagedNe` |
|
||||
| Read-only CLI (batch-first) | `execManagedNe` |
|
||||
| CLI target index | `listCliTargets` |
|
||||
| Fabric paths | `findTopologyPaths` |
|
||||
|
||||
Prefer `nms_ne_id` / `nms_ne_ids`; legacy `ume_*` accepted. `listCliTargets(source=nms)` preferred (`ume` alias).
|
||||
|
||||
## CLI order
|
||||
|
||||
1. `listManagedNe` (`connect_status=pass`) or `listCliTargets` (**once** per session, cache ids)
|
||||
2. Multi-NE → **one** `execManagedNe` with `ne_ids` / `nms_ne_ids` / `targets`
|
||||
3. Paths → `findTopologyPaths` (`nms_ne_id` **or** `managed_ne_id` per end)
|
||||
|
||||
### Batch examples
|
||||
|
||||
**Same commands, many NEs**
|
||||
|
||||
```json
|
||||
{
|
||||
"nms_ne_ids": ["uuid-a", "uuid-b", "uuid-c"],
|
||||
"commands": ["show version"],
|
||||
"read_timeout_sec": 60,
|
||||
"concurrency": 4
|
||||
}
|
||||
```
|
||||
|
||||
**Different commands per NE (still one call)**
|
||||
|
||||
```json
|
||||
{
|
||||
"targets": [
|
||||
{"nms_ne_id": "uuid-zte", "commands": ["show opticalinfo brief"]},
|
||||
{"nms_ne_id": "uuid-hw", "commands": ["display optical-module brief"]},
|
||||
{"nms_ne_id": "uuid-cisco", "commands": ["show interface transceiver"]}
|
||||
],
|
||||
"read_timeout_sec": 90
|
||||
}
|
||||
```
|
||||
|
||||
**Wrong:** N× single-NE `execManagedNe` in one turn (stdio serial).
|
||||
|
||||
## Field recipes
|
||||
|
||||
### Capacity / optical A<>B
|
||||
|
||||
1. Resolve nicknames → `host_name`
|
||||
2. `findTopologyPaths` and/or LLDP → both ports
|
||||
3. Optics CLI on **both** ends; summarize RX/TX / thresholds
|
||||
4. Do not answer with only NMS bandwidth/optical-power-threshold alarm tallies unless asked
|
||||
|
||||
### ZTE optical CLI
|
||||
|
||||
Try in order; one failure → switch spelling (do not blind-retry):
|
||||
|
||||
| Prefer | Fallback |
|
||||
|--------|----------|
|
||||
| `show opticalinfo brief` | Field-confirmed on many ZXR10 |
|
||||
| `show optical brief` | Some EN platforms |
|
||||
| `show opticalinfo brief \| begin <if>` | After port known |
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Never pass NMS alarm UUIDs as managed `ne_id` for `getManagedNe`
|
||||
- Allowlist prefixes: `show ` / `display ` / `ping ` / `traceroute` …
|
||||
- Prefer `host_name` for users; keep ids for tool params
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
---
|
||||
name: netx-nms
|
||||
description: >-
|
||||
netx NMS playbook (MCP + DSH): vendor NMS adapter (zte-ume) for alarm
|
||||
query/aggregate/diagnostics, NE inventory, raw fields, and read-only SQL.
|
||||
Trigger: NMS alarms, host_name, Critical Top, LOS, BN EMS, netx ops.
|
||||
---
|
||||
|
||||
# netx-nms (vendor NMS adapter)
|
||||
|
||||
## Naming (MCP + DSH)
|
||||
|
||||
Canonical playbooks live in **`netx/skills/`** (this file is mirrored into MCP packages / dsh-netxops).
|
||||
Edit the netx repo copy; run sync scripts — do not maintain divergent forks.
|
||||
|
||||
| Host | How tools appear |
|
||||
|------|------------------|
|
||||
| **MCP** (`netx-mcp`) | Bare names: `queryNmsAlarms`, … |
|
||||
| **DSH** (`dsh-netxops`) | Prefixed: `netx__queryNmsAlarms`, … (same camelCase stem) |
|
||||
|
||||
REST still `/v1/ume/*` for provider `zte-ume`.
|
||||
Prefer `nms_ne_id` / `nms_ne_ids`; legacy `ume_*` aliases still work.
|
||||
|
||||
Path lookup → **common** skill `netx-common` (`findTopologyPaths`).
|
||||
Canvas → **netx-topology**(含 dual_unit / 布图;DSH 可把部分布局工具放在实验 tool group)。
|
||||
|
||||
## Tool names
|
||||
|
||||
| Purpose | Tool |
|
||||
|---------|------|
|
||||
| Alarm list | `queryNmsAlarms` |
|
||||
| Alarm aggregate | `aggregateNmsAlarms` |
|
||||
| Diagnostics | `runNmsDiagnostics` |
|
||||
| NE inventory | `queryNmsNeInventory` |
|
||||
| NE detail | `getNmsNe` |
|
||||
| Field list | `listNmsAlarmFields` |
|
||||
| Raw rows | `queryNmsAlarmsRaw` |
|
||||
| Dynamic aggregate | `aggregateNmsAlarmsRaw` |
|
||||
| SQL | `sqlQueryNms` |
|
||||
| Managed CLI (other skill) | `listManagedNe` / `getManagedNe` / `execManagedNe` / `listCliTargets` |
|
||||
|
||||
## Tool order
|
||||
|
||||
1. **Freshness first**: `runNmsDiagnostics` or `aggregateNmsAlarms` → `meta.last_seen_min` / `last_seen_max`.
|
||||
2. Overview: `aggregateNmsAlarms` + `runNmsDiagnostics`; samples via `queryNmsAlarms` (one page).
|
||||
3. Evidence: `listNmsAlarmFields` → `queryNmsAlarmsRaw` (`field_preset=evidence`).
|
||||
4. Custom aggregate: `aggregateNmsAlarmsRaw`.
|
||||
5. SQL: `sqlQueryNms` (SELECT only; `statement_timeout_ms`).
|
||||
6. Paths: `findTopologyPaths` via **netx-common**.
|
||||
7. Device CLI: **netx-common** — multi-NE = **one** `execManagedNe` batch.
|
||||
|
||||
## Short-intent recipes
|
||||
|
||||
| User says | Recipe |
|
||||
|-----------|--------|
|
||||
| fiber cut / LOS / 断纤 | `queryNmsAlarmsRaw(keyword=LOS)` and/or `Fiber Break` → **host_name** list |
|
||||
| offline / BN EMS | keyword=`BN EMS` |
|
||||
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
|
||||
| CRC in area | Raw `keyword=CRC` + `AREA-` hostname prefix |
|
||||
| optical power **threshold** | keyword=`optical power` + area — **not** fiber-cut |
|
||||
| one hostname | host-scoped `queryNmsAlarms` / Raw only |
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Prefer non-SQL; filter severity → keyword/host → time → ne_id.
|
||||
- Lists ≤2 pages; `page_size` default 50.
|
||||
- Display **host_name** only; never bare UUID to users.
|
||||
- `getManagedNe` needs managed id; NMS UUID → `getNmsNe` / `execManagedNe(nms_ne_id=...)`.
|
||||
|
||||
See [reference.md](reference.md).
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# netx-nms quick reference
|
||||
|
||||
## Freshness
|
||||
|
||||
- `runNmsDiagnostics` / `aggregateNmsAlarms` → `meta.last_seen_min` / `last_seen_max`
|
||||
- Snapshot: windows inside min~max — do not default to `now()-30m`
|
||||
|
||||
## Tools
|
||||
|
||||
| Intent | Call |
|
||||
|--------|------|
|
||||
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
|
||||
| Fiber / LOS | Raw `keyword=LOS` / `Fiber Break` |
|
||||
| Offline / BN EMS | Raw keyword=`BN EMS` |
|
||||
| Single host | `queryNmsAlarms(host_name=…)` |
|
||||
| Inventory | `queryNmsNeInventory(keyword=…)` / `getNmsNe` |
|
||||
| Paths | `findTopologyPaths(from_nms_ne_id, to_nms_ne_id)` — **common** skill |
|
||||
| SQL | `sqlQueryNms` SELECT; `statement_timeout_ms=8000` |
|
||||
|
||||
DSH: prefix every tool with `netx__`.
|
||||
94
presets/netxops/skills/ops/netx-ops/SKILL.md
Normal file
94
presets/netxops/skills/ops/netx-ops/SKILL.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
name: netx-ops
|
||||
description: >-
|
||||
Netx Ops playbook (MCP + DSH): NMS alarms/inventory/SQL plus managed-NE CLI
|
||||
login (execManagedNe batch-first) and findTopologyPaths. Trigger: alarms,
|
||||
host_name, Critical Top, LOS, 能否登录, show/display, optical, capacity A<>B,
|
||||
path between sites, netx ops.
|
||||
---
|
||||
|
||||
# netx-ops(告警 + 纳管登录)
|
||||
|
||||
**一组一个 skill。** 问「能否登录 / CLI / show」必须走本 skill 的 managed 工具,不要只查 NMS inventory。
|
||||
|
||||
## Hosts
|
||||
|
||||
| Host | Tools |
|
||||
|------|--------|
|
||||
| **MCP** `netx-mcp` | 裸名 `queryNmsAlarms` / `execManagedNe` / … |
|
||||
| **DSH** `dsh-netxops` | `netx__` + 同 stem;能力组 **ops**(默认开) |
|
||||
|
||||
REST NMS 仍 `/v1/ume/*`(`nmsProvider=zte-ume`)。优先 `nms_ne_id`;`ume_*` 别名可用。
|
||||
画布 / dual_unit → **netx-topology**(能力组 topology)。
|
||||
|
||||
## Tools
|
||||
|
||||
### NMS
|
||||
|
||||
| Purpose | Tool |
|
||||
|---------|------|
|
||||
| Alarm list / aggregate / diagnostics | `queryNmsAlarms` / `aggregateNmsAlarms` / `runNmsDiagnostics` |
|
||||
| Inventory / detail | `queryNmsNeInventory` / `getNmsNe` |
|
||||
| Raw / fields / SQL | `queryNmsAlarmsRaw` / `listNmsAlarmFields` / `aggregateNmsAlarmsRaw` / `sqlQueryNms` |
|
||||
|
||||
### Managed CLI + paths
|
||||
|
||||
| Purpose | Tool |
|
||||
|---------|------|
|
||||
| List CLI targets | `listManagedNe` / `listCliTargets` |
|
||||
| Managed detail | `getManagedNe`(**仅**纳管 id) |
|
||||
| Login / show | `execManagedNe`(batch-first) |
|
||||
| Fabric paths | `findTopologyPaths` |
|
||||
|
||||
## 「能否登录」决策树(强制)
|
||||
|
||||
1. `listCliTargets(keyword=host_or_ip)` 或 `listManagedNe(keyword=…, connect_status=pass)`
|
||||
- 命中 → `execManagedNe(ne_id|nms_ne_id, commands=["show version"])` **验证真正能登**
|
||||
- 未命中 → `queryNmsNeInventory(keyword=…)` 说明 NMS 有无 / `connection_status`,并明确:**未纳管 netx CLI 则不能用本通道登录**
|
||||
2. 禁止只查 inventory 就下「不能登录」或「能登录」结论而不尝试 `execManagedNe`(已纳管时)。
|
||||
3. NMS UUID 不要塞进 `getManagedNe`;用 `execManagedNe(nms_ne_id=…)` 或先 list 拿 managed `ne_id`。
|
||||
|
||||
## NMS 工具顺序
|
||||
|
||||
1. Freshness: `runNmsDiagnostics` / `aggregateNmsAlarms` → `meta.last_seen_*`
|
||||
2. Overview + one-page `queryNmsAlarms`
|
||||
3. Evidence: `queryNmsAlarmsRaw` (`field_preset=evidence`)
|
||||
4. Paths / CLI as needed (same skill)
|
||||
|
||||
## CLI order
|
||||
|
||||
1. `listManagedNe` / `listCliTargets`(每会话最多一次,缓存 id)
|
||||
2. 多台 → **一次** `execManagedNe`(`ne_ids` / `nms_ne_ids` / `targets`)
|
||||
3. 路径 → `findTopologyPaths`
|
||||
|
||||
### Batch 示例
|
||||
|
||||
```json
|
||||
{ "nms_ne_ids": ["uuid-a", "uuid-b"], "commands": ["show version"], "concurrency": 4 }
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"targets": [
|
||||
{"nms_ne_id": "uuid-zte", "commands": ["show opticalinfo brief"]},
|
||||
{"nms_ne_id": "uuid-hw", "commands": ["display optical-module brief"]}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Short recipes
|
||||
|
||||
| User says | Recipe |
|
||||
|-----------|--------|
|
||||
| 能否登录 / login / SSH | 上表「能否登录」决策树 |
|
||||
| fiber / LOS | Raw `keyword=LOS` / `Fiber Break` |
|
||||
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
|
||||
| capacity A<>B | paths / LLDP → 两端 optic CLI(一批) |
|
||||
|
||||
## Guardrails
|
||||
|
||||
- 展示 **host_name**;勿对用户甩裸 UUID
|
||||
- CLI 白名单:`show` / `display` / `ping` / `traceroute` …
|
||||
- 同轮禁止 N× 单台 `execManagedNe`
|
||||
|
||||
See [reference.md](reference.md).
|
||||
22
presets/netxops/skills/ops/netx-ops/reference.md
Normal file
22
presets/netxops/skills/ops/netx-ops/reference.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# netx-ops quick reference
|
||||
|
||||
## Can it log in?
|
||||
|
||||
1. `listCliTargets` / `listManagedNe` → if found, `execManagedNe(…, commands=["show version"])`
|
||||
2. Else `queryNmsNeInventory` → report NMS presence; say CLI not managed if absent from managed list
|
||||
|
||||
## NMS freshness
|
||||
|
||||
- `runNmsDiagnostics` / `aggregateNmsAlarms` → `meta.last_seen_min` / `max`
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Intent | Call |
|
||||
|--------|------|
|
||||
| Critical Top | `aggregateNmsAlarms(severity=critical, top_ne=20)` |
|
||||
| Fiber / LOS | Raw `keyword=LOS` / `Fiber Break` |
|
||||
| Inventory | `queryNmsNeInventory(keyword=…)` / `getNmsNe` |
|
||||
| CLI batch | `execManagedNe(nms_ne_ids=[…], commands=[…])` |
|
||||
| Paths | `findTopologyPaths(from_nms_ne_id, to_nms_ne_id)` |
|
||||
|
||||
DSH: prefix tools with `netx__`.
|
||||
|
|
@ -30,6 +30,7 @@ const external = [
|
|||
const entries = [
|
||||
{ entry: join(root, 'src/index.ts'), out: join(outDir, 'index.js') },
|
||||
{ entry: join(root, 'src/agent-tools.ts'), out: join(outDir, 'agent-tools.js') },
|
||||
{ entry: join(root, 'src/agent-tools-ops.ts'), out: join(outDir, 'agent-tools-ops.js') },
|
||||
{ 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') },
|
||||
|
|
|
|||
|
|
@ -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 @("nms", "common", "topology")) {
|
||||
foreach ($group in @("ops", "topology")) {
|
||||
$from = Join-Path $src $group
|
||||
$to = Join-Path $dst $group
|
||||
if (-not (Test-Path $from)) { throw "missing group $from" }
|
||||
|
|
@ -26,9 +26,11 @@ foreach ($group in @("nms", "common", "topology")) {
|
|||
Copy-Item $from $to -Recurse -Force
|
||||
}
|
||||
|
||||
# Drop legacy separate layout skill if present
|
||||
$legacyLayout = Join-Path $dst "topology-layout"
|
||||
if (Test-Path $legacyLayout) { Remove-Item $legacyLayout -Recurse -Force }
|
||||
# Drop legacy groups if present
|
||||
foreach ($legacy in @("nms", "common", "topology-layout")) {
|
||||
$legacyPath = Join-Path $dst $legacy
|
||||
if (Test-Path $legacyPath) { Remove-Item $legacyPath -Recurse -Force }
|
||||
}
|
||||
|
||||
@"
|
||||
# Mirrored from netx/skills — do not long-edit here
|
||||
|
|
@ -40,7 +42,7 @@ powershell -File .\scripts\sync-skills-from-netx.ps1
|
|||
```
|
||||
|
||||
Runtime prefers ``NETX_SKILLS_ROOT`` or sibling ``../netx/skills``.
|
||||
One topology skill covers canvas + dual_unit / layout recipes; DSH may still gate **tools** via topology vs topology-layout groups.
|
||||
Groups: **ops** (``netx-ops``) + **topology** (``netx-topology``). One group ↔ one skill.
|
||||
"@ | Set-Content -Encoding utf8 (Join-Path $dst "README.md")
|
||||
|
||||
Write-Host "Synced $src -> $dst"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/**
|
||||
* Forced common-group tools + skill (native managed CLI + findTopologyPaths).
|
||||
*
|
||||
* @deprecated Use `dsh-netxops/tools-ops`. Alias that still forces the ops group.
|
||||
* @module dsh-netxops/tools-common
|
||||
*/
|
||||
|
||||
|
|
@ -11,5 +10,5 @@ export const name = 'netxops-tools-common'
|
|||
export const inject = ['tools']
|
||||
|
||||
export function apply(ctx: Context): void {
|
||||
applyGroupToolsPlugin(ctx, { name, mode: 'forced', only: ['common'] })
|
||||
applyGroupToolsPlugin(ctx, { name, mode: 'forced', only: ['ops'] })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
/**
|
||||
* Forced nms-group tools + skill (vendor NMS adapter; current provider zte-ume).
|
||||
* Mount this export in any agent preset that needs NMS alarms/inventory.
|
||||
*
|
||||
* @deprecated Use `dsh-netxops/tools-ops`. Alias that still forces the ops group.
|
||||
* @module dsh-netxops/tools-nms
|
||||
*/
|
||||
|
||||
|
|
@ -12,5 +10,5 @@ export const name = 'netxops-tools-nms'
|
|||
export const inject = ['tools']
|
||||
|
||||
export function apply(ctx: Context): void {
|
||||
applyGroupToolsPlugin(ctx, { name, mode: 'forced', only: ['nms'] })
|
||||
applyGroupToolsPlugin(ctx, { name, mode: 'forced', only: ['ops'] })
|
||||
}
|
||||
|
|
|
|||
15
src/agent-tools-ops.ts
Normal file
15
src/agent-tools-ops.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Forced ops-group tools + skill (NMS + managed CLI + paths).
|
||||
*
|
||||
* @module dsh-netxops/tools-ops
|
||||
*/
|
||||
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { applyGroupToolsPlugin } from './netx/group-tools-plugin.ts'
|
||||
|
||||
export const name = 'netxops-tools-ops'
|
||||
export const inject = ['tools']
|
||||
|
||||
export function apply(ctx: Context): void {
|
||||
applyGroupToolsPlugin(ctx, { name, mode: 'forced', only: ['ops'] })
|
||||
}
|
||||
|
|
@ -364,31 +364,17 @@ export function NetxopsCard(props: NetxopsCardProps) {
|
|||
</div>
|
||||
<p className="dsh-nx-hint">{t('capabilityGroupsHint')}</p>
|
||||
<CapabilityGroupBlock
|
||||
title={t('groupNms')}
|
||||
title={t('groupOps')}
|
||||
inPresetLabel={t('groupInPreset')}
|
||||
publicLabel={t('groupPublic')}
|
||||
inPreset={state.groupNmsInPreset}
|
||||
published={state.groupNmsPublic}
|
||||
inPreset={state.groupOpsInPreset}
|
||||
published={state.groupOpsPublic}
|
||||
disabled={disabled}
|
||||
onEditInPreset={(checked) => {
|
||||
props.edit('groupNmsInPreset', checked ? 'true' : 'false')
|
||||
props.edit('groupOpsInPreset', checked ? 'true' : 'false')
|
||||
}}
|
||||
onEditPublic={(checked) => {
|
||||
props.edit('groupNmsPublic', checked ? 'true' : 'false')
|
||||
}}
|
||||
/>
|
||||
<CapabilityGroupBlock
|
||||
title={t('groupCommon')}
|
||||
inPresetLabel={t('groupInPreset')}
|
||||
publicLabel={t('groupPublic')}
|
||||
inPreset={state.groupCommonInPreset}
|
||||
published={state.groupCommonPublic}
|
||||
disabled={disabled}
|
||||
onEditInPreset={(checked) => {
|
||||
props.edit('groupCommonInPreset', checked ? 'true' : 'false')
|
||||
}}
|
||||
onEditPublic={(checked) => {
|
||||
props.edit('groupCommonPublic', checked ? 'true' : 'false')
|
||||
props.edit('groupOpsPublic', checked ? 'true' : 'false')
|
||||
}}
|
||||
/>
|
||||
<CapabilityGroupBlock
|
||||
|
|
|
|||
|
|
@ -35,10 +35,8 @@ export interface NetxopsSettings {
|
|||
alarmDeliverIm?: boolean
|
||||
imBotId?: string
|
||||
imTargetId?: string
|
||||
groupNmsInPreset?: boolean
|
||||
groupNmsPublic?: boolean
|
||||
groupCommonInPreset?: boolean
|
||||
groupCommonPublic?: boolean
|
||||
groupOpsInPreset?: boolean
|
||||
groupOpsPublic?: boolean
|
||||
groupTopologyInPreset?: boolean
|
||||
groupTopologyPublic?: boolean
|
||||
nmsProvider?: string
|
||||
|
|
@ -54,10 +52,8 @@ interface CredentialState {
|
|||
export interface NetxopsCardState extends CardShell {
|
||||
apiUrl: CardFieldState
|
||||
lang: CardFieldState
|
||||
groupNmsInPreset: CardFieldState
|
||||
groupNmsPublic: CardFieldState
|
||||
groupCommonInPreset: CardFieldState
|
||||
groupCommonPublic: CardFieldState
|
||||
groupOpsInPreset: CardFieldState
|
||||
groupOpsPublic: CardFieldState
|
||||
groupTopologyInPreset: CardFieldState
|
||||
groupTopologyPublic: CardFieldState
|
||||
nmsProvider: CardFieldState
|
||||
|
|
@ -113,10 +109,8 @@ export class NetxopsCardController {
|
|||
textField('apiUrl'),
|
||||
textField('lang'),
|
||||
textField('nmsProvider'),
|
||||
booleanFieldPersistFalse('groupNmsInPreset'),
|
||||
booleanField('groupNmsPublic'),
|
||||
booleanFieldPersistFalse('groupCommonInPreset'),
|
||||
booleanField('groupCommonPublic'),
|
||||
booleanFieldPersistFalse('groupOpsInPreset'),
|
||||
booleanField('groupOpsPublic'),
|
||||
booleanField('groupTopologyInPreset'),
|
||||
booleanField('groupTopologyPublic'),
|
||||
booleanField('alarmPushEnabled'),
|
||||
|
|
@ -244,10 +238,8 @@ export class NetxopsCardController {
|
|||
apiUrl: this.form.field('apiUrl'),
|
||||
lang: this.form.field('lang'),
|
||||
nmsProvider: this.form.field('nmsProvider'),
|
||||
groupNmsInPreset: this.form.field('groupNmsInPreset'),
|
||||
groupNmsPublic: this.form.field('groupNmsPublic'),
|
||||
groupCommonInPreset: this.form.field('groupCommonInPreset'),
|
||||
groupCommonPublic: this.form.field('groupCommonPublic'),
|
||||
groupOpsInPreset: this.form.field('groupOpsInPreset'),
|
||||
groupOpsPublic: this.form.field('groupOpsPublic'),
|
||||
groupTopologyInPreset: this.form.field('groupTopologyInPreset'),
|
||||
groupTopologyPublic: this.form.field('groupTopologyPublic'),
|
||||
alarmPushEnabled: this.form.field('alarmPushEnabled'),
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ export type NetxopsLocaleKey =
|
|||
| 'capabilityGroupsHint'
|
||||
| 'nmsProvider'
|
||||
| 'nmsProviderHint'
|
||||
| 'groupNms'
|
||||
| 'groupCommon'
|
||||
| 'groupOps'
|
||||
| 'groupTopology'
|
||||
| 'groupInPreset'
|
||||
| 'groupPublic'
|
||||
|
|
@ -58,17 +57,16 @@ export type NetxopsLocaleKey =
|
|||
|
||||
export const en: Record<NetxopsLocaleKey, string> = {
|
||||
title: 'Netx Ops',
|
||||
description: 'netx API, bearer token, and capability groups (nms / common / topology).',
|
||||
description: 'netx API, bearer token, and capability groups (ops / topology).',
|
||||
apiUrl: 'API URL',
|
||||
apiUrlHint: 'netx REST root, e.g. http://127.0.0.1:8890 (also used for alarm subscribe)',
|
||||
lang: 'Language',
|
||||
langHint: 'Response language hint (zh / en).',
|
||||
capabilityGroups: 'Capability groups',
|
||||
capabilityGroupsHint: 'One group ↔ one skill (netx-nms / netx-common / netx-topology). Defaults: nms + common in preset; topology and all public off. Save, then open a new session. Other agents can mount dsh-netxops/tools-nms|common|topology.',
|
||||
capabilityGroupsHint: 'One group ↔ one skill. ops = netx-ops (alarms + CLI login + paths); topology = netx-topology. Defaults: ops on; topology and public off. New sessions after save. Other agents: dsh-netxops/tools-ops|topology.',
|
||||
nmsProvider: 'nms provider',
|
||||
nmsProviderHint: 'Vendor adapter id. Supported today: zte-ume (REST still /v1/ume/*; model tools are netx__*Nms*).',
|
||||
groupNms: 'nms — skill netx-nms (alarms / inventory / SQL)',
|
||||
groupCommon: 'common — skill netx-common (managed CLI + findTopologyPaths)',
|
||||
groupOps: 'ops — skill netx-ops (NMS alarms/inventory + managed CLI login + paths)',
|
||||
groupTopology: 'topology — skill netx-topology (canvas / fabric / dual_unit)',
|
||||
groupInPreset: 'In Netx Ops preset',
|
||||
groupPublic: 'Publish to other presets',
|
||||
|
|
@ -115,17 +113,16 @@ export const en: Record<NetxopsLocaleKey, string> = {
|
|||
|
||||
export const zh: Record<NetxopsLocaleKey, string> = {
|
||||
title: 'Netx Ops',
|
||||
description: 'netx API、Bearer Token,以及能力组(nms / common / topology)。',
|
||||
description: 'netx API、Bearer Token,以及能力组(ops / topology)。',
|
||||
apiUrl: 'API 地址',
|
||||
apiUrlHint: 'netx REST 根地址,例如 http://127.0.0.1:8890(告警订阅复用同一地址)',
|
||||
lang: '语言',
|
||||
langHint: '响应语言提示(zh / en)。',
|
||||
capabilityGroups: '能力组',
|
||||
capabilityGroupsHint: '一组一个 skill(netx-nms / netx-common / netx-topology)。默认:nms + common 进入预设;topology 与全部公开关闭。保存后请新开会话。其他 Agent 可挂 dsh-netxops/tools-nms|common|topology。',
|
||||
capabilityGroupsHint: '一组一个 skill。ops = netx-ops(告警 + 纳管登录 + 路径);topology = netx-topology。默认 ops 开;topology / 公开关。保存后新会话生效。其他 Agent:dsh-netxops/tools-ops|topology。',
|
||||
nmsProvider: 'nms provider',
|
||||
nmsProviderHint: '厂商适配器 id。当前支持:zte-ume(REST 仍为 /v1/ume/*;模型工具名为 netx__*Nms*)。',
|
||||
groupNms: 'nms — skill netx-nms(告警 / 库存 / SQL)',
|
||||
groupCommon: 'common — skill netx-common(纳管 CLI + findTopologyPaths)',
|
||||
groupOps: 'ops — skill netx-ops(NMS 告警/库存 + 纳管 CLI 登录 + 路径)',
|
||||
groupTopology: 'topology — skill netx-topology(画布 / Fabric / dual_unit)',
|
||||
groupInPreset: '在 Netx Ops 预设中启用',
|
||||
groupPublic: '对其他预设公开',
|
||||
|
|
|
|||
18
src/index.ts
18
src/index.ts
|
|
@ -91,14 +91,10 @@ export interface Config {
|
|||
* (REST `/v1/ume/*`; model tools are generic `netx__*Nms*`).
|
||||
*/
|
||||
nmsProvider: string
|
||||
/** nms tools/skills in the Netx Ops preset (default on). */
|
||||
groupNmsInPreset: boolean
|
||||
/** Publish nms tools/skills to other presets via the host layer (default off). */
|
||||
groupNmsPublic: boolean
|
||||
/** common tools/skills in the Netx Ops preset (default on). */
|
||||
groupCommonInPreset: boolean
|
||||
/** Publish common tools/skills to other presets (default off). */
|
||||
groupCommonPublic: boolean
|
||||
/** ops tools/skills in the Netx Ops preset (default on) — NMS + CLI + paths. */
|
||||
groupOpsInPreset: boolean
|
||||
/** Publish ops tools/skills to other presets (default off). */
|
||||
groupOpsPublic: boolean
|
||||
/** topology canvas / layout tools in the Netx Ops preset (default off). */
|
||||
groupTopologyInPreset: boolean
|
||||
/** Publish topology tools/skills to other presets (default off). */
|
||||
|
|
@ -117,10 +113,8 @@ export const Config: z<Config> = z.object({
|
|||
imBotId: z.string().default(''),
|
||||
imTargetId: z.string().default(''),
|
||||
nmsProvider: z.string().default('zte-ume'),
|
||||
groupNmsInPreset: z.boolean().default(true),
|
||||
groupNmsPublic: z.boolean().default(false),
|
||||
groupCommonInPreset: z.boolean().default(true),
|
||||
groupCommonPublic: z.boolean().default(false),
|
||||
groupOpsInPreset: z.boolean().default(true),
|
||||
groupOpsPublic: z.boolean().default(false),
|
||||
groupTopologyInPreset: z.boolean().default(false),
|
||||
groupTopologyPublic: z.boolean().default(false),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@
|
|||
*
|
||||
* Rule: **one group ↔ one skill** (dirs under `netx/skills/<group>/`).
|
||||
*
|
||||
* - `nms` → skill `netx-nms` — vendor NMS adapter (zte-ume behind generic names)
|
||||
* - `common` → skill `netx-common` — managed CLI + findTopologyPaths
|
||||
* - `topology` → skill `netx-topology` — canvas / fabric / dual_unit / layout recipes
|
||||
* - `ops` → skill `netx-ops` — NMS alarms/inventory/SQL + managed CLI + findTopologyPaths
|
||||
* - `topology` → skill `netx-topology` — canvas / fabric / dual_unit / layout
|
||||
*/
|
||||
|
||||
/** Stable group ids used in settings, skill dirs, and registration filters. */
|
||||
export type NetxCapabilityGroupId = 'nms' | 'common' | 'topology'
|
||||
export type NetxCapabilityGroupId = 'ops' | 'topology'
|
||||
|
||||
/** Per-group exposure knobs. */
|
||||
export interface NetxGroupExposure {
|
||||
|
|
@ -24,29 +23,26 @@ export type NetxCapabilityGroups = Record<NetxCapabilityGroupId, NetxGroupExposu
|
|||
|
||||
/** Flat settings fields (Plugins card + schemastery Config). */
|
||||
export interface NetxCapabilityGroupSettingsFields {
|
||||
groupNmsInPreset: boolean
|
||||
groupNmsPublic: boolean
|
||||
groupCommonInPreset: boolean
|
||||
groupCommonPublic: boolean
|
||||
groupOpsInPreset: boolean
|
||||
groupOpsPublic: boolean
|
||||
groupTopologyInPreset: boolean
|
||||
groupTopologyPublic: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Default: nms + common in Ops preset; topology and all public off.
|
||||
* Default: ops in Ops preset; topology and all public off.
|
||||
*/
|
||||
export const DEFAULT_CAPABILITY_GROUPS: NetxCapabilityGroups = Object.freeze({
|
||||
nms: Object.freeze({ inPreset: true, public: false }),
|
||||
common: Object.freeze({ inPreset: true, public: false }),
|
||||
ops: Object.freeze({ inPreset: true, public: false }),
|
||||
topology: Object.freeze({ inPreset: false, public: false }),
|
||||
})
|
||||
|
||||
/**
|
||||
* Model-facing tool names (`netx__*`) owned by each group.
|
||||
* nms tools use generic `Nms` names; HTTP still hits the configured NMS provider adapter.
|
||||
* NMS tools use generic `Nms` names; HTTP still hits the configured NMS provider adapter.
|
||||
*/
|
||||
export const TOOLS_BY_GROUP: Readonly<Record<NetxCapabilityGroupId, readonly string[]>> = Object.freeze({
|
||||
nms: Object.freeze([
|
||||
ops: Object.freeze([
|
||||
'netx__queryNmsAlarms',
|
||||
'netx__aggregateNmsAlarms',
|
||||
'netx__runNmsDiagnostics',
|
||||
|
|
@ -56,8 +52,6 @@ export const TOOLS_BY_GROUP: Readonly<Record<NetxCapabilityGroupId, readonly str
|
|||
'netx__aggregateNmsAlarmsRaw',
|
||||
'netx__listNmsAlarmFields',
|
||||
'netx__sqlQueryNms',
|
||||
]),
|
||||
common: Object.freeze([
|
||||
'netx__listManagedNe',
|
||||
'netx__getManagedNe',
|
||||
'netx__execManagedNe',
|
||||
|
|
@ -86,24 +80,29 @@ export const TOOLS_BY_GROUP: Readonly<Record<NetxCapabilityGroupId, readonly str
|
|||
|
||||
/** Skill directory name under skills root / `presets/netxops/skills/<group>/`. */
|
||||
export const SKILL_DIR_BY_GROUP: Readonly<Record<NetxCapabilityGroupId, string>> = Object.freeze({
|
||||
nms: 'nms',
|
||||
common: 'common',
|
||||
ops: 'ops',
|
||||
topology: 'topology',
|
||||
})
|
||||
|
||||
export const CAPABILITY_GROUP_IDS: readonly NetxCapabilityGroupId[] = Object.freeze([
|
||||
'nms',
|
||||
'common',
|
||||
'ops',
|
||||
'topology',
|
||||
])
|
||||
|
||||
/**
|
||||
* Build group policy from flat settings / Config fields.
|
||||
* Accepts legacy `groupManagedNe*` as aliases of `groupCommon*`.
|
||||
* Legacy `groupTopologyLayout*` ORs into `topology` (layout tools now live in topology).
|
||||
*
|
||||
* Legacy aliases (all map into `ops`):
|
||||
* - `groupNms*` / `groupCommon*` / `groupManagedNe*`
|
||||
*
|
||||
* Legacy `groupTopologyLayout*` ORs into `topology`.
|
||||
*/
|
||||
export function capabilityGroupsFromSettings(
|
||||
fields: Partial<NetxCapabilityGroupSettingsFields & {
|
||||
groupNmsInPreset?: boolean
|
||||
groupNmsPublic?: boolean
|
||||
groupCommonInPreset?: boolean
|
||||
groupCommonPublic?: boolean
|
||||
groupManagedNeInPreset?: boolean
|
||||
groupManagedNePublic?: boolean
|
||||
groupTopologyLayoutInPreset?: boolean
|
||||
|
|
@ -111,19 +110,29 @@ export function capabilityGroupsFromSettings(
|
|||
}> | null | undefined,
|
||||
): NetxCapabilityGroups {
|
||||
const src = fields ?? {}
|
||||
const commonInPreset = src.groupCommonInPreset !== undefined
|
||||
? src.groupCommonInPreset !== false
|
||||
: src.groupManagedNeInPreset !== false
|
||||
const commonPublic = src.groupCommonPublic === true
|
||||
|| src.groupManagedNePublic === true
|
||||
|
||||
let opsInPreset = true
|
||||
if (src.groupOpsInPreset !== undefined) {
|
||||
opsInPreset = src.groupOpsInPreset !== false
|
||||
} else {
|
||||
const legacy = [
|
||||
src.groupNmsInPreset,
|
||||
src.groupCommonInPreset,
|
||||
src.groupManagedNeInPreset,
|
||||
].filter((v): v is boolean => v !== undefined)
|
||||
if (legacy.length > 0) {
|
||||
// Any explicit true → on; all explicit false → off.
|
||||
opsInPreset = legacy.some((v) => v === true)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nms: {
|
||||
inPreset: src.groupNmsInPreset !== false,
|
||||
public: src.groupNmsPublic === true,
|
||||
},
|
||||
common: {
|
||||
inPreset: commonInPreset,
|
||||
public: commonPublic,
|
||||
ops: {
|
||||
inPreset: opsInPreset,
|
||||
public: src.groupOpsPublic === true
|
||||
|| src.groupNmsPublic === true
|
||||
|| src.groupCommonPublic === true
|
||||
|| src.groupManagedNePublic === true,
|
||||
},
|
||||
topology: {
|
||||
inPreset: src.groupTopologyInPreset === true
|
||||
|
|
@ -136,9 +145,6 @@ export function capabilityGroupsFromSettings(
|
|||
|
||||
/**
|
||||
* Groups enabled for one registration plane.
|
||||
* @param groups - published policy.
|
||||
* @param plane - preset scope vs host public layer.
|
||||
* @param only - when set, intersect with this allow-list (per-export tools packages).
|
||||
*/
|
||||
export function groupsForPlane(
|
||||
groups: NetxCapabilityGroups | undefined,
|
||||
|
|
@ -155,13 +161,16 @@ export function groupsForPlane(
|
|||
}
|
||||
|
||||
/**
|
||||
* Force-enable the listed groups (for `dsh-netxops/tools-<group>` mounts that
|
||||
* intentionally ignore Ops-preset inPreset flags).
|
||||
* Force-enable the listed groups (for `dsh-netxops/tools-<group>` mounts).
|
||||
* Legacy `nms` / `common` aliases resolve to `ops`.
|
||||
*/
|
||||
export function groupsForced(
|
||||
only: readonly NetxCapabilityGroupId[],
|
||||
only: readonly (NetxCapabilityGroupId | 'nms' | 'common')[],
|
||||
): NetxCapabilityGroupId[] {
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => only.includes(id))
|
||||
const normalized = only.map((id) => (
|
||||
id === 'nms' || id === 'common' ? 'ops' as const : id
|
||||
))
|
||||
return CAPABILITY_GROUP_IDS.filter((id) => normalized.includes(id))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,34 +15,32 @@ import {
|
|||
TOOLS_BY_GROUP,
|
||||
} from '../src/netx/capability-groups.ts'
|
||||
|
||||
test('defaults: nms + common in preset; topology and public off', () => {
|
||||
test('defaults: ops in preset; topology and public off', () => {
|
||||
assert.deepEqual(DEFAULT_CAPABILITY_GROUPS, {
|
||||
nms: { inPreset: true, public: false },
|
||||
common: { inPreset: true, public: false },
|
||||
ops: { inPreset: true, public: false },
|
||||
topology: { inPreset: false, public: false },
|
||||
})
|
||||
assert.deepEqual(groupsForPlane(undefined, 'preset'), ['nms', 'common'])
|
||||
assert.deepEqual(groupsForPlane(undefined, 'preset'), ['ops'])
|
||||
assert.deepEqual(groupsForPlane(undefined, 'public'), [])
|
||||
})
|
||||
|
||||
test('one group one skill dir; topology owns canvas + layout tools', () => {
|
||||
test('one group one skill dir; ops owns NMS + managed CLI; topology owns canvas', () => {
|
||||
assert.deepEqual(SKILL_DIR_BY_GROUP, {
|
||||
nms: 'nms',
|
||||
common: 'common',
|
||||
ops: 'ops',
|
||||
topology: 'topology',
|
||||
})
|
||||
assert.ok(TOOLS_BY_GROUP.common.includes('netx__findTopologyPaths'))
|
||||
assert.ok(TOOLS_BY_GROUP.ops.includes('netx__findTopologyPaths'))
|
||||
assert.ok(TOOLS_BY_GROUP.ops.includes('netx__execManagedNe'))
|
||||
assert.ok(TOOLS_BY_GROUP.ops.includes('netx__queryNmsAlarms'))
|
||||
assert.ok(TOOLS_BY_GROUP.topology.includes('netx__getTopologyTree'))
|
||||
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.nms.includes('netx__queryNmsAlarms'))
|
||||
})
|
||||
|
||||
test('capabilityGroupsFromSettings honors explicit false for inPreset defaults', () => {
|
||||
const groups = capabilityGroupsFromSettings({
|
||||
groupNmsInPreset: false,
|
||||
groupCommonInPreset: false,
|
||||
groupOpsInPreset: false,
|
||||
groupTopologyInPreset: true,
|
||||
groupTopologyPublic: true,
|
||||
})
|
||||
|
|
@ -53,18 +51,27 @@ test('capabilityGroupsFromSettings honors explicit false for inPreset defaults',
|
|||
assert.equal(toolNamesForGroups(['topology']).has('netx__queryNmsAlarms'), false)
|
||||
})
|
||||
|
||||
test('legacy groupManagedNe* maps to common; legacy layout flags OR into topology', () => {
|
||||
test('legacy groupNms/common/managedNe map to ops; legacy layout flags OR into topology', () => {
|
||||
const groups = capabilityGroupsFromSettings({
|
||||
groupManagedNeInPreset: false,
|
||||
groupNmsInPreset: false,
|
||||
groupCommonInPreset: false,
|
||||
groupManagedNePublic: true,
|
||||
groupTopologyLayoutInPreset: true,
|
||||
})
|
||||
assert.equal(groups.common.inPreset, false)
|
||||
assert.equal(groups.common.public, true)
|
||||
assert.equal(groups.ops.inPreset, false)
|
||||
assert.equal(groups.ops.public, true)
|
||||
assert.equal(groups.topology.inPreset, true)
|
||||
})
|
||||
|
||||
test('groupsForced ignores settings for per-export mounts', () => {
|
||||
assert.deepEqual(groupsForced(['common']), ['common'])
|
||||
assert.deepEqual(groupsForced(['nms', 'topology']), ['nms', 'topology'])
|
||||
test('legacy any-true turns ops inPreset on', () => {
|
||||
const groups = capabilityGroupsFromSettings({
|
||||
groupNmsInPreset: false,
|
||||
groupCommonInPreset: true,
|
||||
})
|
||||
assert.equal(groups.ops.inPreset, true)
|
||||
})
|
||||
|
||||
test('groupsForced ignores settings for per-export mounts', () => {
|
||||
assert.deepEqual(groupsForced(['ops']), ['ops'])
|
||||
assert.deepEqual(groupsForced(['ops', 'topology']), ['ops', 'topology'])
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { join } from 'node:path'
|
|||
import { opsSkillsRoot } from '../src/netx/group-skills.ts'
|
||||
import { SKILL_DIR_BY_GROUP } from '../src/netx/capability-groups.ts'
|
||||
|
||||
test('opsSkillsRoot points at nms / common / topology playbook dirs', () => {
|
||||
test('opsSkillsRoot points at ops / topology playbook dirs', () => {
|
||||
const root = opsSkillsRoot()
|
||||
assert.ok(existsSync(root), root)
|
||||
for (const dir of Object.values(SKILL_DIR_BY_GROUP)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue