@iletai/nzb 1.6.4 → 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.
@@ -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/db.js";
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 = [
@@ -44,6 +44,7 @@ export function buildSettingsText(getUptimeStr) {
44
44
  `⏱ Worker Timeout: ${getTimeoutLabel()}\n` +
45
45
  `🤖 Model: ${config.copilotModel}\n` +
46
46
  `🧠 Thinking: ${config.thinkingLevel}\n` +
47
+ `💡 Reasoning: ${config.reasoningEffort}\n` +
47
48
  `📝 Verbose: ${config.verboseMode ? "✅ ON" : "❌ OFF"}\n` +
48
49
  `📊 Usage: ${config.usageMode}\n` +
49
50
  `🔧 Show Reasoning: ${config.showReasoning ? "✅ ON" : "❌ OFF"}\n\n` +
@@ -76,141 +77,296 @@ export function createMenus(getUptimeStr) {
76
77
  // Settings sub-menu
77
78
  const settingsMenu = new Menu("settings-menu")
78
79
  .text(() => `⏱ Timeout: ${getTimeoutLabel()}`, async (ctx) => {
79
- const idx = TIMEOUT_PRESETS.findIndex((p) => p.ms === config.workerTimeoutMs);
80
- const next = TIMEOUT_PRESETS[(idx + 1) % TIMEOUT_PRESETS.length];
81
- config.workerTimeoutMs = next.ms;
82
- persistEnvVar("WORKER_TIMEOUT", String(next.ms));
83
- ctx.menu.update();
84
- await ctx.editMessageText(buildSettingsText(getUptimeStr));
85
- await ctx.answerCallbackQuery(`Timeout → ${next.label}`);
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
+ }
86
96
  })
87
97
  .row()
88
98
  .text(() => `🤖 ${config.copilotModel}`, async (ctx) => {
89
- const models = await getAvailableModels();
90
- if (models.length === 0) {
91
- await ctx.answerCallbackQuery("No models available");
92
- return;
93
- }
94
- const idx = models.indexOf(config.copilotModel);
95
- const next = models[(idx + 1) % models.length];
96
- config.copilotModel = next;
97
- persistModel(next);
98
- ctx.menu.update();
99
- await ctx.editMessageText(buildSettingsText(getUptimeStr));
100
- await ctx.answerCallbackQuery(`Model → ${next}`);
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
+ }
101
120
  })
102
121
  .row()
103
122
  .text(() => `${config.showReasoning ? "✅" : "❌"} Show Reasoning`, async (ctx) => {
104
- config.showReasoning = !config.showReasoning;
105
- persistEnvVar("SHOW_REASONING", config.showReasoning ? "true" : "false");
106
- ctx.menu.update();
107
- await ctx.editMessageText(buildSettingsText(getUptimeStr));
108
- await ctx.answerCallbackQuery(`Reasoning ${config.showReasoning ? "ON" : "OFF"}`);
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
+ }
109
137
  })
110
138
  .row()
111
139
  .text(() => `🧠 Think: ${config.thinkingLevel}`, async (ctx) => {
112
- const levels = ["off", "low", "medium", "high"];
113
- const idx = levels.indexOf(config.thinkingLevel);
114
- const next = levels[(idx + 1) % levels.length];
115
- config.thinkingLevel = next;
116
- persistEnvVar("THINKING_LEVEL", next);
117
- ctx.menu.update();
118
- await ctx.editMessageText(buildSettingsText(getUptimeStr));
119
- await ctx.answerCallbackQuery(`Thinking → ${next}`);
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
+ }
120
157
  })
121
158
  .text(() => `📝 ${config.verboseMode ? "Verbose" : "Concise"}`, async (ctx) => {
122
- config.verboseMode = !config.verboseMode;
123
- persistEnvVar("VERBOSE_MODE", config.verboseMode ? "true" : "false");
124
- ctx.menu.update();
125
- await ctx.editMessageText(buildSettingsText(getUptimeStr));
126
- await ctx.answerCallbackQuery(`Verbose ${config.verboseMode ? "ON" : "OFF"}`);
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
+ }
173
+ })
174
+ .row()
175
+ .text(() => `💡 Reasoning: ${config.reasoningEffort}`, async (ctx) => {
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
+ }
127
193
  })
128
194
  .row()
129
195
  .text(() => `📊 Usage: ${config.usageMode}`, async (ctx) => {
130
- const modes = ["off", "tokens", "full"];
131
- const idx = modes.indexOf(config.usageMode);
132
- const next = modes[(idx + 1) % modes.length];
133
- config.usageMode = next;
134
- persistEnvVar("USAGE_MODE", next);
135
- ctx.menu.update();
136
- await ctx.editMessageText(buildSettingsText(getUptimeStr));
137
- await ctx.answerCallbackQuery(`Usage → ${next}`);
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
+ }
138
213
  })
139
214
  .row()
140
215
  .text(() => `📌 v${process.env.npm_package_version || "?"} · uptime ${getUptimeStr()}`, async (ctx) => {
141
- await ctx.answerCallbackQuery(`Uptime: ${getUptimeStr()}`);
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
+ }
142
226
  })
143
227
  .row()
144
228
  .back("🔙 Back", async (ctx) => {
145
- await ctx.editMessageText("NZB Menu:");
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
+ }
146
239
  });
147
240
  // Main interactive menu with navigation
148
241
  const mainMenu = new Menu("main-menu")
149
242
  .text("📊 Status", async (ctx) => {
150
- const workers = Array.from(getWorkers().values());
151
- const lines = [
152
- "📊 NZB Status",
153
- `Model: ${config.copilotModel}`,
154
- `Thinking: ${config.thinkingLevel}`,
155
- `Verbose: ${config.verboseMode ? "on" : "off"}`,
156
- `Usage: ${config.usageMode}`,
157
- `Uptime: ${getUptimeStr()}`,
158
- `Workers: ${workers.length} active`,
159
- `Queue: ${getQueueSize()} pending`,
160
- ];
161
- await ctx.answerCallbackQuery();
162
- await ctx.reply(lines.join("\n"));
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
+ }
163
265
  })
164
266
  .text("🤖 Model", async (ctx) => {
165
- await ctx.answerCallbackQuery();
166
- await ctx.reply(`Current model: ${config.copilotModel}`);
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
+ }
167
278
  })
168
279
  .row()
169
280
  .text("👥 Workers", async (ctx) => {
170
- await ctx.answerCallbackQuery();
171
- const workers = Array.from(getWorkers().values());
172
- if (workers.length === 0) {
173
- await ctx.reply("No active worker sessions.");
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
+ }
174
291
  }
175
- else {
176
- const lines = workers.map((w) => `• ${w.name} (${w.workingDir}) ${w.status}`);
177
- await ctx.reply(truncateForTelegram(lines.join("\n")));
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(() => { });
178
298
  }
179
299
  })
180
300
  .text("🧠 Skills", async (ctx) => {
181
- await ctx.answerCallbackQuery();
182
- const skills = listSkills();
183
- if (skills.length === 0) {
184
- await ctx.reply("No skills installed.");
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
+ }
185
311
  }
186
- else {
187
- const lines = skills.map((s) => `• ${s.name} (${s.source}) ${s.description}`);
188
- await ctx.reply(truncateForTelegram(lines.join("\n")));
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(() => { });
189
318
  }
190
319
  })
191
320
  .row()
192
321
  .text("🗂 Memory", async (ctx) => {
193
- await ctx.answerCallbackQuery();
194
- const memories = searchMemories(undefined, undefined, 50);
195
- if (memories.length === 0) {
196
- await ctx.reply("No memories stored.");
197
- }
198
- else {
199
- const formatted = formatMemoryList(memories);
200
- const chunks = chunkMessage(formatted);
201
- for (const chunk of chunks) {
202
- 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.");
203
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(() => { });
204
342
  }
205
343
  })
206
344
  .submenu("⚙️ Settings", "settings-menu", async (ctx) => {
207
- await ctx.editMessageText(buildSettingsText(getUptimeStr));
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
+ }
208
355
  })
209
356
  .row()
210
357
  .text("❌ Cancel", async (ctx) => {
211
- await ctx.answerCallbackQuery();
212
- const cancelled = await cancelCurrentMessage();
213
- await ctx.reply(cancelled ? "Cancelled." : "Nothing to cancel.");
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
+ }
214
370
  });
215
371
  // Register sub-menu as child
216
372
  mainMenu.register(settingsMenu);
@@ -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