mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-09 09:23:20 +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(() => () => {
|
||||
|
|
|
|||
|
|
@ -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(() => () => {
|
||||
|
|
|
|||
|
|
@ -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(() => () => {
|
||||
|
|
|
|||
|
|
@ -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(() => () => {
|
||||
|
|
|
|||
|
|
@ -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(() => () => {
|
||||
|
|
|
|||
|
|
@ -569,10 +569,11 @@ var posix = ((p) => (p.posix = p, p))({ resolve, normalize, isAbsolute, join, re
|
|||
|
||||
// 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 {
|
||||
|
|
@ -1410,6 +1411,20 @@ function NetxopsCard(props) {
|
|||
children: state.kbStatus.realRoot
|
||||
}) : null
|
||||
]
|
||||
}),
|
||||
/* @__PURE__ */ jsx_runtime.jsx(CapabilityGroupBlock, {
|
||||
title: t("groupKb"),
|
||||
inPresetLabel: t("groupInPreset"),
|
||||
publicLabel: t("groupPublic"),
|
||||
inPreset: state.groupKbInPreset,
|
||||
published: state.groupKbPublic,
|
||||
disabled,
|
||||
onEditInPreset: (checked) => {
|
||||
props.edit("groupKbInPreset", checked ? "true" : "false");
|
||||
},
|
||||
onEditPublic: (checked) => {
|
||||
props.edit("groupKbPublic", checked ? "true" : "false");
|
||||
}
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
|
@ -1841,6 +1856,8 @@ class NetxopsCardController {
|
|||
booleanField("groupOpsPublic"),
|
||||
booleanField("groupTopologyInPreset"),
|
||||
booleanField("groupTopologyPublic"),
|
||||
booleanFieldPersistFalse("groupKbInPreset"),
|
||||
booleanField("groupKbPublic"),
|
||||
booleanField("alarmPushEnabled"),
|
||||
booleanFieldPersistFalse("alarmDeliverDsh"),
|
||||
booleanField("alarmDeliverIm"),
|
||||
|
|
@ -2106,6 +2123,8 @@ class NetxopsCardController {
|
|||
groupOpsPublic: this.form.field("groupOpsPublic"),
|
||||
groupTopologyInPreset: this.form.field("groupTopologyInPreset"),
|
||||
groupTopologyPublic: this.form.field("groupTopologyPublic"),
|
||||
groupKbInPreset: this.form.field("groupKbInPreset"),
|
||||
groupKbPublic: this.form.field("groupKbPublic"),
|
||||
alarmPushEnabled: this.form.field("alarmPushEnabled"),
|
||||
alarmDeliverDsh: this.form.field("alarmDeliverDsh"),
|
||||
alarmDeliverIm: this.form.field("alarmDeliverIm"),
|
||||
|
|
@ -2259,10 +2278,11 @@ var en = {
|
|||
nmsProviderHint: "zte-ume",
|
||||
groupOps: "ops",
|
||||
groupTopology: "topology",
|
||||
groupKb: "Knowledge skills",
|
||||
groupInPreset: "In Netx Ops preset",
|
||||
groupPublic: "Publish to other presets",
|
||||
kbRoot: "Package root",
|
||||
kbRootHint: "Folder with MANIFEST.json (operator-subset v1.0). Browse auto-saves.",
|
||||
kbRootHint: "Folder with MANIFEST.json (operator-subset v1.0). Browse auto-saves. Pack skills load from _skills/ when hasSkills.",
|
||||
kbBrowse: "Browse…",
|
||||
kbBrowseUnavailable: "Directory picker unavailable — paste an absolute folder path, then Save.",
|
||||
kbBrowseFailed: "Browse failed: {detail}",
|
||||
|
|
@ -2331,10 +2351,11 @@ var zh = {
|
|||
nmsProviderHint: "zte-ume",
|
||||
groupOps: "ops",
|
||||
groupTopology: "topology",
|
||||
groupKb: "知识库技能",
|
||||
groupInPreset: "在 Netx Ops 预设中启用",
|
||||
groupPublic: "对其他预设公开",
|
||||
kbRoot: "知识包根目录",
|
||||
kbRootHint: "含 MANIFEST.json 的运营商子集包(v1.0)。浏览选目录后会自动保存。",
|
||||
kbRootHint: "含 MANIFEST.json 的运营商子集包(v1.0)。浏览选目录后会自动保存;hasSkills 时加载 _skills/。",
|
||||
kbBrowse: "浏览…",
|
||||
kbBrowseUnavailable: "目录选择器不可用 — 请粘贴绝对目录路径后点保存。",
|
||||
kbBrowseFailed: "浏览失败:{detail}",
|
||||
|
|
|
|||
151
lib/index.js
151
lib/index.js
|
|
@ -4,7 +4,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|||
// src/index.ts
|
||||
import { cpSync, existsSync as existsSync3, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { homedir as homedir3 } from "node:os";
|
||||
import { dirname as dirname3, join as join4 } from "node:path";
|
||||
import { dirname as dirname3, join as join5 } from "node:path";
|
||||
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
||||
import z from "@deepseek-ai/schemastery";
|
||||
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
||||
|
|
@ -807,7 +807,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(`
|
||||
`)
|
||||
};
|
||||
|
|
@ -818,7 +822,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).",
|
||||
|
|
@ -846,10 +850,18 @@ function registerKbContextSkill(ctx, snapshot) {
|
|||
import { existsSync as existsSync2, readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { basename, dirname as dirname2, join as join3, resolve } from "node:path";
|
||||
var EMPTY_CONTENT = {
|
||||
regions: false,
|
||||
theory: false,
|
||||
packet: false,
|
||||
skills: false
|
||||
hasRegions: false,
|
||||
hasTheory: false,
|
||||
hasPacket: false,
|
||||
hasCommon: false,
|
||||
hasSkills: false
|
||||
};
|
||||
var LEGACY_CONTENT_KEY = {
|
||||
regions: "hasRegions",
|
||||
theory: "hasTheory",
|
||||
packet: "hasPacket",
|
||||
skills: "hasSkills",
|
||||
common: "hasCommon"
|
||||
};
|
||||
function unconfiguredKbSnapshot() {
|
||||
return {
|
||||
|
|
@ -962,8 +974,17 @@ function parseContent(raw) {
|
|||
if (typeof raw !== "object" || Array.isArray(raw)) {
|
||||
throw new Error("MANIFEST content must be an object");
|
||||
}
|
||||
for (const [key, value] of Object.entries(raw)) {
|
||||
out[key] = value === true;
|
||||
const row = raw;
|
||||
const present = new Set(Object.keys(row));
|
||||
for (const [key, value] of Object.entries(row)) {
|
||||
const on = value === true;
|
||||
const mapped = LEGACY_CONTENT_KEY[key];
|
||||
if (mapped !== undefined) {
|
||||
if (on && !present.has(mapped))
|
||||
out[mapped] = true;
|
||||
continue;
|
||||
}
|
||||
out[key] = on;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -1047,6 +1068,70 @@ function resolveKbRoot(kbRoot, maxDepth = 3) {
|
|||
}
|
||||
}
|
||||
|
||||
// src/netx/kb-pack-skills.ts
|
||||
import { readdir as readdir2, stat as stat2 } from "node:fs/promises";
|
||||
import { join as join4 } 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 = join4(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 = join4(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-runtime.ts
|
||||
var STORE_KEY2 = Symbol.for("dsh-netxops.kb-store");
|
||||
var ENV_KEYS = [
|
||||
|
|
@ -3709,33 +3794,35 @@ var Config = z.object({
|
|||
groupOpsPublic: z.boolean().default(false),
|
||||
groupTopologyInPreset: z.boolean().default(false),
|
||||
groupTopologyPublic: z.boolean().default(false),
|
||||
kbRoot: z.string().default("")
|
||||
kbRoot: z.string().default(""),
|
||||
groupKbInPreset: z.boolean().default(true),
|
||||
groupKbPublic: z.boolean().default(false)
|
||||
});
|
||||
function packageRoot() {
|
||||
return join4(dirname3(fileURLToPath2(import.meta.url)), "..");
|
||||
return join5(dirname3(fileURLToPath2(import.meta.url)), "..");
|
||||
}
|
||||
function resolveDshHome() {
|
||||
const fromEnv = process.env.DSH_HOME?.trim();
|
||||
if (fromEnv !== undefined && fromEnv.length > 0)
|
||||
return fromEnv;
|
||||
return join4(homedir3(), ".dsh");
|
||||
return join5(homedir3(), ".dsh");
|
||||
}
|
||||
function ensureAgentPresetInstalled(logger) {
|
||||
const src = join4(packageRoot(), "presets", NETXOPS_PRESET_ID);
|
||||
const composition = join4(src, "agent.cordis.yml");
|
||||
const src = join5(packageRoot(), "presets", NETXOPS_PRESET_ID);
|
||||
const composition = join5(src, "agent.cordis.yml");
|
||||
if (!existsSync3(composition)) {
|
||||
logger.warn("netxops: bundled preset missing at %s — skip user-preset install", src);
|
||||
return;
|
||||
}
|
||||
const destParent = join4(resolveDshHome(), ".agent-presets");
|
||||
const dest = join4(destParent, NETXOPS_PRESET_ID);
|
||||
const destParent = join5(resolveDshHome(), ".agent-presets");
|
||||
const dest = join5(destParent, NETXOPS_PRESET_ID);
|
||||
try {
|
||||
mkdirSync(destParent, { recursive: true });
|
||||
if (existsSync3(dest)) {
|
||||
rmSync(dest, { recursive: true, force: true });
|
||||
}
|
||||
cpSync(src, dest, { recursive: true });
|
||||
writeFileSync(join4(dest, ".dsh-netxops-managed"), `${new Date().toISOString()}
|
||||
writeFileSync(join5(dest, ".dsh-netxops-managed"), `${new Date().toISOString()}
|
||||
`, "utf8");
|
||||
logger.info("netxops: agent preset installed at %s", dest);
|
||||
} catch (error) {
|
||||
|
|
@ -3825,7 +3912,9 @@ function apply(ctx, config = Config({})) {
|
|||
token,
|
||||
lang: current.lang,
|
||||
toolCallTimeoutMs: current.toolCallTimeoutMs,
|
||||
groups
|
||||
groups,
|
||||
groupKbInPreset: current.groupKbInPreset !== false,
|
||||
groupKbPublic: current.groupKbPublic === true
|
||||
});
|
||||
const kb = resolveKbRoot(current.kbRoot ?? "");
|
||||
publishKbContext(kb);
|
||||
|
|
@ -3887,7 +3976,9 @@ function apply(ctx, config = Config({})) {
|
|||
ctx.inject(["skills"], (skillsCtx) => {
|
||||
let unregisterSkills;
|
||||
let unregisterKbSkill;
|
||||
let unregisterKbPack;
|
||||
let generation2 = 0;
|
||||
let packGeneration = 0;
|
||||
const remountPublicSkills = () => {
|
||||
const gen = ++generation2;
|
||||
unregisterSkills?.();
|
||||
|
|
@ -3914,20 +4005,44 @@ function apply(ctx, config = Config({})) {
|
|||
unregisterKbSkill = registerKbContextSkill(skillsCtx, snapshot);
|
||||
skillsCtx.logger.info("netxops: kb-context skill status=%s", snapshot.status);
|
||||
};
|
||||
const remountKbPack = () => {
|
||||
const gen = ++packGeneration;
|
||||
unregisterKbPack?.();
|
||||
unregisterKbPack = undefined;
|
||||
const connection = getNetxConnection();
|
||||
const enabled = connection?.groupKbPublic === true;
|
||||
registerKbPackSkills(skillsCtx, getKbContext(), {
|
||||
enabled,
|
||||
providerLabel: "netxops-kb-pack-public"
|
||||
}).then((dispose) => {
|
||||
if (gen !== packGeneration) {
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
unregisterKbPack = dispose;
|
||||
}).catch((error) => {
|
||||
skillsCtx.logger.warn("netxops: public kb pack skill register failed: %s", error);
|
||||
});
|
||||
};
|
||||
remountPublicSkills();
|
||||
remountKbSkill();
|
||||
remountKbPack();
|
||||
const stopWatch = watchNetxConnection(() => {
|
||||
remountPublicSkills();
|
||||
remountKbPack();
|
||||
});
|
||||
const stopKbWatch = watchKbContext(() => {
|
||||
remountKbSkill();
|
||||
remountKbPack();
|
||||
});
|
||||
skillsCtx.effect(() => () => {
|
||||
generation2 += 1;
|
||||
packGeneration += 1;
|
||||
stopWatch();
|
||||
stopKbWatch();
|
||||
unregisterSkills?.();
|
||||
unregisterKbSkill?.();
|
||||
unregisterKbPack?.();
|
||||
}, "netxops: dispose public skills");
|
||||
});
|
||||
ctx.inject(["connection"], (connCtx) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue