mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-09 04:00:46 +08:00
Register KB pack skills from operator-subset _skills (P2).
Load hasSkills packs onto preset/public planes with groupKb toggles; unify content has* keys and keep netx-ops pure. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
fcdd11ac59
commit
2caa3c98d7
26 changed files with 1122 additions and 106 deletions
|
|
@ -237,7 +237,11 @@ function skillBody(snapshot) {
|
|||
`| kbVersion | ${snapshot.version} |`,
|
||||
`| kbContent (on) | ${flags} |`,
|
||||
"",
|
||||
"Environment mirrors: `KB_ROOT`, `KB_OPERATOR`, `KB_COUNTRY`, `KB_VERSION`, `KB_CONTENT`, `KB_STATUS`."
|
||||
"Environment mirrors: KB_ROOT, KB_OPERATOR, KB_COUNTRY, KB_VERSION, KB_CONTENT, KB_STATUS.",
|
||||
"",
|
||||
"Business playbooks (kb-troubleshoot, kb-retrieve, …) register from",
|
||||
`${snapshot.realRoot}/_skills/ when hasSkills is true — use those skills for KB triage;`,
|
||||
"keep netx-ops for live netx evidence only."
|
||||
].join(`
|
||||
`)
|
||||
};
|
||||
|
|
@ -248,7 +252,7 @@ function skillBody(snapshot) {
|
|||
content: [
|
||||
"## Knowledge base (unavailable)",
|
||||
"",
|
||||
`kbStatus=\`${snapshot.status}\`. ${reason}`,
|
||||
`kbStatus=${snapshot.status}. ${reason}`,
|
||||
"",
|
||||
"**Do not invent an operator, country, or KB paths.**",
|
||||
"Use only netx tools / live evidence (alarms, inventory, CLI, topology).",
|
||||
|
|
@ -272,12 +276,77 @@ function registerKbContextSkill(ctx, snapshot) {
|
|||
});
|
||||
}
|
||||
|
||||
// src/netx/kb-pack-skills.ts
|
||||
import { readdir as readdir2, stat as stat2 } from "node:fs/promises";
|
||||
import { join as join2 } from "node:path";
|
||||
function kbPackSkillsEnabled(snapshot) {
|
||||
return snapshot.status === "configured" && snapshot.content.hasSkills === true;
|
||||
}
|
||||
async function registerKbPackSkills(ctx, snapshot, options) {
|
||||
if (!options.enabled || !kbPackSkillsEnabled(snapshot)) {
|
||||
return () => {};
|
||||
}
|
||||
const skillsApi = ctx.skills;
|
||||
if (!skillsApi || typeof skillsApi.register !== "function") {
|
||||
return () => {};
|
||||
}
|
||||
const root = join2(snapshot.realRoot, "_skills");
|
||||
let entries;
|
||||
try {
|
||||
entries = await readdir2(root);
|
||||
} catch {
|
||||
ctx.logger.info("netxops: kb pack skills dir missing at %s — skip", root);
|
||||
return () => {};
|
||||
}
|
||||
const disposers = [];
|
||||
const provider = options.providerLabel ?? "netxops-kb-pack";
|
||||
for (const entry of entries) {
|
||||
const full = join2(root, entry);
|
||||
let isDir = false;
|
||||
try {
|
||||
isDir = (await stat2(full)).isDirectory();
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
if (!isDir)
|
||||
continue;
|
||||
let skill;
|
||||
try {
|
||||
skill = await loadSkillBundle(full);
|
||||
} catch (error) {
|
||||
ctx.logger.warn("netxops: kb pack skill load failed at %s: %s", full, error instanceof Error ? error.message : String(error));
|
||||
continue;
|
||||
}
|
||||
if (!skill) {
|
||||
ctx.logger.warn("netxops: kb pack skill skipped (invalid SKILL.md): %s", full);
|
||||
continue;
|
||||
}
|
||||
disposers.push(skillsApi.register({
|
||||
name: skill.name,
|
||||
description: skill.description,
|
||||
content: skill.content,
|
||||
path: skill.path,
|
||||
resourceBase: { kind: "directory", path: skill.directory },
|
||||
provider,
|
||||
source: "custom"
|
||||
}));
|
||||
}
|
||||
if (disposers.length > 0) {
|
||||
ctx.logger.info("netxops: kb pack skills registered count=%s root=%s provider=%s", disposers.length, root, provider);
|
||||
}
|
||||
return () => {
|
||||
for (const dispose of disposers)
|
||||
dispose();
|
||||
};
|
||||
}
|
||||
|
||||
// src/netx/kb-manifest.ts
|
||||
var EMPTY_CONTENT = {
|
||||
regions: false,
|
||||
theory: false,
|
||||
packet: false,
|
||||
skills: false
|
||||
hasRegions: false,
|
||||
hasTheory: false,
|
||||
hasPacket: false,
|
||||
hasCommon: false,
|
||||
hasSkills: false
|
||||
};
|
||||
function unconfiguredKbSnapshot() {
|
||||
return {
|
||||
|
|
@ -1848,6 +1917,8 @@ function applyGroupToolsPlugin(ctx, options) {
|
|||
});
|
||||
ctx.inject(["skills"], (skillsCtx) => {
|
||||
let unregisterKbSkill;
|
||||
let unregisterKbPack;
|
||||
let packGeneration = 0;
|
||||
const remountSkills = () => {
|
||||
const gen = ++skillGeneration;
|
||||
unregisterSkills?.();
|
||||
|
|
@ -1870,22 +1941,47 @@ function applyGroupToolsPlugin(ctx, options) {
|
|||
unregisterKbSkill = undefined;
|
||||
unregisterKbSkill = registerKbContextSkill(skillsCtx, getKbContext());
|
||||
};
|
||||
const remountKbPack = () => {
|
||||
const gen = ++packGeneration;
|
||||
unregisterKbPack?.();
|
||||
unregisterKbPack = undefined;
|
||||
const connection = getNetxConnection();
|
||||
const enabled = connection?.groupKbInPreset !== false;
|
||||
registerKbPackSkills(skillsCtx, getKbContext(), {
|
||||
enabled,
|
||||
providerLabel: `${options.name}-kb-pack`
|
||||
}).then((dispose) => {
|
||||
if (gen !== packGeneration) {
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
unregisterKbPack = dispose;
|
||||
}).catch((error) => {
|
||||
skillsCtx.logger.warn("%s: kb pack skill register failed: %s", options.name, error);
|
||||
});
|
||||
};
|
||||
remountSkills();
|
||||
remountKbSkill();
|
||||
remountKbPack();
|
||||
const stopSkillWatch = watchNetxConnection(() => {
|
||||
remountSkills();
|
||||
remountKbPack();
|
||||
});
|
||||
const stopKbWatch = watchKbContext(() => {
|
||||
remountKbSkill();
|
||||
remountKbPack();
|
||||
});
|
||||
skillsCtx.effect(() => () => {
|
||||
skillGeneration += 1;
|
||||
packGeneration += 1;
|
||||
stopSkillWatch();
|
||||
stopKbWatch();
|
||||
unregisterSkills?.();
|
||||
unregisterSkills = undefined;
|
||||
unregisterKbSkill?.();
|
||||
unregisterKbSkill = undefined;
|
||||
unregisterKbPack?.();
|
||||
unregisterKbPack = undefined;
|
||||
}, `${options.name}: dispose skills`);
|
||||
});
|
||||
ctx.effect(() => () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue