@iletai/nzb 1.3.0 → 1.3.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/telegram/bot.js +115 -97
- package/package.json +2 -1
package/dist/telegram/bot.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { autoRetry } from "@grammyjs/auto-retry";
|
|
2
|
-
import {
|
|
2
|
+
import { Menu } from "@grammyjs/menu";
|
|
3
|
+
import { Bot } from "grammy";
|
|
3
4
|
import { Agent as HttpsAgent } from "https";
|
|
4
5
|
import { config, persistEnvVar, persistModel } from "../config.js";
|
|
5
6
|
import { cancelCurrentMessage, getQueueSize, getWorkers, sendToOrchestrator } from "../copilot/orchestrator.js";
|
|
@@ -10,22 +11,118 @@ import { chunkMessage, formatToolSummaryExpandable, toTelegramMarkdown } from ".
|
|
|
10
11
|
import { initLogChannel, logDebug, logError, logInfo } from "./log-channel.js";
|
|
11
12
|
let bot;
|
|
12
13
|
const startedAt = Date.now();
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
.
|
|
16
|
-
.
|
|
14
|
+
// Helper: build uptime string
|
|
15
|
+
function getUptimeStr() {
|
|
16
|
+
const uptime = Math.floor((Date.now() - startedAt) / 1000);
|
|
17
|
+
const hours = Math.floor(uptime / 3600);
|
|
18
|
+
const minutes = Math.floor((uptime % 3600) / 60);
|
|
19
|
+
const seconds = uptime % 60;
|
|
20
|
+
return hours > 0 ? `${hours}h ${minutes}m ${seconds}s` : minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
21
|
+
}
|
|
22
|
+
// Settings sub-menu
|
|
23
|
+
const settingsMenu = new Menu("settings-menu")
|
|
24
|
+
.text((ctx) => `${config.showReasoning ? "✅" : "❌"} Show Reasoning`, async (ctx) => {
|
|
25
|
+
config.showReasoning = !config.showReasoning;
|
|
26
|
+
persistEnvVar("SHOW_REASONING", config.showReasoning ? "true" : "false");
|
|
27
|
+
ctx.menu.update();
|
|
28
|
+
await ctx.answerCallbackQuery(`Reasoning ${config.showReasoning ? "ON" : "OFF"}`);
|
|
29
|
+
})
|
|
17
30
|
.row()
|
|
18
|
-
.
|
|
19
|
-
.
|
|
31
|
+
.back("🔙 Back", async (ctx) => {
|
|
32
|
+
await ctx.editMessageText("NZB Menu:");
|
|
33
|
+
});
|
|
34
|
+
// Main interactive menu with navigation
|
|
35
|
+
const mainMenu = new Menu("main-menu")
|
|
36
|
+
.text("📊 Status", async (ctx) => {
|
|
37
|
+
const workers = Array.from(getWorkers().values());
|
|
38
|
+
const lines = [
|
|
39
|
+
"📊 NZB Status",
|
|
40
|
+
`Model: ${config.copilotModel}`,
|
|
41
|
+
`Uptime: ${getUptimeStr()}`,
|
|
42
|
+
`Workers: ${workers.length} active`,
|
|
43
|
+
`Queue: ${getQueueSize()} pending`,
|
|
44
|
+
];
|
|
45
|
+
await ctx.answerCallbackQuery();
|
|
46
|
+
await ctx.reply(lines.join("\n"));
|
|
47
|
+
})
|
|
48
|
+
.text("🤖 Model", async (ctx) => {
|
|
49
|
+
await ctx.answerCallbackQuery();
|
|
50
|
+
await ctx.reply(`Current model: ${config.copilotModel}`);
|
|
51
|
+
})
|
|
20
52
|
.row()
|
|
21
|
-
.text("
|
|
22
|
-
.
|
|
53
|
+
.text("👥 Workers", async (ctx) => {
|
|
54
|
+
await ctx.answerCallbackQuery();
|
|
55
|
+
const workers = Array.from(getWorkers().values());
|
|
56
|
+
if (workers.length === 0) {
|
|
57
|
+
await ctx.reply("No active worker sessions.");
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const lines = workers.map((w) => `• ${w.name} (${w.workingDir}) — ${w.status}`);
|
|
61
|
+
await ctx.reply(lines.join("\n"));
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
.text("🧠 Skills", async (ctx) => {
|
|
65
|
+
await ctx.answerCallbackQuery();
|
|
66
|
+
const skills = listSkills();
|
|
67
|
+
if (skills.length === 0) {
|
|
68
|
+
await ctx.reply("No skills installed.");
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const lines = skills.map((s) => `• ${s.name} (${s.source}) — ${s.description}`);
|
|
72
|
+
await ctx.reply(lines.join("\n"));
|
|
73
|
+
}
|
|
74
|
+
})
|
|
23
75
|
.row()
|
|
24
|
-
.text("
|
|
76
|
+
.text("🗂 Memory", async (ctx) => {
|
|
77
|
+
await ctx.answerCallbackQuery();
|
|
78
|
+
const memories = searchMemories(undefined, undefined, 50);
|
|
79
|
+
if (memories.length === 0) {
|
|
80
|
+
await ctx.reply("No memories stored.");
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await ctx.reply(formatMemoryList(memories));
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.submenu("⚙️ Settings", "settings-menu", async (ctx) => {
|
|
87
|
+
await ctx.editMessageText("⚙️ Settings\n\n" +
|
|
88
|
+
`🔧 Show Reasoning: ${config.showReasoning ? "✅ ON" : "❌ OFF"}\n` +
|
|
89
|
+
` └ Hiển thị tools đã dùng + thời gian cuối mỗi phản hồi\n\n` +
|
|
90
|
+
`🤖 Model: ${config.copilotModel}\n` +
|
|
91
|
+
` └ Dùng /model <name> để đổi`);
|
|
92
|
+
})
|
|
93
|
+
.row()
|
|
94
|
+
.text("❌ Cancel", async (ctx) => {
|
|
95
|
+
await ctx.answerCallbackQuery();
|
|
96
|
+
const cancelled = await cancelCurrentMessage();
|
|
97
|
+
await ctx.reply(cancelled ? "Cancelled." : "Nothing to cancel.");
|
|
98
|
+
});
|
|
99
|
+
// Register sub-menu as child
|
|
100
|
+
mainMenu.register(settingsMenu);
|
|
25
101
|
// Direct-connection HTTPS agent for Telegram API requests.
|
|
26
102
|
// This bypasses corporate proxy (HTTP_PROXY/HTTPS_PROXY env vars) without
|
|
27
103
|
// modifying process.env, so other services (Copilot SDK, MCP, npm) are unaffected.
|
|
28
104
|
const telegramAgent = new HttpsAgent({ keepAlive: true });
|
|
105
|
+
const CATEGORY_ICONS = {
|
|
106
|
+
project: "📦",
|
|
107
|
+
preference: "⚙️",
|
|
108
|
+
fact: "💡",
|
|
109
|
+
person: "👤",
|
|
110
|
+
routine: "🔄",
|
|
111
|
+
};
|
|
112
|
+
function formatMemoryList(memories) {
|
|
113
|
+
// Group by category
|
|
114
|
+
const groups = {};
|
|
115
|
+
for (const m of memories) {
|
|
116
|
+
(groups[m.category] ??= []).push(m);
|
|
117
|
+
}
|
|
118
|
+
const sections = Object.entries(groups).map(([cat, items]) => {
|
|
119
|
+
const icon = CATEGORY_ICONS[cat] || "📝";
|
|
120
|
+
const header = `${icon} ${cat.charAt(0).toUpperCase() + cat.slice(1)}`;
|
|
121
|
+
const lines = items.map((m) => ` #${m.id} ${m.content}`);
|
|
122
|
+
return `${header}\n${lines.join("\n")}`;
|
|
123
|
+
});
|
|
124
|
+
return `🧠 Memory (${memories.length})\n\n${sections.join("\n\n")}`;
|
|
125
|
+
}
|
|
29
126
|
export function createBot() {
|
|
30
127
|
if (!config.telegramBotToken) {
|
|
31
128
|
throw new Error("Telegram bot token is missing. Run 'nzb setup' and enter the bot token from @BotFather.");
|
|
@@ -53,6 +150,8 @@ export function createBot() {
|
|
|
53
150
|
}
|
|
54
151
|
await next();
|
|
55
152
|
});
|
|
153
|
+
// Register interactive menu plugin
|
|
154
|
+
bot.use(mainMenu);
|
|
56
155
|
// /start and /help — with inline menu
|
|
57
156
|
bot.command("start", (ctx) => ctx.reply("NZB is online. Send me anything, or use the menu below:", { reply_markup: mainMenu }));
|
|
58
157
|
bot.command("help", (ctx) => ctx.reply("I'm NZB, your AI daemon.\n\n" +
|
|
@@ -108,8 +207,7 @@ export function createBot() {
|
|
|
108
207
|
await ctx.reply("No memories stored.");
|
|
109
208
|
}
|
|
110
209
|
else {
|
|
111
|
-
|
|
112
|
-
await ctx.reply(lines.join("\n") + `\n\n${memories.length} total`);
|
|
210
|
+
await ctx.reply(formatMemoryList(memories));
|
|
113
211
|
}
|
|
114
212
|
});
|
|
115
213
|
bot.command("skills", async (ctx) => {
|
|
@@ -156,92 +254,12 @@ export function createBot() {
|
|
|
156
254
|
});
|
|
157
255
|
}, 500);
|
|
158
256
|
});
|
|
159
|
-
// /settings — show toggleable settings with inline keyboard
|
|
160
|
-
const buildSettingsKeyboard = () => new InlineKeyboard()
|
|
161
|
-
.text(`${config.showReasoning ? "✅" : "❌"} Show Reasoning`, "setting:toggle:reasoning")
|
|
162
|
-
.row()
|
|
163
|
-
.text("🔙 Back to Menu", "action:menu");
|
|
164
|
-
const buildSettingsText = () => "⚙️ Settings\n\n" +
|
|
165
|
-
`🔧 Show Reasoning: ${config.showReasoning ? "✅ ON" : "❌ OFF"}\n` +
|
|
166
|
-
` └ Hiển thị tools đã dùng + thời gian cuối mỗi phản hồi\n\n` +
|
|
167
|
-
`🤖 Model: ${config.copilotModel}\n` +
|
|
168
|
-
` └ Dùng /model <name> để đổi`;
|
|
169
257
|
bot.command("settings", async (ctx) => {
|
|
170
|
-
await ctx.reply(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const uptime = Math.floor((Date.now() - startedAt) / 1000);
|
|
176
|
-
const hours = Math.floor(uptime / 3600);
|
|
177
|
-
const minutes = Math.floor((uptime % 3600) / 60);
|
|
178
|
-
const seconds = uptime % 60;
|
|
179
|
-
const uptimeStr = hours > 0 ? `${hours}h ${minutes}m ${seconds}s` : minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
180
|
-
const workers = Array.from(getWorkers().values());
|
|
181
|
-
const lines = [
|
|
182
|
-
"📊 NZB Status",
|
|
183
|
-
`Model: ${config.copilotModel}`,
|
|
184
|
-
`Uptime: ${uptimeStr}`,
|
|
185
|
-
`Workers: ${workers.length} active`,
|
|
186
|
-
`Queue: ${getQueueSize()} pending`,
|
|
187
|
-
];
|
|
188
|
-
await ctx.reply(lines.join("\n"));
|
|
189
|
-
});
|
|
190
|
-
bot.callbackQuery("action:model", async (ctx) => {
|
|
191
|
-
await ctx.answerCallbackQuery();
|
|
192
|
-
await ctx.reply(`Current model: ${config.copilotModel}`);
|
|
193
|
-
});
|
|
194
|
-
bot.callbackQuery("action:workers", async (ctx) => {
|
|
195
|
-
await ctx.answerCallbackQuery();
|
|
196
|
-
const workers = Array.from(getWorkers().values());
|
|
197
|
-
if (workers.length === 0) {
|
|
198
|
-
await ctx.reply("No active worker sessions.");
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
const lines = workers.map((w) => `• ${w.name} (${w.workingDir}) — ${w.status}`);
|
|
202
|
-
await ctx.reply(lines.join("\n"));
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
bot.callbackQuery("action:skills", async (ctx) => {
|
|
206
|
-
await ctx.answerCallbackQuery();
|
|
207
|
-
const skills = listSkills();
|
|
208
|
-
if (skills.length === 0) {
|
|
209
|
-
await ctx.reply("No skills installed.");
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
const lines = skills.map((s) => `• ${s.name} (${s.source}) — ${s.description}`);
|
|
213
|
-
await ctx.reply(lines.join("\n"));
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
bot.callbackQuery("action:memory", async (ctx) => {
|
|
217
|
-
await ctx.answerCallbackQuery();
|
|
218
|
-
const memories = searchMemories(undefined, undefined, 50);
|
|
219
|
-
if (memories.length === 0) {
|
|
220
|
-
await ctx.reply("No memories stored.");
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
const lines = memories.map((m) => `#${m.id} [${m.category}] ${m.content}`);
|
|
224
|
-
await ctx.reply(lines.join("\n") + `\n\n${memories.length} total`);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
bot.callbackQuery("action:cancel", async (ctx) => {
|
|
228
|
-
await ctx.answerCallbackQuery();
|
|
229
|
-
const cancelled = await cancelCurrentMessage();
|
|
230
|
-
await ctx.reply(cancelled ? "Cancelled." : "Nothing to cancel.");
|
|
231
|
-
});
|
|
232
|
-
bot.callbackQuery("action:settings", async (ctx) => {
|
|
233
|
-
await ctx.answerCallbackQuery();
|
|
234
|
-
await ctx.reply(buildSettingsText(), { reply_markup: buildSettingsKeyboard() });
|
|
235
|
-
});
|
|
236
|
-
bot.callbackQuery("action:menu", async (ctx) => {
|
|
237
|
-
await ctx.answerCallbackQuery();
|
|
238
|
-
await ctx.editMessageText("NZB Menu:", { reply_markup: mainMenu });
|
|
239
|
-
});
|
|
240
|
-
bot.callbackQuery("setting:toggle:reasoning", async (ctx) => {
|
|
241
|
-
config.showReasoning = !config.showReasoning;
|
|
242
|
-
persistEnvVar("SHOW_REASONING", config.showReasoning ? "true" : "false");
|
|
243
|
-
await ctx.answerCallbackQuery(`Reasoning ${config.showReasoning ? "ON" : "OFF"}`);
|
|
244
|
-
await ctx.editMessageText(buildSettingsText(), { reply_markup: buildSettingsKeyboard() });
|
|
258
|
+
await ctx.reply("⚙️ Settings\n\n" +
|
|
259
|
+
`🔧 Show Reasoning: ${config.showReasoning ? "✅ ON" : "❌ OFF"}\n` +
|
|
260
|
+
` └ Hiển thị tools đã dùng + thời gian cuối mỗi phản hồi\n\n` +
|
|
261
|
+
`🤖 Model: ${config.copilotModel}\n` +
|
|
262
|
+
` └ Dùng /model <name> để đổi`, { reply_markup: settingsMenu });
|
|
245
263
|
});
|
|
246
264
|
// Handle all text messages — progressive streaming with tool event feedback
|
|
247
265
|
bot.on("message:text", async (ctx) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iletai/nzb",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "NZB — a personal AI assistant for developers, built on the GitHub Copilot SDK",
|
|
5
5
|
"bin": {
|
|
6
6
|
"nzb": "dist/cli.js"
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@github/copilot-sdk": "^0.1.26",
|
|
50
50
|
"@grammyjs/auto-retry": "^2.0.2",
|
|
51
|
+
"@grammyjs/menu": "^1.3.1",
|
|
51
52
|
"better-sqlite3": "^12.6.2",
|
|
52
53
|
"dotenv": "^17.3.1",
|
|
53
54
|
"express": "^5.2.1",
|