@iletai/nzb 1.1.0 → 1.1.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.
@@ -28,7 +28,7 @@ const app = express();
28
28
  app.use(express.json());
29
29
  // Bearer token authentication middleware (skip /status health check)
30
30
  app.use((req, res, next) => {
31
- if (!apiToken || req.path === "/status" || req.path === "/send-photo")
31
+ if (!apiToken || req.path === "/status")
32
32
  return next();
33
33
  const auth = req.headers.authorization;
34
34
  if (!auth || auth !== `Bearer ${apiToken}`) {
package/dist/config.js CHANGED
@@ -11,8 +11,14 @@ const configSchema = z.object({
11
11
  API_PORT: z.string().optional(),
12
12
  COPILOT_MODEL: z.string().optional(),
13
13
  WORKER_TIMEOUT: z.string().optional(),
14
+ NODE_EXTRA_CA_CERTS: z.string().optional(),
14
15
  });
15
16
  const raw = configSchema.parse(process.env);
17
+ // Apply NODE_EXTRA_CA_CERTS from .env if not already set via environment.
18
+ // This allows corporate users to configure their CA bundle path in ~/.nzb/.env.
19
+ if (raw.NODE_EXTRA_CA_CERTS && !process.env.NODE_EXTRA_CA_CERTS) {
20
+ process.env.NODE_EXTRA_CA_CERTS = raw.NODE_EXTRA_CA_CERTS;
21
+ }
16
22
  const parsedUserId = raw.AUTHORIZED_USER_ID ? parseInt(raw.AUTHORIZED_USER_ID, 10) : undefined;
17
23
  const parsedPort = parseInt(raw.API_PORT || "7777", 10);
18
24
  if (parsedUserId !== undefined && (Number.isNaN(parsedUserId) || parsedUserId <= 0)) {
@@ -7,8 +7,8 @@ import { loadMcpConfig } from "./mcp-config.js";
7
7
  import { getSkillDirectories } from "./skills.js";
8
8
  import { getOrchestratorSystemMessage } from "./system-message.js";
9
9
  import { createTools } from "./tools.js";
10
- const MAX_RETRIES = 3;
11
- const RECONNECT_DELAYS_MS = [1_000, 3_000, 10_000];
10
+ const MAX_RETRIES = 2;
11
+ const RECONNECT_DELAYS_MS = [1_000, 5_000];
12
12
  const HEALTH_CHECK_INTERVAL_MS = 30_000;
13
13
  const ORCHESTRATOR_SESSION_KEY = "orchestrator_session_id";
14
14
  let logMessage = () => { };
@@ -242,7 +242,7 @@ async function executeOnSession(prompt, callback, onToolEvent) {
242
242
  callback(accumulated, false);
243
243
  });
244
244
  try {
245
- const result = await session.sendAndWait({ prompt }, 300_000);
245
+ const result = await session.sendAndWait({ prompt }, 120_000);
246
246
  const finalContent = result?.data?.content || accumulated || "(No response)";
247
247
  return finalContent;
248
248
  }
package/dist/daemon.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "child_process";
2
+ import { existsSync } from "fs";
2
3
  import { broadcastToSSE, startApiServer } from "./api/server.js";
3
4
  import { config } from "./config.js";
4
5
  import { getClient, stopClient } from "./copilot/client.js";
@@ -6,6 +7,21 @@ import { getWorkers, initOrchestrator, setMessageLogger, setProactiveNotify } fr
6
7
  import { closeDb, getDb } from "./store/db.js";
7
8
  import { createBot, sendProactiveMessage, startBot, stopBot } from "./telegram/bot.js";
8
9
  import { checkForUpdate } from "./update.js";
10
+ // Auto-detect system CA bundle for corporate environments with TLS inspection.
11
+ // NODE_EXTRA_CA_CERTS must be set before any TLS connection is made.
12
+ // Users can override via NODE_EXTRA_CA_CERTS env var or NODE_EXTRA_CA_CERTS in ~/.nzb/.env.
13
+ if (!process.env.NODE_EXTRA_CA_CERTS) {
14
+ const knownCaBundles = [
15
+ "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu
16
+ "/etc/pki/tls/certs/ca-bundle.crt", // RHEL/CentOS/Fedora
17
+ "/etc/ssl/cert.pem", // macOS / Alpine
18
+ ];
19
+ const found = knownCaBundles.find((p) => existsSync(p));
20
+ if (found) {
21
+ process.env.NODE_EXTRA_CA_CERTS = found;
22
+ console.log(`[nzb] Auto-detected system CA bundle: ${found}`);
23
+ }
24
+ }
9
25
  function truncate(text, max = 200) {
10
26
  const oneLine = text.replace(/\n/g, " ").trim();
11
27
  return oneLine.length > max ? oneLine.slice(0, max) + "…" : oneLine;
@@ -60,11 +76,15 @@ async function main() {
60
76
  console.log("[nzb] Telegram user ID missing — skipping bot. Run 'nzb setup' and enter your Telegram user ID (get it from @userinfobot).");
61
77
  }
62
78
  console.log("[nzb] NZB is fully operational.");
63
- // Non-blocking update check
79
+ // Non-blocking update check — notify via console + all active channels
64
80
  checkForUpdate()
65
81
  .then(({ updateAvailable, current, latest }) => {
66
82
  if (updateAvailable) {
67
- console.log(`[nzb] Update available: v${current} → v${latest} run 'nzb update' to install`);
83
+ const msg = `⬆ Update available: v${current} → v${latest} run \`nzb update\` to install`;
84
+ console.log(`[nzb] ${msg}`);
85
+ if (config.telegramEnabled)
86
+ sendProactiveMessage(msg).catch(() => { });
87
+ broadcastToSSE(msg);
68
88
  }
69
89
  })
70
90
  .catch(() => { }); // silent — network may be unavailable
@@ -1,4 +1,5 @@
1
1
  import { Bot } from "grammy";
2
+ import { Agent as HttpsAgent } from "https";
2
3
  import { config, persistModel } from "../config.js";
3
4
  import { cancelCurrentMessage, getQueueSize, getWorkers, sendToOrchestrator } from "../copilot/orchestrator.js";
4
5
  import { listSkills } from "../copilot/skills.js";
@@ -7,6 +8,10 @@ import { searchMemories } from "../store/db.js";
7
8
  import { chunkMessage, toTelegramMarkdown } from "./formatter.js";
8
9
  let bot;
9
10
  const startedAt = Date.now();
11
+ // Direct-connection HTTPS agent for Telegram API requests.
12
+ // This bypasses corporate proxy (HTTP_PROXY/HTTPS_PROXY env vars) without
13
+ // modifying process.env, so other services (Copilot SDK, MCP, npm) are unaffected.
14
+ const telegramAgent = new HttpsAgent({ keepAlive: true });
10
15
  export function createBot() {
11
16
  if (!config.telegramBotToken) {
12
17
  throw new Error("Telegram bot token is missing. Run 'nzb setup' and enter the bot token from @BotFather.");
@@ -14,11 +19,20 @@ export function createBot() {
14
19
  if (config.authorizedUserId === undefined) {
15
20
  throw new Error("Telegram user ID is missing. Run 'nzb setup' and enter your Telegram user ID (get it from @userinfobot).");
16
21
  }
17
- bot = new Bot(config.telegramBotToken);
22
+ bot = new Bot(config.telegramBotToken, {
23
+ client: {
24
+ baseFetchConfig: {
25
+ agent: telegramAgent,
26
+ compress: true,
27
+ },
28
+ },
29
+ });
30
+ console.log("[nzb] Telegram bot using direct HTTPS agent (proxy bypass)");
18
31
  // Auth middleware — only allow the authorized user
19
32
  bot.use(async (ctx, next) => {
20
33
  if (config.authorizedUserId !== undefined && ctx.from?.id !== config.authorizedUserId) {
21
- return; // Silently ignore unauthorized users
34
+ console.log(`[nzb] Telegram auth rejected: user ${ctx.from?.id} (authorized: ${config.authorizedUserId})`);
35
+ return;
22
36
  }
23
37
  await next();
24
38
  });
@@ -171,7 +185,8 @@ export function createBot() {
171
185
  const enqueueEdit = (text) => {
172
186
  if (finalized || text === lastEditedText)
173
187
  return;
174
- editChain = editChain.then(async () => {
188
+ editChain = editChain
189
+ .then(async () => {
175
190
  if (finalized || text === lastEditedText)
176
191
  return;
177
192
  if (!placeholderMsgId) {
@@ -199,7 +214,8 @@ export function createBot() {
199
214
  }
200
215
  }
201
216
  lastEditedText = text;
202
- }).catch(() => { });
217
+ })
218
+ .catch(() => { });
203
219
  };
204
220
  const onToolEvent = (event) => {
205
221
  if (event.type === "tool_start") {
@@ -231,7 +247,9 @@ export function createBot() {
231
247
  await bot.api.editMessageText(chatId, placeholderMsgId, fallbackChunks[0]);
232
248
  return;
233
249
  }
234
- catch { /* fall through to send new messages */ }
250
+ catch {
251
+ /* fall through to send new messages */
252
+ }
235
253
  }
236
254
  }
237
255
  // Multi-chunk or no placeholder: delete placeholder and send chunks
@@ -239,7 +257,9 @@ export function createBot() {
239
257
  try {
240
258
  await bot.api.deleteMessage(chatId, placeholderMsgId);
241
259
  }
242
- catch { /* ignore */ }
260
+ catch {
261
+ /* ignore */
262
+ }
243
263
  }
244
264
  const sendChunk = async (chunk, fallback, isFirst) => {
245
265
  const opts = isFirst
@@ -260,7 +280,9 @@ export function createBot() {
260
280
  await ctx.reply(fallbackChunks[i], i === 0 ? { reply_parameters: replyParams } : {});
261
281
  }
262
282
  }
263
- catch { /* nothing more we can do */ }
283
+ catch {
284
+ /* nothing more we can do */
285
+ }
264
286
  }
265
287
  });
266
288
  }
package/dist/update.js CHANGED
@@ -1,17 +1,21 @@
1
+ import { exec as execCb, execSync } from "child_process";
1
2
  import { readFileSync } from "fs";
2
- import { join, dirname } from "path";
3
+ import { dirname, join } from "path";
3
4
  import { fileURLToPath } from "url";
4
- import { exec as execCb, execSync } from "child_process";
5
5
  const __dirname = dirname(fileURLToPath(import.meta.url));
6
- function getLocalVersion() {
6
+ const PKG_NAME = "@iletai/nzb";
7
+ function getPackageJson() {
7
8
  try {
8
9
  const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
9
- return pkg.version || "0.0.0";
10
+ return { name: pkg.name || PKG_NAME, version: pkg.version || "0.0.0" };
10
11
  }
11
12
  catch {
12
- return "0.0.0";
13
+ return { name: PKG_NAME, version: "0.0.0" };
13
14
  }
14
15
  }
16
+ function getLocalVersion() {
17
+ return getPackageJson().version;
18
+ }
15
19
  /** Run a command asynchronously and return stdout. */
16
20
  function execAsync(cmd, timeoutMs) {
17
21
  return new Promise((resolve, reject) => {
@@ -25,7 +29,8 @@ function execAsync(cmd, timeoutMs) {
25
29
  /** Fetch the latest published version from npm. Returns null on failure. */
26
30
  export async function getLatestVersion() {
27
31
  try {
28
- const result = await execAsync("npm view nzb version", 10_000);
32
+ const { name } = getPackageJson();
33
+ const result = await execAsync(`npm view ${name} version`, 10_000);
29
34
  return result || null;
30
35
  }
31
36
  catch {
@@ -54,10 +59,11 @@ export async function checkForUpdate() {
54
59
  checkSucceeded: latest !== null,
55
60
  };
56
61
  }
57
- /** Run `npm install -g nzb@latest` and return success/failure. */
62
+ /** Run `npm install -g <pkg>@latest` and return success/failure. */
58
63
  export async function performUpdate() {
59
64
  try {
60
- const output = execSync("npm install -g nzb@latest", {
65
+ const { name } = getPackageJson();
66
+ const output = execSync(`npm install -g ${name}@latest`, {
61
67
  encoding: "utf-8",
62
68
  timeout: 60_000,
63
69
  stdio: ["ignore", "pipe", "pipe"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iletai/nzb",
3
- "version": "1.1.0",
3
+ "version": "1.1.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"