mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-10 04:40:47 +08:00
Ship capability groups (nms/common/topology) with shared netx skills.
One group maps to one skill and tools-*; rename NMS tools to *Nms*; sync playbooks from netx/skills. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
83c3796fbb
commit
3c9a383071
47 changed files with 11790 additions and 680 deletions
430
lib/client.js
430
lib/client.js
|
|
@ -112,6 +112,68 @@ function alarmPushTone(phase) {
|
|||
}
|
||||
}
|
||||
|
||||
// src/client/im-delivery-catalog.ts
|
||||
var IM_DELIVERY_CATALOG_ENDPOINT = "im-delivery.catalog";
|
||||
var EMPTY_IM_DELIVERY_CATALOG = {
|
||||
available: true,
|
||||
options: [],
|
||||
hint: "",
|
||||
loading: false
|
||||
};
|
||||
function asImDeliveryCatalog(value) {
|
||||
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
||||
return { ...EMPTY_IM_DELIVERY_CATALOG, available: false };
|
||||
}
|
||||
const row = value;
|
||||
const raw = Array.isArray(row.options) ? row.options : [];
|
||||
const options = [];
|
||||
for (const entry of raw) {
|
||||
if (entry === null || typeof entry !== "object" || Array.isArray(entry))
|
||||
continue;
|
||||
const item = entry;
|
||||
const botId = typeof item.botId === "string" ? item.botId.trim() : "";
|
||||
const targetId = typeof item.targetId === "string" ? item.targetId.trim() : "";
|
||||
if (!botId || !targetId)
|
||||
continue;
|
||||
options.push({
|
||||
botId,
|
||||
targetId,
|
||||
name: typeof item.name === "string" && item.name.trim() ? item.name.trim() : targetId,
|
||||
kind: typeof item.kind === "string" ? item.kind : "",
|
||||
channel: typeof item.channel === "string" ? item.channel : "im"
|
||||
});
|
||||
}
|
||||
return {
|
||||
available: row.available !== false,
|
||||
options,
|
||||
hint: typeof row.hint === "string" ? row.hint : "",
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
async function fetchImDeliveryCatalog(call, signal) {
|
||||
const result = await call(NETXOPS_RPC_CHANNEL, IM_DELIVERY_CATALOG_ENDPOINT, {}, signal);
|
||||
if (result !== null && typeof result === "object" && result.ok === true) {
|
||||
return asImDeliveryCatalog(result.value);
|
||||
}
|
||||
if (result !== null && typeof result === "object" && result.ok === false) {
|
||||
return {
|
||||
...EMPTY_IM_DELIVERY_CATALOG,
|
||||
available: false,
|
||||
hint: String(result.error?.message ?? "rpc failed")
|
||||
};
|
||||
}
|
||||
return asImDeliveryCatalog(result);
|
||||
}
|
||||
function imCatalogOptionKey(botId, targetId) {
|
||||
return `${botId}::${targetId}`;
|
||||
}
|
||||
function parseImCatalogOptionKey(value) {
|
||||
const at = value.indexOf("::");
|
||||
if (at <= 0)
|
||||
return { botId: "", targetId: "" };
|
||||
return { botId: value.slice(0, at), targetId: value.slice(at + 2) };
|
||||
}
|
||||
|
||||
// src/client/styles.ts
|
||||
var STYLE_ID = "dsh-netxops-card-css";
|
||||
var CSS = `
|
||||
|
|
@ -142,11 +204,15 @@ var CSS = `
|
|||
.dsh-nx-reset{border:none;background:none;padding:0;font:inherit;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary);cursor:pointer}
|
||||
.dsh-nx-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
||||
.dsh-nx-input{height:34px;padding:0 12px;border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;background:var(--dsw-alias-bg-layer-3);font:inherit;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary)}
|
||||
.dsh-nx-select{appearance:auto;cursor:pointer}
|
||||
.dsh-nx-input:focus-visible{outline:none;border-color:var(--dsw-alias-brand-primary)}
|
||||
.dsh-nx-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
||||
.dsh-nx-hint{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
|
||||
.dsh-nx-checkRow{display:flex;align-items:flex-start;gap:10px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary);cursor:pointer}
|
||||
.dsh-nx-checkRow input{margin-top:2px;flex:none}
|
||||
.dsh-nx-groupBlock{display:flex;flex-direction:column;gap:8px;padding:8px 0 4px}
|
||||
.dsh-nx-groupTitle{font-size:12px;font-weight:600;line-height:1.5;color:var(--dsw-alias-label-primary)}
|
||||
.dsh-nx-groupChecks{display:flex;flex-direction:column;gap:6px;padding-left:2px}
|
||||
.dsh-nx-invalid{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-error)}
|
||||
.dsh-nx-footer{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 0 4px;border-top:.5px solid var(--dsw-alias-border-l2)}
|
||||
.dsh-nx-failed{flex:1;min-width:0;margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-error)}
|
||||
|
|
@ -247,6 +313,151 @@ function ValueField(props) {
|
|||
]
|
||||
});
|
||||
}
|
||||
function ImDeliveryPicker(props) {
|
||||
const options = props.catalog.options;
|
||||
const selectedKey = props.botId && props.targetId ? imCatalogOptionKey(props.botId, props.targetId) : "";
|
||||
const matched = options.some((row) => imCatalogOptionKey(row.botId, row.targetId) === selectedKey);
|
||||
const [manual, setManual] = import_react.useState(() => !!(selectedKey && !matched));
|
||||
import_react.useEffect(() => {
|
||||
if (selectedKey && matched)
|
||||
setManual(false);
|
||||
else if (selectedKey && !matched)
|
||||
setManual(true);
|
||||
}, [selectedKey, matched]);
|
||||
const selectValue = manual ? "__manual__" : matched ? selectedKey : "";
|
||||
const showManual = manual || selectedKey && !matched;
|
||||
return /* @__PURE__ */ jsx_runtime.jsxs(jsx_runtime.Fragment, {
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsxs("div", {
|
||||
className: "dsh-nx-field",
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsx("div", {
|
||||
className: "dsh-nx-fieldHead",
|
||||
children: /* @__PURE__ */ jsx_runtime.jsx("label", {
|
||||
className: "dsh-nx-label",
|
||||
htmlFor: "netxops-im-target",
|
||||
children: props.labels.target
|
||||
})
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsxs("select", {
|
||||
id: "netxops-im-target",
|
||||
className: "dsh-nx-input dsh-nx-select",
|
||||
value: selectValue,
|
||||
disabled: props.disabled,
|
||||
onChange: (event) => {
|
||||
const value = event.target.value;
|
||||
if (value === "__manual__") {
|
||||
setManual(true);
|
||||
return;
|
||||
}
|
||||
setManual(false);
|
||||
if (!value) {
|
||||
props.onPick("", "");
|
||||
return;
|
||||
}
|
||||
const next = parseImCatalogOptionKey(value);
|
||||
props.onPick(next.botId, next.targetId);
|
||||
},
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsx("option", {
|
||||
value: "",
|
||||
children: options.length === 0 ? props.labels.none : `— ${props.labels.target} —`
|
||||
}),
|
||||
options.map((row) => /* @__PURE__ */ jsx_runtime.jsx("option", {
|
||||
value: imCatalogOptionKey(row.botId, row.targetId),
|
||||
children: `${row.name} · ${row.channel || "im"} · ${row.targetId}`
|
||||
}, imCatalogOptionKey(row.botId, row.targetId))),
|
||||
/* @__PURE__ */ jsx_runtime.jsx("option", {
|
||||
value: "__manual__",
|
||||
children: props.labels.manual
|
||||
})
|
||||
]
|
||||
}),
|
||||
!props.catalog.available || options.length === 0 ? /* @__PURE__ */ jsx_runtime.jsx("p", {
|
||||
className: "dsh-nx-hint",
|
||||
children: props.catalog.hint || props.labels.unavailable
|
||||
}) : null
|
||||
]
|
||||
}),
|
||||
showManual ? /* @__PURE__ */ jsx_runtime.jsxs(jsx_runtime.Fragment, {
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsx(ValueField, {
|
||||
id: "netxops-im-bot-id",
|
||||
label: props.labels.botId,
|
||||
hint: props.labels.botHint,
|
||||
field: props.botField,
|
||||
overriddenLabel: props.labels.overridden,
|
||||
resetLabel: props.labels.reset,
|
||||
invalidLabel: props.labels.invalid,
|
||||
disabled: props.disabled,
|
||||
onEdit: props.onEditBot,
|
||||
onReset: props.onResetBot
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(ValueField, {
|
||||
id: "netxops-im-target-id",
|
||||
label: props.labels.targetId,
|
||||
hint: props.labels.targetHint,
|
||||
field: props.targetField,
|
||||
overriddenLabel: props.labels.overridden,
|
||||
resetLabel: props.labels.reset,
|
||||
invalidLabel: props.labels.invalid,
|
||||
disabled: props.disabled,
|
||||
onEdit: props.onEditTarget,
|
||||
onReset: props.onResetTarget
|
||||
})
|
||||
]
|
||||
}) : null
|
||||
]
|
||||
});
|
||||
}
|
||||
function CapabilityGroupBlock(props) {
|
||||
return /* @__PURE__ */ jsx_runtime.jsxs("div", {
|
||||
className: "dsh-nx-groupBlock",
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsx("div", {
|
||||
className: "dsh-nx-groupTitle",
|
||||
children: props.title
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsxs("div", {
|
||||
className: "dsh-nx-groupChecks",
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsxs("label", {
|
||||
className: "dsh-nx-checkRow",
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsx("input", {
|
||||
type: "checkbox",
|
||||
checked: props.inPreset.text === "true",
|
||||
disabled: props.disabled,
|
||||
onChange: (event) => {
|
||||
props.onEditInPreset(event.target.checked);
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
||||
children: props.inPresetLabel
|
||||
})
|
||||
]
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsxs("label", {
|
||||
className: "dsh-nx-checkRow",
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsx("input", {
|
||||
type: "checkbox",
|
||||
checked: props.published.text === "true",
|
||||
disabled: props.disabled,
|
||||
onChange: (event) => {
|
||||
props.onEditPublic(event.target.checked);
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
||||
children: props.publicLabel
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
function NetxopsCard(props) {
|
||||
ensureStyles();
|
||||
const { t } = props;
|
||||
|
|
@ -388,6 +599,80 @@ function NetxopsCard(props) {
|
|||
props.resetField("lang");
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(ValueField, {
|
||||
id: "netxops-nms-provider",
|
||||
label: t("nmsProvider"),
|
||||
hint: t("nmsProviderHint"),
|
||||
field: state.nmsProvider,
|
||||
overriddenLabel: t("overridden"),
|
||||
resetLabel: t("reset"),
|
||||
invalidLabel: t("invalid"),
|
||||
disabled,
|
||||
onEdit: (text) => {
|
||||
props.edit("nmsProvider", text);
|
||||
},
|
||||
onReset: () => {
|
||||
props.resetField("nmsProvider");
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsxs("div", {
|
||||
className: "dsh-nx-field",
|
||||
children: [
|
||||
/* @__PURE__ */ jsx_runtime.jsx("div", {
|
||||
className: "dsh-nx-fieldHead",
|
||||
children: /* @__PURE__ */ jsx_runtime.jsx("span", {
|
||||
className: "dsh-nx-label",
|
||||
children: t("capabilityGroups")
|
||||
})
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx("p", {
|
||||
className: "dsh-nx-hint",
|
||||
children: t("capabilityGroupsHint")
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(CapabilityGroupBlock, {
|
||||
title: t("groupNms"),
|
||||
inPresetLabel: t("groupInPreset"),
|
||||
publicLabel: t("groupPublic"),
|
||||
inPreset: state.groupNmsInPreset,
|
||||
published: state.groupNmsPublic,
|
||||
disabled,
|
||||
onEditInPreset: (checked) => {
|
||||
props.edit("groupNmsInPreset", 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");
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(CapabilityGroupBlock, {
|
||||
title: t("groupTopology"),
|
||||
inPresetLabel: t("groupInPreset"),
|
||||
publicLabel: t("groupPublic"),
|
||||
inPreset: state.groupTopologyInPreset,
|
||||
published: state.groupTopologyPublic,
|
||||
disabled,
|
||||
onEditInPreset: (checked) => {
|
||||
props.edit("groupTopologyInPreset", checked ? "true" : "false");
|
||||
},
|
||||
onEditPublic: (checked) => {
|
||||
props.edit("groupTopologyPublic", checked ? "true" : "false");
|
||||
}
|
||||
})
|
||||
]
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsxs("div", {
|
||||
className: "dsh-nx-field",
|
||||
children: [
|
||||
|
|
@ -558,35 +843,40 @@ function NetxopsCard(props) {
|
|||
})
|
||||
]
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(ValueField, {
|
||||
id: "netxops-im-bot-id",
|
||||
label: t("imBotId"),
|
||||
hint: t("imBotIdHint"),
|
||||
field: state.imBotId,
|
||||
overriddenLabel: t("overridden"),
|
||||
resetLabel: t("reset"),
|
||||
invalidLabel: t("invalid"),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(ImDeliveryPicker, {
|
||||
catalog: state.imDeliveryCatalog,
|
||||
botId: state.imBotId.text,
|
||||
targetId: state.imTargetId.text,
|
||||
disabled,
|
||||
onEdit: (text) => {
|
||||
labels: {
|
||||
target: t("imTarget"),
|
||||
none: t("imTargetNone"),
|
||||
manual: t("imTargetManual"),
|
||||
unavailable: t("imCatalogUnavailable"),
|
||||
botId: t("imBotId"),
|
||||
botHint: t("imBotIdHint"),
|
||||
targetId: t("imTargetId"),
|
||||
targetHint: t("imTargetIdHint"),
|
||||
overridden: t("overridden"),
|
||||
reset: t("reset"),
|
||||
invalid: t("invalid")
|
||||
},
|
||||
botField: state.imBotId,
|
||||
targetField: state.imTargetId,
|
||||
onPick: (botId, targetId) => {
|
||||
props.edit("imBotId", botId);
|
||||
props.edit("imTargetId", targetId);
|
||||
},
|
||||
onEditBot: (text) => {
|
||||
props.edit("imBotId", text);
|
||||
},
|
||||
onReset: () => {
|
||||
props.resetField("imBotId");
|
||||
}
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(ValueField, {
|
||||
id: "netxops-im-target-id",
|
||||
label: t("imTargetId"),
|
||||
hint: t("imTargetIdHint"),
|
||||
field: state.imTargetId,
|
||||
overriddenLabel: t("overridden"),
|
||||
resetLabel: t("reset"),
|
||||
invalidLabel: t("invalid"),
|
||||
disabled,
|
||||
onEdit: (text) => {
|
||||
onEditTarget: (text) => {
|
||||
props.edit("imTargetId", text);
|
||||
},
|
||||
onReset: () => {
|
||||
onResetBot: () => {
|
||||
props.resetField("imBotId");
|
||||
},
|
||||
onResetTarget: () => {
|
||||
props.resetField("imTargetId");
|
||||
}
|
||||
}),
|
||||
|
|
@ -861,14 +1151,23 @@ class NetxopsCardController {
|
|||
};
|
||||
rpcCall;
|
||||
alarmPushStatus = null;
|
||||
imDeliveryCatalog = { ...EMPTY_IM_DELIVERY_CATALOG };
|
||||
pollTimer;
|
||||
pollInFlight = false;
|
||||
catalogInFlight = false;
|
||||
constructor(scope, ctx) {
|
||||
this.scope = scope;
|
||||
this.ctx = ctx;
|
||||
this.form = new CardForm(scope, [
|
||||
textField("apiUrl"),
|
||||
textField("lang"),
|
||||
textField("nmsProvider"),
|
||||
booleanFieldPersistFalse("groupNmsInPreset"),
|
||||
booleanField("groupNmsPublic"),
|
||||
booleanFieldPersistFalse("groupCommonInPreset"),
|
||||
booleanField("groupCommonPublic"),
|
||||
booleanField("groupTopologyInPreset"),
|
||||
booleanField("groupTopologyPublic"),
|
||||
booleanField("alarmPushEnabled"),
|
||||
booleanFieldPersistFalse("alarmDeliverDsh"),
|
||||
booleanField("alarmDeliverIm"),
|
||||
|
|
@ -897,20 +1196,29 @@ class NetxopsCardController {
|
|||
this.rpcCall = call;
|
||||
if (call === undefined) {
|
||||
this.stopStatusPoll();
|
||||
let changed = false;
|
||||
if (this.alarmPushStatus !== null) {
|
||||
this.alarmPushStatus = null;
|
||||
this.store.set(this.projection());
|
||||
changed = true;
|
||||
}
|
||||
if (this.imDeliveryCatalog.options.length > 0 || this.imDeliveryCatalog.available !== true) {
|
||||
this.imDeliveryCatalog = { ...EMPTY_IM_DELIVERY_CATALOG };
|
||||
changed = true;
|
||||
}
|
||||
if (changed)
|
||||
this.store.set(this.projection());
|
||||
return;
|
||||
}
|
||||
this.startStatusPoll();
|
||||
this.refreshAlarmPushStatus();
|
||||
this.refreshImDeliveryCatalog();
|
||||
}
|
||||
startStatusPoll() {
|
||||
if (this.pollTimer !== undefined)
|
||||
return;
|
||||
this.pollTimer = setInterval(() => {
|
||||
this.refreshAlarmPushStatus();
|
||||
this.refreshImDeliveryCatalog();
|
||||
}, STATUS_POLL_MS);
|
||||
}
|
||||
stopStatusPoll() {
|
||||
|
|
@ -935,11 +1243,38 @@ class NetxopsCardController {
|
|||
this.pollInFlight = false;
|
||||
}
|
||||
}
|
||||
async refreshImDeliveryCatalog() {
|
||||
const call = this.rpcCall;
|
||||
if (call === undefined || this.catalogInFlight)
|
||||
return;
|
||||
this.catalogInFlight = true;
|
||||
try {
|
||||
const next = await fetchImDeliveryCatalog(call);
|
||||
const prev = this.imDeliveryCatalog;
|
||||
const sameOptions = prev.options.length === next.options.length && prev.options.every((row, index) => {
|
||||
const other = next.options[index];
|
||||
return other && row.botId === other.botId && row.targetId === other.targetId && row.name === other.name && row.channel === other.channel;
|
||||
});
|
||||
if (prev.available === next.available && prev.hint === next.hint && sameOptions)
|
||||
return;
|
||||
this.imDeliveryCatalog = next;
|
||||
this.store.set(this.projection());
|
||||
} catch {} finally {
|
||||
this.catalogInFlight = false;
|
||||
}
|
||||
}
|
||||
projection() {
|
||||
return {
|
||||
...this.form.shell(),
|
||||
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"),
|
||||
groupTopologyInPreset: this.form.field("groupTopologyInPreset"),
|
||||
groupTopologyPublic: this.form.field("groupTopologyPublic"),
|
||||
alarmPushEnabled: this.form.field("alarmPushEnabled"),
|
||||
alarmDeliverDsh: this.form.field("alarmDeliverDsh"),
|
||||
alarmDeliverIm: this.form.field("alarmDeliverIm"),
|
||||
|
|
@ -949,7 +1284,8 @@ class NetxopsCardController {
|
|||
apiTokenConfigured: this.credential.configured,
|
||||
apiTokenWritable: this.credential.remoteReady && this.credential.writable,
|
||||
apiTokenRemoteReady: this.credential.remoteReady,
|
||||
alarmPushStatus: this.alarmPushStatus
|
||||
alarmPushStatus: this.alarmPushStatus,
|
||||
imDeliveryCatalog: this.imDeliveryCatalog
|
||||
};
|
||||
}
|
||||
credentials() {
|
||||
|
|
@ -1014,11 +1350,20 @@ function refOf(snapshot) {
|
|||
// src/client/locales.ts
|
||||
var en = {
|
||||
title: "Netx Ops",
|
||||
description: "UME API endpoint and bearer token for netx__* tools.",
|
||||
description: "netx API, bearer token, and capability groups (nms / common / 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.",
|
||||
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)",
|
||||
groupTopology: "topology — skill netx-topology (canvas / fabric / dual_unit)",
|
||||
groupInPreset: "In Netx Ops preset",
|
||||
groupPublic: "Publish to other presets",
|
||||
alarmPushEnabled: "Key-alarm push",
|
||||
alarmPushEnabledHint: "Dial out to netx when a matched key alarm arrives. Choose DSH and/or IM sinks below.",
|
||||
alarmPushStatus: "Push link",
|
||||
|
|
@ -1033,11 +1378,15 @@ var en = {
|
|||
alarmDeliverDsh: "Deliver to DSH session",
|
||||
alarmDeliverDshHint: "Open/follow the sticky「Netx 关键告警」session on this Host.",
|
||||
alarmDeliverIm: "Deliver to WhatsApp / IM",
|
||||
alarmDeliverImHint: "Requires dsh-im-ops. Paste botId + targetId from IM → 投递设置 → 复制调用参数.",
|
||||
alarmDeliverImHint: "Requires dsh-im-ops ≥ops.24. Pick a saved delivery target below (create targets under IM → Delivery settings).",
|
||||
imBotId: "IM Bot ID",
|
||||
imBotIdHint: "Opaque botId from IM delivery settings (not a phone number).",
|
||||
imBotIdHint: "Filled from the picker, or enter manually.",
|
||||
imTargetId: "IM Target ID",
|
||||
imTargetIdHint: "Opaque targetId alias (e.g. release-alerts). Create the target in IM first.",
|
||||
imTargetIdHint: "Filled from the picker, or enter manually.",
|
||||
imTarget: "Delivery target",
|
||||
imTargetNone: "(Create a target in IM delivery settings first)",
|
||||
imTargetManual: "Enter manually…",
|
||||
imCatalogUnavailable: "Cannot load targets — install/update dsh-im-ops (≥ops.24) and save at least one delivery target.",
|
||||
apiToken: "API token",
|
||||
apiTokenHint: "Stored as credential NETX_API_TOKEN (never written into settings). Leave blank to keep the current token.",
|
||||
apiTokenUnavailable: "This DSH build does not expose remote.credentials. Set the token with scripts/set-netx-token.ps1 (or .sh), then restart is not required if credentials are watched.",
|
||||
|
|
@ -1057,11 +1406,20 @@ var en = {
|
|||
};
|
||||
var zh = {
|
||||
title: "Netx Ops",
|
||||
description: "UME API 地址与 Bearer Token,供 netx__* 工具使用。",
|
||||
description: "netx API、Bearer Token,以及能力组(nms / common / 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。",
|
||||
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)",
|
||||
groupTopology: "topology — skill netx-topology(画布 / Fabric / dual_unit)",
|
||||
groupInPreset: "在 Netx Ops 预设中启用",
|
||||
groupPublic: "对其他预设公开",
|
||||
alarmPushEnabled: "关键告警推送",
|
||||
alarmPushEnabledHint: "主动连接 netx;匹配到关键告警后按下述开关投递。可只开 DSH、只开 IM,或两者都开。",
|
||||
alarmPushStatus: "推送链路",
|
||||
|
|
@ -1076,11 +1434,15 @@ var zh = {
|
|||
alarmDeliverDsh: "投递到 DSH 会话",
|
||||
alarmDeliverDshHint: "在本机打开/续写 sticky「Netx 关键告警」会话。",
|
||||
alarmDeliverIm: "投递到 WhatsApp / IM",
|
||||
alarmDeliverImHint: "需要已安装 dsh-im-ops。请先在 IM「投递设置」新建目标,再粘贴「复制调用参数」里的 botId / targetId。",
|
||||
alarmDeliverImHint: "需要 dsh-im-ops ≥ops.24。请在下方下拉选择已保存的投递目标(先在 IM「投递设置」新建)。",
|
||||
imBotId: "IM Bot ID",
|
||||
imBotIdHint: "来自 IM 投递设置的不透明 botId(不是手机号)。",
|
||||
imBotIdHint: "由下拉自动填入,也可手动改。",
|
||||
imTargetId: "IM Target ID",
|
||||
imTargetIdHint: "投递目标别名(如 release-alerts)。请先在 IM 侧创建目标。",
|
||||
imTargetIdHint: "由下拉自动填入,也可手动改。",
|
||||
imTarget: "投递目标",
|
||||
imTargetNone: "(请先在 IM「投递设置」新建目标)",
|
||||
imTargetManual: "手动填写…",
|
||||
imCatalogUnavailable: "无法加载投递目标 — 请安装/更新 dsh-im-ops(≥ops.24)并至少保存一个投递目标。",
|
||||
apiToken: "API Token",
|
||||
apiTokenHint: "写入凭据 NETX_API_TOKEN(不会进 settings)。留空表示保留已有 token。",
|
||||
apiTokenUnavailable: "当前 DSH 未提供 remote.credentials。请用 scripts/set-netx-token.ps1(或 .sh)写入 token;若 harness 在监视凭据文件则无需重启。",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue