@iletai/nzb 1.7.0 → 1.7.3
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/api/server.js +14 -5
- package/dist/cli.js +1 -0
- package/dist/config.js +12 -3
- package/dist/copilot/client.js +17 -16
- package/dist/copilot/mcp-config.js +2 -0
- package/dist/copilot/orchestrator.js +181 -51
- package/dist/copilot/skills.js +4 -2
- package/dist/copilot/tools.js +33 -5
- package/dist/copilot/types.js +2 -0
- package/dist/daemon.js +11 -10
- package/dist/setup.js +3 -2
- package/dist/store/conversation.js +96 -0
- package/dist/store/db.js +6 -206
- package/dist/store/memory.js +90 -0
- package/dist/store/team-store.js +51 -0
- package/dist/telegram/bot.js +77 -8
- package/dist/telegram/handlers/commands.js +1 -1
- package/dist/telegram/handlers/media.js +63 -6
- package/dist/telegram/handlers/streaming.js +223 -188
- package/dist/telegram/handlers/suggestions.js +22 -1
- package/dist/telegram/log-channel.js +2 -2
- package/dist/telegram/menus.js +243 -99
- package/dist/tui/ansi.js +19 -0
- package/dist/tui/api-client.js +158 -0
- package/dist/tui/debug.js +27 -0
- package/dist/tui/renderer.js +59 -0
- package/dist/tui/stream.js +163 -0
- package/dist/update.js +2 -0
- package/dist/utils.js +102 -0
- package/package.json +1 -1
package/dist/telegram/menus.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Menu } from "@grammyjs/menu";
|
|
|
2
2
|
import { config, persistEnvVar, persistModel } from "../config.js";
|
|
3
3
|
import { cancelCurrentMessage, getQueueSize, getWorkers } from "../copilot/orchestrator.js";
|
|
4
4
|
import { listSkills } from "../copilot/skills.js";
|
|
5
|
-
import { searchMemories } from "../store/
|
|
5
|
+
import { searchMemories } from "../store/memory.js";
|
|
6
6
|
import { chunkMessage, escapeHtml, truncateForTelegram } from "./formatter.js";
|
|
7
7
|
// Worker timeout presets (ms → display label)
|
|
8
8
|
export const TIMEOUT_PRESETS = [
|
|
@@ -77,152 +77,296 @@ export function createMenus(getUptimeStr) {
|
|
|
77
77
|
// Settings sub-menu
|
|
78
78
|
const settingsMenu = new Menu("settings-menu")
|
|
79
79
|
.text(() => `⏱ Timeout: ${getTimeoutLabel()}`, async (ctx) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
try {
|
|
81
|
+
const idx = TIMEOUT_PRESETS.findIndex((p) => p.ms === config.workerTimeoutMs);
|
|
82
|
+
const next = TIMEOUT_PRESETS[(idx + 1) % TIMEOUT_PRESETS.length];
|
|
83
|
+
config.workerTimeoutMs = next.ms;
|
|
84
|
+
persistEnvVar("WORKER_TIMEOUT", String(next.ms));
|
|
85
|
+
ctx.menu.update();
|
|
86
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
87
|
+
await ctx.answerCallbackQuery(`Timeout → ${next.label}`);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
91
|
+
await ctx.answerCallbackQuery({
|
|
92
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
93
|
+
show_alert: true,
|
|
94
|
+
}).catch(() => { });
|
|
95
|
+
}
|
|
87
96
|
})
|
|
88
97
|
.row()
|
|
89
98
|
.text(() => `🤖 ${config.copilotModel}`, async (ctx) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
try {
|
|
100
|
+
const models = await getAvailableModels();
|
|
101
|
+
if (models.length === 0) {
|
|
102
|
+
await ctx.answerCallbackQuery("No models available");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const idx = models.indexOf(config.copilotModel);
|
|
106
|
+
const next = models[(idx + 1) % models.length];
|
|
107
|
+
config.copilotModel = next;
|
|
108
|
+
persistModel(next);
|
|
109
|
+
ctx.menu.update();
|
|
110
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
111
|
+
await ctx.answerCallbackQuery(`Model → ${next}`);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
115
|
+
await ctx.answerCallbackQuery({
|
|
116
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
117
|
+
show_alert: true,
|
|
118
|
+
}).catch(() => { });
|
|
119
|
+
}
|
|
102
120
|
})
|
|
103
121
|
.row()
|
|
104
122
|
.text(() => `${config.showReasoning ? "✅" : "❌"} Show Reasoning`, async (ctx) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
try {
|
|
124
|
+
config.showReasoning = !config.showReasoning;
|
|
125
|
+
persistEnvVar("SHOW_REASONING", config.showReasoning ? "true" : "false");
|
|
126
|
+
ctx.menu.update();
|
|
127
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
128
|
+
await ctx.answerCallbackQuery(`Reasoning ${config.showReasoning ? "ON" : "OFF"}`);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
132
|
+
await ctx.answerCallbackQuery({
|
|
133
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
134
|
+
show_alert: true,
|
|
135
|
+
}).catch(() => { });
|
|
136
|
+
}
|
|
110
137
|
})
|
|
111
138
|
.row()
|
|
112
139
|
.text(() => `🧠 Think: ${config.thinkingLevel}`, async (ctx) => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
140
|
+
try {
|
|
141
|
+
const levels = ["off", "low", "medium", "high"];
|
|
142
|
+
const idx = levels.indexOf(config.thinkingLevel);
|
|
143
|
+
const next = levels[(idx + 1) % levels.length];
|
|
144
|
+
config.thinkingLevel = next;
|
|
145
|
+
persistEnvVar("THINKING_LEVEL", next);
|
|
146
|
+
ctx.menu.update();
|
|
147
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
148
|
+
await ctx.answerCallbackQuery(`Thinking → ${next}`);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
152
|
+
await ctx.answerCallbackQuery({
|
|
153
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
154
|
+
show_alert: true,
|
|
155
|
+
}).catch(() => { });
|
|
156
|
+
}
|
|
121
157
|
})
|
|
122
158
|
.text(() => `📝 ${config.verboseMode ? "Verbose" : "Concise"}`, async (ctx) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
159
|
+
try {
|
|
160
|
+
config.verboseMode = !config.verboseMode;
|
|
161
|
+
persistEnvVar("VERBOSE_MODE", config.verboseMode ? "true" : "false");
|
|
162
|
+
ctx.menu.update();
|
|
163
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
164
|
+
await ctx.answerCallbackQuery(`Verbose ${config.verboseMode ? "ON" : "OFF"}`);
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
168
|
+
await ctx.answerCallbackQuery({
|
|
169
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
170
|
+
show_alert: true,
|
|
171
|
+
}).catch(() => { });
|
|
172
|
+
}
|
|
128
173
|
})
|
|
129
174
|
.row()
|
|
130
175
|
.text(() => `💡 Reasoning: ${config.reasoningEffort}`, async (ctx) => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
176
|
+
try {
|
|
177
|
+
const efforts = ["low", "medium", "high"];
|
|
178
|
+
const idx = efforts.indexOf(config.reasoningEffort);
|
|
179
|
+
const next = efforts[(idx + 1) % efforts.length];
|
|
180
|
+
config.reasoningEffort = next;
|
|
181
|
+
persistEnvVar("REASONING_EFFORT", next);
|
|
182
|
+
ctx.menu.update();
|
|
183
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
184
|
+
await ctx.answerCallbackQuery(`Reasoning → ${next}`);
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
188
|
+
await ctx.answerCallbackQuery({
|
|
189
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
190
|
+
show_alert: true,
|
|
191
|
+
}).catch(() => { });
|
|
192
|
+
}
|
|
139
193
|
})
|
|
140
194
|
.row()
|
|
141
195
|
.text(() => `📊 Usage: ${config.usageMode}`, async (ctx) => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
196
|
+
try {
|
|
197
|
+
const modes = ["off", "tokens", "full"];
|
|
198
|
+
const idx = modes.indexOf(config.usageMode);
|
|
199
|
+
const next = modes[(idx + 1) % modes.length];
|
|
200
|
+
config.usageMode = next;
|
|
201
|
+
persistEnvVar("USAGE_MODE", next);
|
|
202
|
+
ctx.menu.update();
|
|
203
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
204
|
+
await ctx.answerCallbackQuery(`Usage → ${next}`);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
208
|
+
await ctx.answerCallbackQuery({
|
|
209
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
210
|
+
show_alert: true,
|
|
211
|
+
}).catch(() => { });
|
|
212
|
+
}
|
|
150
213
|
})
|
|
151
214
|
.row()
|
|
152
215
|
.text(() => `📌 v${process.env.npm_package_version || "?"} · uptime ${getUptimeStr()}`, async (ctx) => {
|
|
153
|
-
|
|
216
|
+
try {
|
|
217
|
+
await ctx.answerCallbackQuery(`Uptime: ${getUptimeStr()}`);
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
221
|
+
await ctx.answerCallbackQuery({
|
|
222
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
223
|
+
show_alert: true,
|
|
224
|
+
}).catch(() => { });
|
|
225
|
+
}
|
|
154
226
|
})
|
|
155
227
|
.row()
|
|
156
228
|
.back("🔙 Back", async (ctx) => {
|
|
157
|
-
|
|
229
|
+
try {
|
|
230
|
+
await ctx.editMessageText("NZB Menu:");
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
234
|
+
await ctx.answerCallbackQuery({
|
|
235
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
236
|
+
show_alert: true,
|
|
237
|
+
}).catch(() => { });
|
|
238
|
+
}
|
|
158
239
|
});
|
|
159
240
|
// Main interactive menu with navigation
|
|
160
241
|
const mainMenu = new Menu("main-menu")
|
|
161
242
|
.text("📊 Status", async (ctx) => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
243
|
+
try {
|
|
244
|
+
const workers = Array.from(getWorkers().values());
|
|
245
|
+
const lines = [
|
|
246
|
+
"📊 NZB Status",
|
|
247
|
+
`Model: ${config.copilotModel}`,
|
|
248
|
+
`Thinking: ${config.thinkingLevel}`,
|
|
249
|
+
`Verbose: ${config.verboseMode ? "on" : "off"}`,
|
|
250
|
+
`Usage: ${config.usageMode}`,
|
|
251
|
+
`Uptime: ${getUptimeStr()}`,
|
|
252
|
+
`Workers: ${workers.length} active`,
|
|
253
|
+
`Queue: ${getQueueSize()} pending`,
|
|
254
|
+
];
|
|
255
|
+
await ctx.answerCallbackQuery();
|
|
256
|
+
await ctx.reply(lines.join("\n"));
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
260
|
+
await ctx.answerCallbackQuery({
|
|
261
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
262
|
+
show_alert: true,
|
|
263
|
+
}).catch(() => { });
|
|
264
|
+
}
|
|
175
265
|
})
|
|
176
266
|
.text("🤖 Model", async (ctx) => {
|
|
177
|
-
|
|
178
|
-
|
|
267
|
+
try {
|
|
268
|
+
await ctx.answerCallbackQuery();
|
|
269
|
+
await ctx.reply(`Current model: ${config.copilotModel}`);
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
273
|
+
await ctx.answerCallbackQuery({
|
|
274
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
275
|
+
show_alert: true,
|
|
276
|
+
}).catch(() => { });
|
|
277
|
+
}
|
|
179
278
|
})
|
|
180
279
|
.row()
|
|
181
280
|
.text("👥 Workers", async (ctx) => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
281
|
+
try {
|
|
282
|
+
await ctx.answerCallbackQuery();
|
|
283
|
+
const workers = Array.from(getWorkers().values());
|
|
284
|
+
if (workers.length === 0) {
|
|
285
|
+
await ctx.reply("No active worker sessions.");
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
const lines = workers.map((w) => `• ${w.name} (${w.workingDir}) — ${w.status}`);
|
|
289
|
+
await ctx.reply(truncateForTelegram(lines.join("\n")));
|
|
290
|
+
}
|
|
186
291
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
await ctx.
|
|
292
|
+
catch (err) {
|
|
293
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
294
|
+
await ctx.answerCallbackQuery({
|
|
295
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
296
|
+
show_alert: true,
|
|
297
|
+
}).catch(() => { });
|
|
190
298
|
}
|
|
191
299
|
})
|
|
192
300
|
.text("🧠 Skills", async (ctx) => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
301
|
+
try {
|
|
302
|
+
await ctx.answerCallbackQuery();
|
|
303
|
+
const skills = listSkills();
|
|
304
|
+
if (skills.length === 0) {
|
|
305
|
+
await ctx.reply("No skills installed.");
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
const lines = skills.map((s) => `• ${s.name} (${s.source}) — ${s.description}`);
|
|
309
|
+
await ctx.reply(truncateForTelegram(lines.join("\n")));
|
|
310
|
+
}
|
|
197
311
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
await ctx.
|
|
312
|
+
catch (err) {
|
|
313
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
314
|
+
await ctx.answerCallbackQuery({
|
|
315
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
316
|
+
show_alert: true,
|
|
317
|
+
}).catch(() => { });
|
|
201
318
|
}
|
|
202
319
|
})
|
|
203
320
|
.row()
|
|
204
321
|
.text("🗂 Memory", async (ctx) => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
else {
|
|
211
|
-
const formatted = formatMemoryList(memories);
|
|
212
|
-
const chunks = chunkMessage(formatted);
|
|
213
|
-
for (const chunk of chunks) {
|
|
214
|
-
await ctx.reply(chunk, { parse_mode: "HTML" });
|
|
322
|
+
try {
|
|
323
|
+
await ctx.answerCallbackQuery();
|
|
324
|
+
const memories = searchMemories(undefined, undefined, 50);
|
|
325
|
+
if (memories.length === 0) {
|
|
326
|
+
await ctx.reply("No memories stored.");
|
|
215
327
|
}
|
|
328
|
+
else {
|
|
329
|
+
const formatted = formatMemoryList(memories);
|
|
330
|
+
const chunks = chunkMessage(formatted);
|
|
331
|
+
for (const chunk of chunks) {
|
|
332
|
+
await ctx.reply(chunk, { parse_mode: "HTML" });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch (err) {
|
|
337
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
338
|
+
await ctx.answerCallbackQuery({
|
|
339
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
340
|
+
show_alert: true,
|
|
341
|
+
}).catch(() => { });
|
|
216
342
|
}
|
|
217
343
|
})
|
|
218
344
|
.submenu("⚙️ Settings", "settings-menu", async (ctx) => {
|
|
219
|
-
|
|
345
|
+
try {
|
|
346
|
+
await ctx.editMessageText(buildSettingsText(getUptimeStr));
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
350
|
+
await ctx.answerCallbackQuery({
|
|
351
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
352
|
+
show_alert: true,
|
|
353
|
+
}).catch(() => { });
|
|
354
|
+
}
|
|
220
355
|
})
|
|
221
356
|
.row()
|
|
222
357
|
.text("❌ Cancel", async (ctx) => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
358
|
+
try {
|
|
359
|
+
await ctx.answerCallbackQuery();
|
|
360
|
+
const cancelled = await cancelCurrentMessage();
|
|
361
|
+
await ctx.reply(cancelled ? "Cancelled." : "Nothing to cancel.");
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
console.error("[nzb] Menu callback error:", err instanceof Error ? err.message : err);
|
|
365
|
+
await ctx.answerCallbackQuery({
|
|
366
|
+
text: `Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
367
|
+
show_alert: true,
|
|
368
|
+
}).catch(() => { });
|
|
369
|
+
}
|
|
226
370
|
});
|
|
227
371
|
// Register sub-menu as child
|
|
228
372
|
mainMenu.register(settingsMenu);
|
package/dist/tui/ansi.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// ── ANSI helpers ──────────────────────────────────────────
|
|
2
|
+
export const C = {
|
|
3
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
4
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
5
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
6
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
7
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
8
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
9
|
+
magenta: (s) => `\x1b[35m${s}\x1b[0m`,
|
|
10
|
+
boldCyan: (s) => `\x1b[1;36m${s}\x1b[0m`,
|
|
11
|
+
bgDim: (s) => `\x1b[48;5;236m${s}\x1b[0m`,
|
|
12
|
+
coral: (s) => `\x1b[38;2;255;127;80m${s}\x1b[0m`,
|
|
13
|
+
boldWhite: (s) => `\x1b[1;97m${s}\x1b[0m`,
|
|
14
|
+
blue: (s) => `\x1b[38;2;14;165;233m${s}\x1b[0m`,
|
|
15
|
+
};
|
|
16
|
+
// ── Layout constants ─────────────────────────────────────
|
|
17
|
+
export const LABEL_PAD = " "; // 10-char indent for continuation lines
|
|
18
|
+
export const NZB_LABEL = ` ${C.cyan("NZB")} `;
|
|
19
|
+
//# sourceMappingURL=ansi.js.map
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import * as http from "http";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { API_TOKEN_PATH } from "../paths.js";
|
|
4
|
+
import { C } from "./ansi.js";
|
|
5
|
+
import { debugLog, previewForDebug } from "./debug.js";
|
|
6
|
+
export const API_BASE = process.env.MAX_API_URL || "http://127.0.0.1:7777";
|
|
7
|
+
// Load API auth token (if it exists)
|
|
8
|
+
let apiToken = null;
|
|
9
|
+
try {
|
|
10
|
+
if (existsSync(API_TOKEN_PATH)) {
|
|
11
|
+
apiToken = readFileSync(API_TOKEN_PATH, "utf-8").trim();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
console.error("Warning: Could not read API token from " + API_TOKEN_PATH + " — requests may fail.");
|
|
16
|
+
}
|
|
17
|
+
export function authHeaders() {
|
|
18
|
+
return apiToken ? { Authorization: `Bearer ${apiToken}` } : {};
|
|
19
|
+
}
|
|
20
|
+
let promptFn = () => { };
|
|
21
|
+
/** Initialize the API client with a prompt callback (called after API responses). */
|
|
22
|
+
export function initApiClient(opts) {
|
|
23
|
+
promptFn = opts.prompt;
|
|
24
|
+
}
|
|
25
|
+
/** Silent GET — no re-prompt (used for startup info). */
|
|
26
|
+
export function apiGetSilent(path, cb) {
|
|
27
|
+
const url = new URL(path, API_BASE);
|
|
28
|
+
http
|
|
29
|
+
.get(url, { headers: authHeaders() }, (res) => {
|
|
30
|
+
let data = "";
|
|
31
|
+
res.on("data", (chunk) => (data += chunk));
|
|
32
|
+
res.on("end", () => {
|
|
33
|
+
try {
|
|
34
|
+
cb(JSON.parse(data));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* ignore */
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
})
|
|
41
|
+
.on("error", () => {
|
|
42
|
+
cb(null);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** GET a JSON endpoint and call back with parsed result. */
|
|
46
|
+
export function apiGet(path, cb) {
|
|
47
|
+
const url = new URL(path, API_BASE);
|
|
48
|
+
http
|
|
49
|
+
.get(url, { headers: authHeaders() }, (res) => {
|
|
50
|
+
let data = "";
|
|
51
|
+
res.on("data", (chunk) => (data += chunk));
|
|
52
|
+
res.on("end", () => {
|
|
53
|
+
try {
|
|
54
|
+
cb(JSON.parse(data));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
console.log(data);
|
|
58
|
+
}
|
|
59
|
+
promptFn();
|
|
60
|
+
});
|
|
61
|
+
})
|
|
62
|
+
.on("error", (err) => {
|
|
63
|
+
console.error(C.red(` Error: ${err.message}`));
|
|
64
|
+
promptFn();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** POST a JSON endpoint and call back with parsed result. */
|
|
68
|
+
export function apiPost(path, body, cb) {
|
|
69
|
+
const json = JSON.stringify(body);
|
|
70
|
+
const url = new URL(path, API_BASE);
|
|
71
|
+
const req = http.request(url, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(json), ...authHeaders() },
|
|
74
|
+
}, (res) => {
|
|
75
|
+
let data = "";
|
|
76
|
+
res.on("data", (chunk) => (data += chunk));
|
|
77
|
+
res.on("end", () => {
|
|
78
|
+
try {
|
|
79
|
+
cb(JSON.parse(data));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
console.log(data);
|
|
83
|
+
}
|
|
84
|
+
promptFn();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
req.on("error", (err) => {
|
|
88
|
+
console.error(C.red(` Error: ${err.message}`));
|
|
89
|
+
promptFn();
|
|
90
|
+
});
|
|
91
|
+
req.write(json);
|
|
92
|
+
req.end();
|
|
93
|
+
}
|
|
94
|
+
/** DELETE an endpoint and call back with parsed result. */
|
|
95
|
+
export function apiDelete(path, cb) {
|
|
96
|
+
const url = new URL(path, API_BASE);
|
|
97
|
+
const req = http.request(url, {
|
|
98
|
+
method: "DELETE",
|
|
99
|
+
headers: authHeaders(),
|
|
100
|
+
}, (res) => {
|
|
101
|
+
let data = "";
|
|
102
|
+
res.on("data", (chunk) => (data += chunk));
|
|
103
|
+
res.on("end", () => {
|
|
104
|
+
try {
|
|
105
|
+
cb(JSON.parse(data));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
console.log(data);
|
|
109
|
+
}
|
|
110
|
+
promptFn();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
req.on("error", (err) => {
|
|
114
|
+
console.error(C.red(` Error: ${err.message}`));
|
|
115
|
+
promptFn();
|
|
116
|
+
});
|
|
117
|
+
req.end();
|
|
118
|
+
}
|
|
119
|
+
/** Send a message to the orchestrator via HTTP POST. */
|
|
120
|
+
export function sendMessage(prompt, requestId, connectionId, onError) {
|
|
121
|
+
const body = JSON.stringify({ prompt, connectionId });
|
|
122
|
+
const url = new URL("/message", API_BASE);
|
|
123
|
+
debugLog("message-send-start", {
|
|
124
|
+
requestId,
|
|
125
|
+
promptLength: prompt.length,
|
|
126
|
+
connectionId: connectionId || null,
|
|
127
|
+
});
|
|
128
|
+
const req = http.request(url, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: {
|
|
131
|
+
"Content-Type": "application/json",
|
|
132
|
+
"Content-Length": Buffer.byteLength(body),
|
|
133
|
+
...authHeaders(),
|
|
134
|
+
},
|
|
135
|
+
}, (res) => {
|
|
136
|
+
let data = "";
|
|
137
|
+
res.on("data", (chunk) => (data += chunk));
|
|
138
|
+
res.on("end", () => {
|
|
139
|
+
debugLog("message-send-end", {
|
|
140
|
+
requestId,
|
|
141
|
+
statusCode: res.statusCode || null,
|
|
142
|
+
responseLength: data.length,
|
|
143
|
+
responsePreview: previewForDebug(data),
|
|
144
|
+
});
|
|
145
|
+
if (res.statusCode !== 200) {
|
|
146
|
+
onError(data);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
req.on("error", (err) => {
|
|
151
|
+
debugLog("message-send-error", { requestId, error: err.message });
|
|
152
|
+
onError(`Failed to send: ${err.message}`);
|
|
153
|
+
});
|
|
154
|
+
req.write(body);
|
|
155
|
+
req.end();
|
|
156
|
+
debugLog("message-send-dispatched", { requestId, byteLength: Buffer.byteLength(body) });
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { appendFileSync } from "fs";
|
|
2
|
+
import { TUI_DEBUG_LOG_PATH } from "../paths.js";
|
|
3
|
+
export const TUI_DEBUG_ENABLED = /^(1|true|yes|on)$/i.test((process.env.NZB_TUI_DEBUG || "").trim());
|
|
4
|
+
let debugWriteFailureReported = false;
|
|
5
|
+
export function previewForDebug(text, max = 120) {
|
|
6
|
+
return text.slice(0, max).replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
7
|
+
}
|
|
8
|
+
export function debugLog(event, data = {}) {
|
|
9
|
+
if (!TUI_DEBUG_ENABLED)
|
|
10
|
+
return;
|
|
11
|
+
const entry = {
|
|
12
|
+
ts: new Date().toISOString(),
|
|
13
|
+
event,
|
|
14
|
+
...data,
|
|
15
|
+
};
|
|
16
|
+
try {
|
|
17
|
+
appendFileSync(TUI_DEBUG_LOG_PATH, JSON.stringify(entry) + "\n");
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
if (debugWriteFailureReported)
|
|
21
|
+
return;
|
|
22
|
+
debugWriteFailureReported = true;
|
|
23
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
24
|
+
process.stderr.write(`\n[nzb] failed to write TUI debug log: ${msg}\n`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=debug.js.map
|