mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-19 21:44:06 +08:00
feat(card): show cached netx skills per capability group
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fa82f530e9
commit
056f2d4ea3
9 changed files with 285 additions and 96 deletions
141
lib/index.js
141
lib/index.js
|
|
@ -2,7 +2,7 @@ import { createRequire } from "node:module";
|
|||
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
||||
|
||||
// src/index.ts
|
||||
import { readFileSync as readFileSync9 } from "node:fs";
|
||||
import { readFileSync as readFileSync10 } from "node:fs";
|
||||
import { dirname as dirname8, join as join14 } from "node:path";
|
||||
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
||||
import z from "@deepseek-ai/schemastery";
|
||||
|
|
@ -1391,7 +1391,9 @@ import {
|
|||
existsSync as existsSync3,
|
||||
mkdirSync as mkdirSync2,
|
||||
readdirSync,
|
||||
readFileSync as readFileSync3,
|
||||
rmSync,
|
||||
statSync as statSync2,
|
||||
writeFileSync as writeFileSync3
|
||||
} from "node:fs";
|
||||
import { homedir as homedir3 } from "node:os";
|
||||
|
|
@ -1449,6 +1451,40 @@ function replaceSkillsCache(apiUrl, files) {
|
|||
`, "utf8");
|
||||
return root;
|
||||
}
|
||||
function countFiles(dir) {
|
||||
let n = 0;
|
||||
for (const entry of readdirSync(dir)) {
|
||||
const full = join3(dir, entry);
|
||||
n += statSync2(full).isDirectory() ? countFiles(full) : 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
function listCachedSkills(apiUrl) {
|
||||
const root = skillsCacheDir(apiUrl);
|
||||
const skills = [];
|
||||
let listedAt = null;
|
||||
if (!apiUrl.trim() || !existsSync3(root))
|
||||
return { listedAt, skills };
|
||||
try {
|
||||
const meta = JSON.parse(readFileSync3(join3(root, ".meta.json"), "utf8"));
|
||||
if (typeof meta.listedAt === "string")
|
||||
listedAt = meta.listedAt;
|
||||
} catch {}
|
||||
for (const group of CAPABILITY_GROUP_IDS) {
|
||||
const groupDir = join3(root, SKILL_DIR_BY_GROUP[group]);
|
||||
if (!existsSync3(groupDir))
|
||||
continue;
|
||||
try {
|
||||
for (const name of readdirSync(groupDir).sort()) {
|
||||
const dir = join3(groupDir, name);
|
||||
if (!statSync2(dir).isDirectory() || !existsSync3(join3(dir, "SKILL.md")))
|
||||
continue;
|
||||
skills.push({ group, name, files: countFiles(dir) });
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
return { listedAt, skills };
|
||||
}
|
||||
|
||||
// src/netx/group-skills.ts
|
||||
function parseFrontmatter(raw) {
|
||||
|
|
@ -1659,11 +1695,11 @@ Use only tools present in this session. Dual-unit/orbit/polish recipes require s
|
|||
}
|
||||
|
||||
// src/netx/kb-context-skill.ts
|
||||
import { readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync4 } from "node:fs";
|
||||
import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync5 } from "node:fs";
|
||||
import { join as join7 } from "node:path";
|
||||
|
||||
// src/netx/kb-manifest.ts
|
||||
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
||||
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
|
||||
import { basename, dirname as dirname3, join as join5, resolve } from "node:path";
|
||||
var EMPTY_CONTENT = {
|
||||
hasRegions: false,
|
||||
|
|
@ -1710,7 +1746,7 @@ function findManifest(kbRoot, maxDepth = 3) {
|
|||
}
|
||||
let rootStat;
|
||||
try {
|
||||
rootStat = statSync2(root);
|
||||
rootStat = statSync3(root);
|
||||
} catch {
|
||||
return { paths: [], error: `kbRoot not found: ${root}` };
|
||||
}
|
||||
|
|
@ -1720,7 +1756,7 @@ function findManifest(kbRoot, maxDepth = 3) {
|
|||
const direct = join5(root, "MANIFEST.json");
|
||||
if (existsSync4(direct)) {
|
||||
try {
|
||||
if (statSync2(direct).isFile())
|
||||
if (statSync3(direct).isFile())
|
||||
return { paths: [direct] };
|
||||
} catch {}
|
||||
}
|
||||
|
|
@ -1731,7 +1767,7 @@ function findManifest(kbRoot, maxDepth = 3) {
|
|||
const candidate = join5(dir, "MANIFEST.json");
|
||||
if (existsSync4(candidate)) {
|
||||
try {
|
||||
if (statSync2(candidate).isFile())
|
||||
if (statSync3(candidate).isFile())
|
||||
found.push(candidate);
|
||||
} catch {}
|
||||
}
|
||||
|
|
@ -1749,7 +1785,7 @@ function findManifest(kbRoot, maxDepth = 3) {
|
|||
const full = join5(dir, name);
|
||||
let st;
|
||||
try {
|
||||
st = statSync2(full);
|
||||
st = statSync3(full);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1769,7 +1805,7 @@ function findManifest(kbRoot, maxDepth = 3) {
|
|||
const full = join5(root, name);
|
||||
let st;
|
||||
try {
|
||||
st = statSync2(full);
|
||||
st = statSync3(full);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1859,7 +1895,7 @@ function resolveKbRoot(kbRoot, maxDepth = 3) {
|
|||
return unconfiguredKbSnapshot();
|
||||
let root = resolve(trimmed);
|
||||
try {
|
||||
const st = statSync2(root);
|
||||
const st = statSync3(root);
|
||||
if (st.isFile()) {
|
||||
if (basename(root).toLowerCase() === "manifest.json") {
|
||||
root = dirname3(root);
|
||||
|
|
@ -1884,7 +1920,7 @@ function resolveKbRoot(kbRoot, maxDepth = 3) {
|
|||
const manifestPath = located.paths[0];
|
||||
let raw;
|
||||
try {
|
||||
raw = readFileSync3(manifestPath, "utf8");
|
||||
raw = readFileSync4(manifestPath, "utf8");
|
||||
} catch (error) {
|
||||
return errorSnapshot(`cannot read MANIFEST: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
|
|
@ -1926,7 +1962,7 @@ function renderKbPathTable(snapshot) {
|
|||
}
|
||||
|
||||
// src/netx/kb-local-path.ts
|
||||
import { existsSync as existsSync5, realpathSync, statSync as statSync3 } from "node:fs";
|
||||
import { existsSync as existsSync5, realpathSync, statSync as statSync4 } from "node:fs";
|
||||
import { join as join6, relative, resolve as resolve2, sep } from "node:path";
|
||||
var KB_LOCAL_WRITABLE_ROOTS = Object.freeze([
|
||||
"memories",
|
||||
|
|
@ -2046,7 +2082,7 @@ function resolveExistingWritableFile(localRoot, pathArg) {
|
|||
if (!KB_LOCAL_WRITABLE_ROOTS.includes(top)) {
|
||||
throw new Error(`path not under writable roots: ${realRel}`);
|
||||
}
|
||||
if (!statSync3(realFile).isFile()) {
|
||||
if (!statSync4(realFile).isFile()) {
|
||||
throw new Error(`not a file: ${realFile}`);
|
||||
}
|
||||
return {
|
||||
|
|
@ -2111,9 +2147,9 @@ function readKbIdentity(snapshot) {
|
|||
const full = join7(dir, name);
|
||||
let text;
|
||||
try {
|
||||
if (!statSync4(full).isFile())
|
||||
if (!statSync5(full).isFile())
|
||||
continue;
|
||||
text = readFileSync4(full, "utf8").replace(/^\uFEFF/, "").trim();
|
||||
text = readFileSync5(full, "utf8").replace(/^\uFEFF/, "").trim();
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2239,9 +2275,9 @@ import {
|
|||
existsSync as existsSync6,
|
||||
mkdirSync as mkdirSync3,
|
||||
readdirSync as readdirSync4,
|
||||
readFileSync as readFileSync5,
|
||||
readFileSync as readFileSync6,
|
||||
rmSync as rmSync2,
|
||||
statSync as statSync5,
|
||||
statSync as statSync6,
|
||||
writeFileSync as writeFileSync4
|
||||
} from "node:fs";
|
||||
import { dirname as dirname4, join as join8 } from "node:path";
|
||||
|
|
@ -2404,7 +2440,7 @@ function walkFiles(dir, baseLocal, out) {
|
|||
const full = join8(dir, name);
|
||||
let st;
|
||||
try {
|
||||
st = statSync5(full);
|
||||
st = statSync6(full);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2438,7 +2474,7 @@ function listLocalFiles(snapshot, args = {}) {
|
|||
}
|
||||
|
||||
// src/netx/kb-read-ops.ts
|
||||
import { readdirSync as readdirSync5, readFileSync as readFileSync6, realpathSync as realpathSync2, statSync as statSync6 } from "node:fs";
|
||||
import { readdirSync as readdirSync5, readFileSync as readFileSync7, realpathSync as realpathSync2, statSync as statSync7 } from "node:fs";
|
||||
import { extname, join as join9, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
||||
var TEXT_EXTENSIONS = new Set([".md", ".markdown", ".txt", ".json", ".yml", ".yaml", ".csv", ".ps1", ".sh", ".cfg", ".conf", ".log"]);
|
||||
var SKIP_DIRS = new Set([".git", "node_modules"]);
|
||||
|
|
@ -2483,14 +2519,14 @@ function resolveKbReadPath(snapshot, pathArg) {
|
|||
}
|
||||
function readKbFile(snapshot, args) {
|
||||
const target = resolveKbReadPath(snapshot, args.path);
|
||||
const st = statSync6(target.absolutePath);
|
||||
const st = statSync7(target.absolutePath);
|
||||
if (st.isDirectory()) {
|
||||
const entries = readdirSync5(target.absolutePath, { withFileTypes: true }).filter((entry) => !SKIP_DIRS.has(entry.name)).map((entry) => {
|
||||
if (entry.isDirectory())
|
||||
return { name: entry.name, type: "dir" };
|
||||
let size;
|
||||
try {
|
||||
size = statSync6(join9(target.absolutePath, entry.name)).size;
|
||||
size = statSync7(join9(target.absolutePath, entry.name)).size;
|
||||
} catch {}
|
||||
return { name: entry.name, type: "file", size };
|
||||
}).sort((a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1);
|
||||
|
|
@ -2499,7 +2535,7 @@ function readKbFile(snapshot, args) {
|
|||
if (!TEXT_EXTENSIONS.has(extname(target.absolutePath).toLowerCase())) {
|
||||
throw new Error(`not a text KB file: ${target.relativePath}`);
|
||||
}
|
||||
const lines = readFileSync6(target.absolutePath, "utf8").replace(/^\uFEFF/, "").replace(/\r\n?/g, `
|
||||
const lines = readFileSync7(target.absolutePath, "utf8").replace(/^\uFEFF/, "").replace(/\r\n?/g, `
|
||||
`).split(`
|
||||
`);
|
||||
const start = Math.max(1, Math.floor(args.offset ?? 1));
|
||||
|
|
@ -2544,7 +2580,7 @@ function cachedText(path, mtimeMs, size) {
|
|||
const hit = cache.files.get(path);
|
||||
if (hit && hit.mtimeMs === mtimeMs && hit.size === size)
|
||||
return hit;
|
||||
const text = readFileSync6(path, "utf8").replace(/^\uFEFF/, "");
|
||||
const text = readFileSync7(path, "utf8").replace(/^\uFEFF/, "");
|
||||
if (hit)
|
||||
cache.chars -= hit.text.length;
|
||||
if (cache.chars + text.length > CACHE_MAX_CHARS) {
|
||||
|
|
@ -2574,7 +2610,7 @@ function walkTextFiles(dir, out) {
|
|||
if (!entry.isFile() || !TEXT_EXTENSIONS.has(extname(entry.name).toLowerCase()))
|
||||
continue;
|
||||
try {
|
||||
const st = statSync6(full);
|
||||
const st = statSync7(full);
|
||||
if (st.size <= SEARCH_MAX_FILE_BYTES)
|
||||
out.push({ path: full, mtimeMs: st.mtimeMs, size: st.size });
|
||||
} catch {}
|
||||
|
|
@ -2604,7 +2640,7 @@ function searchKb(snapshot, args) {
|
|||
const files = [];
|
||||
const seen = new Set;
|
||||
for (const dir of dirs) {
|
||||
const st = statSync6(dir);
|
||||
const st = statSync7(dir);
|
||||
const batch = [];
|
||||
if (st.isFile())
|
||||
batch.push({ path: dir, mtimeMs: st.mtimeMs, size: st.size });
|
||||
|
|
@ -3273,7 +3309,7 @@ function replyReminder(setting) {
|
|||
// src/netx/session-export.ts
|
||||
import { homedir as homedir5, hostname as osHostname } from "node:os";
|
||||
|
||||
// ../../netxops/node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/index.mjs
|
||||
// node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/index.mjs
|
||||
import { createRequire as createRequire3 } from "module";
|
||||
var require2 = createRequire3("/");
|
||||
var _a;
|
||||
|
|
@ -4540,7 +4576,7 @@ function mountNetxopsWebRoute(ctx, channel, handler) {
|
|||
}
|
||||
|
||||
// src/netx/tools.ts
|
||||
import { readFileSync as readFileSync7 } from "node:fs";
|
||||
import { readFileSync as readFileSync8 } from "node:fs";
|
||||
import { dirname as dirname6, join as join12 } from "node:path";
|
||||
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
||||
import { defineTool as defineTool2 } from "@deepseek-ai/dsh-tools";
|
||||
|
|
@ -5085,7 +5121,7 @@ async function refreshMcpCatalog(input) {
|
|||
function packageVersion() {
|
||||
try {
|
||||
const here = dirname6(fileURLToPath2(import.meta.url));
|
||||
const pkg = JSON.parse(readFileSync7(join12(here, "..", "package.json"), "utf8"));
|
||||
const pkg = JSON.parse(readFileSync8(join12(here, "..", "package.json"), "utf8"));
|
||||
return typeof pkg.version === "string" ? pkg.version : "0";
|
||||
} catch {
|
||||
return "0";
|
||||
|
|
@ -5217,13 +5253,13 @@ function registerNetxTools(ctx, connection, options) {
|
|||
import { createScope, scopeOf } from "@deepseek-ai/dsh-scope";
|
||||
|
||||
// src/netx/group-tools-plugin.ts
|
||||
import { readFileSync as readFileSync8 } from "node:fs";
|
||||
import { readFileSync as readFileSync9 } from "node:fs";
|
||||
import { dirname as dirname7, join as join13 } from "node:path";
|
||||
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
||||
function packageVersion2() {
|
||||
try {
|
||||
const here = dirname7(fileURLToPath3(import.meta.url));
|
||||
const pkg = JSON.parse(readFileSync8(join13(here, "..", "package.json"), "utf8"));
|
||||
const pkg = JSON.parse(readFileSync9(join13(here, "..", "package.json"), "utf8"));
|
||||
return typeof pkg.version === "string" ? pkg.version : "0";
|
||||
} catch {
|
||||
return "0";
|
||||
|
|
@ -5476,6 +5512,24 @@ function installPresetPublication(ctx) {
|
|||
}
|
||||
|
||||
// src/index.ts
|
||||
function mcpCatalogPayload(catalog) {
|
||||
const cached = listCachedSkills(getNetxConnection()?.apiUrl ?? "");
|
||||
return {
|
||||
phase: catalog.phase,
|
||||
mcpUrl: catalog.mcpUrl,
|
||||
error: catalog.error,
|
||||
listedAt: catalog.listedAt,
|
||||
fromCache: catalog.fromCache,
|
||||
tools: catalog.tools.map((tool2) => ({
|
||||
name: tool2.name,
|
||||
description: tool2.description,
|
||||
readOnlyHint: tool2.readOnlyHint,
|
||||
group: tool2.group ?? null
|
||||
})),
|
||||
skills: cached.skills,
|
||||
skillsListedAt: cached.listedAt
|
||||
};
|
||||
}
|
||||
var name = "netxops";
|
||||
var inject = ["tools"];
|
||||
var NETXOPS_RPC_CHANNEL = "/netxops";
|
||||
|
|
@ -5550,7 +5604,7 @@ function resolveApiUrlForConnection(value) {
|
|||
function packageVersion3() {
|
||||
try {
|
||||
const here = dirname8(fileURLToPath4(import.meta.url));
|
||||
const pkg = JSON.parse(readFileSync9(join14(here, "..", "package.json"), "utf8"));
|
||||
const pkg = JSON.parse(readFileSync10(join14(here, "..", "package.json"), "utf8"));
|
||||
return typeof pkg.version === "string" ? pkg.version : "0";
|
||||
} catch {
|
||||
return "0";
|
||||
|
|
@ -6147,23 +6201,7 @@ function apply(ctx, config = Config({})) {
|
|||
};
|
||||
}
|
||||
if (endpoint === "tools.catalog") {
|
||||
const catalog = getMcpCatalogState();
|
||||
return {
|
||||
ok: true,
|
||||
value: {
|
||||
phase: catalog.phase,
|
||||
mcpUrl: catalog.mcpUrl,
|
||||
error: catalog.error,
|
||||
listedAt: catalog.listedAt,
|
||||
fromCache: catalog.fromCache,
|
||||
tools: catalog.tools.map((tool2) => ({
|
||||
name: tool2.name,
|
||||
description: tool2.description,
|
||||
readOnlyHint: tool2.readOnlyHint,
|
||||
group: tool2.group ?? null
|
||||
}))
|
||||
}
|
||||
};
|
||||
return { ok: true, value: mcpCatalogPayload(getMcpCatalogState()) };
|
||||
}
|
||||
if (endpoint === "tools.refresh") {
|
||||
const connection = getNetxConnection();
|
||||
|
|
@ -6177,14 +6215,7 @@ function apply(ctx, config = Config({})) {
|
|||
});
|
||||
return {
|
||||
ok: catalog.phase === "connected",
|
||||
value: {
|
||||
phase: catalog.phase,
|
||||
mcpUrl: catalog.mcpUrl,
|
||||
error: catalog.error,
|
||||
listedAt: catalog.listedAt,
|
||||
fromCache: catalog.fromCache,
|
||||
toolCount: catalog.tools.length
|
||||
},
|
||||
value: { ...mcpCatalogPayload(catalog), toolCount: catalog.tools.length },
|
||||
...catalog.error ? { error: catalog.error } : {}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue