@iletai/nzb 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/copilot/mcp-config.js +64 -11
- package/dist/telegram/bot.js +3 -3
- package/dist/telegram/formatter.js +2 -1
- package/package.json +1 -1
|
@@ -1,31 +1,84 @@
|
|
|
1
|
-
import { readFileSync } from "fs";
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
let cachedConfig;
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
* Detect if running inside WSL.
|
|
7
|
+
*/
|
|
8
|
+
const isWSL = (() => {
|
|
9
|
+
try {
|
|
10
|
+
return readFileSync("/proc/version", "utf-8").toLowerCase().includes("microsoft");
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
})();
|
|
16
|
+
/**
|
|
17
|
+
* Transform a VS Code MCP server entry for native WSL execution.
|
|
18
|
+
* Strips `wsl.exe bash -c "..."` wrappers since NZB runs inside WSL directly.
|
|
19
|
+
* Also resolves `${input:...}` placeholders from environment or strips them.
|
|
20
|
+
*/
|
|
21
|
+
function transformForWSL(entry) {
|
|
22
|
+
if (!isWSL || entry.type !== "stdio")
|
|
23
|
+
return entry;
|
|
24
|
+
const cmd = entry.command;
|
|
25
|
+
const args = entry.args || [];
|
|
26
|
+
// Detect wsl.exe wrapper: wsl.exe [bash -c "actual command"]
|
|
27
|
+
if (cmd === "wsl.exe" || cmd === "wsl") {
|
|
28
|
+
// Find the actual command inside `bash -c "..."` pattern
|
|
29
|
+
const bashIdx = args.indexOf("bash");
|
|
30
|
+
const cIdx = bashIdx >= 0 ? args.indexOf("-c", bashIdx) : -1;
|
|
31
|
+
if (cIdx >= 0 && args.length > cIdx + 1) {
|
|
32
|
+
const innerCmd = args[cIdx + 1];
|
|
33
|
+
// Strip nvm source prefix if present (NZB already has node in PATH)
|
|
34
|
+
const cleaned = innerCmd
|
|
35
|
+
.replace(/^source\s+~\/\.nvm\/nvm\.sh\s*&&\s*/, "")
|
|
36
|
+
.replace(/^source\s+~\/\.nvm\/nvm\.sh\s*;\s*/, "")
|
|
37
|
+
.trim();
|
|
38
|
+
return {
|
|
39
|
+
...entry,
|
|
40
|
+
command: "bash",
|
|
41
|
+
args: ["-c", cleaned],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// npx commands referencing Windows paths — convert to Linux
|
|
46
|
+
if (cmd === "npx") {
|
|
47
|
+
const fixedArgs = args.map((a) => a.replace(/^([a-zA-Z]):\\/, (_, drive) => `/mnt/${drive.toLowerCase()}/`).replace(/\\/g, "/"));
|
|
48
|
+
return { ...entry, args: fixedArgs };
|
|
49
|
+
}
|
|
50
|
+
return entry;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Load MCP server configs from ~/.nzb/mcp.json (NZB-specific, priority)
|
|
54
|
+
* then fall back to ~/.copilot/mcp-config.json (shared with VS Code).
|
|
55
|
+
* Skips disabled entries. Transforms wsl.exe wrappers for native WSL execution.
|
|
10
56
|
*/
|
|
11
57
|
export function loadMcpConfig() {
|
|
12
58
|
if (cachedConfig)
|
|
13
59
|
return cachedConfig;
|
|
14
|
-
const
|
|
60
|
+
const nzbPath = join(homedir(), ".nzb", "mcp.json");
|
|
61
|
+
const copilotPath = join(homedir(), ".copilot", "mcp-config.json");
|
|
62
|
+
const configPath = existsSync(nzbPath) ? nzbPath : copilotPath;
|
|
15
63
|
try {
|
|
16
64
|
const raw = readFileSync(configPath, "utf-8");
|
|
17
65
|
const parsed = JSON.parse(raw);
|
|
18
66
|
if (parsed.mcpServers && typeof parsed.mcpServers === "object") {
|
|
19
|
-
// Filter out malformed entries — each server must have at least a type
|
|
20
67
|
const servers = {};
|
|
21
68
|
for (const [name, entry] of Object.entries(parsed.mcpServers)) {
|
|
22
|
-
if (entry
|
|
23
|
-
|
|
69
|
+
if (!entry || typeof entry !== "object" || !("type" in entry)) {
|
|
70
|
+
console.log(`[nzb] Skipping malformed MCP server '${name}'`);
|
|
71
|
+
continue;
|
|
24
72
|
}
|
|
25
|
-
|
|
26
|
-
|
|
73
|
+
const e = entry;
|
|
74
|
+
if (e.disabled) {
|
|
75
|
+
console.log(`[nzb] Skipping disabled MCP server '${name}'`);
|
|
76
|
+
continue;
|
|
27
77
|
}
|
|
78
|
+
const transformed = transformForWSL(e);
|
|
79
|
+
servers[name] = transformed;
|
|
28
80
|
}
|
|
81
|
+
console.log(`[nzb] Loaded ${Object.keys(servers).length} MCP servers from ${configPath}`);
|
|
29
82
|
cachedConfig = servers;
|
|
30
83
|
return servers;
|
|
31
84
|
}
|
package/dist/telegram/bot.js
CHANGED
|
@@ -330,9 +330,9 @@ export function createBot() {
|
|
|
330
330
|
const onToolEvent = (event) => {
|
|
331
331
|
console.log(`[nzb] Bot received tool event: ${event.type} ${event.toolName}`);
|
|
332
332
|
if (event.type === "tool_start") {
|
|
333
|
-
void logDebug(`🔧 Tool start: ${event.toolName}`);
|
|
333
|
+
void logDebug(`🔧 Tool start: ${event.toolName}${event.detail ? ` — ${event.detail}` : ""}`);
|
|
334
334
|
currentToolName = event.toolName;
|
|
335
|
-
toolHistory.push({ name: event.toolName, startTime: Date.now() });
|
|
335
|
+
toolHistory.push({ name: event.toolName, startTime: Date.now(), detail: event.detail });
|
|
336
336
|
const existingText = lastEditedText.replace(/^🔧 .*\n\n/, "");
|
|
337
337
|
enqueueEdit(`🔧 ${event.toolName}\n\n${existingText}`.trim() || `🔧 ${event.toolName}`);
|
|
338
338
|
}
|
|
@@ -416,7 +416,7 @@ export function createBot() {
|
|
|
416
416
|
const formatted = toTelegramMarkdown(textWithMeta);
|
|
417
417
|
let fullFormatted = formatted;
|
|
418
418
|
if (config.showReasoning && toolHistory.length > 0) {
|
|
419
|
-
const expandable = formatToolSummaryExpandable(toolHistory.map((t) => ({ name: t.name, durationMs: t.durationMs })));
|
|
419
|
+
const expandable = formatToolSummaryExpandable(toolHistory.map((t) => ({ name: t.name, durationMs: t.durationMs, detail: t.detail })));
|
|
420
420
|
fullFormatted += expandable;
|
|
421
421
|
}
|
|
422
422
|
const chunks = chunkMessage(fullFormatted);
|
|
@@ -159,7 +159,8 @@ export function formatToolSummaryExpandable(toolCalls) {
|
|
|
159
159
|
const dur = t.durationMs !== undefined
|
|
160
160
|
? ` \\(${escapeSegment((t.durationMs / 1000).toFixed(1) + "s")}\\)`
|
|
161
161
|
: "";
|
|
162
|
-
|
|
162
|
+
const detail = t.detail ? `\n> _${escapeSegment(t.detail.slice(0, 60))}_` : "";
|
|
163
|
+
return `${escapeSegment("• ")}${name}${dur}${detail}`;
|
|
163
164
|
});
|
|
164
165
|
const header = escapeSegment("🔧 Tools used:");
|
|
165
166
|
const toolList = lines.join(`\n>`);
|