@nexus-cortex/cli 4.36.1 → 4.36.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.
Files changed (2) hide show
  1. package/bin/cortex.js +33 -17
  2. package/package.json +1 -1
package/bin/cortex.js CHANGED
@@ -25,7 +25,8 @@
25
25
  import { spawn, spawnSync } from 'child_process';
26
26
  import { fileURLToPath } from 'url';
27
27
  import { dirname, join, resolve } from 'path';
28
- import { existsSync, readFileSync, realpathSync } from 'fs';
28
+ import { existsSync, readFileSync, realpathSync, mkdirSync, openSync } from 'fs';
29
+ import { homedir } from 'os';
29
30
  import { createRequire } from 'module';
30
31
 
31
32
  const __cortex_require = createRequire(import.meta.url);
@@ -299,8 +300,30 @@ async function startServer() {
299
300
  if (idleTimeoutFlag) {
300
301
  serverArgs.push('--idle-timeout', idleTimeoutFlag);
301
302
  }
303
+ // Send the detached server's stdout+stderr to a LOG FILE, not a pipe.
304
+ //
305
+ // Previously stdio was ['ignore','pipe','pipe']. The client read-ends of those
306
+ // pipes close when this short-lived one-shot client EXITS after its command —
307
+ // so the NEXT command makes the still-running detached server write to a broken
308
+ // pipe (EPIPE), which disrupts it and the client's fetch dies with a bare
309
+ // "fetch failed". `browse` writes the most server output (its sub-agent), so it
310
+ // was the reliable trigger; a quiet command like 2+2 often slipped through,
311
+ // which made the failure look intermittent.
312
+ //
313
+ // A file sink has no reader to disappear, so the server never hits EPIPE — and
314
+ // it gives users/containers a real server log to inspect. Readiness is detected
315
+ // via /health polling below, so we never needed to parse the server's stdout.
316
+ // 'w' truncates per fresh server spawn, bounding growth to one server lifetime.
317
+ const logPath = getServerLogPath();
318
+ let logFd;
319
+ try {
320
+ mkdirSync(dirname(logPath), { recursive: true });
321
+ logFd = openSync(logPath, 'w');
322
+ } catch {
323
+ logFd = 'ignore'; // last resort: never fall back to an undrained pipe
324
+ }
302
325
  serverProcess = spawn('node', serverArgs, {
303
- stdio: ['ignore', 'pipe', 'pipe'],
326
+ stdio: ['ignore', logFd, logFd],
304
327
  // Run the server in — and treat as project root — the directory cortex was invoked
305
328
  // from, so `cd foo && cortex "..."` operates on `foo` (not the home dir or a stale
306
329
  // PROJECT_PATH from the global ~/.cortex/.env). An explicitly-exported PROJECT_PATH /
@@ -316,32 +339,25 @@ async function startServer() {
316
339
  });
317
340
  serverProcess.unref();
318
341
 
319
- serverProcess.stderr.on('data', (chunk) => {
320
- const line = chunk.toString();
321
- if (line.includes('[ERROR]') || line.includes('Failed to start')) {
322
- process.stderr.write(line);
323
- }
324
- });
325
-
326
342
  // Wait for healthy (up to 15s)
327
343
  for (let i = 0; i < 30; i++) {
328
344
  await new Promise(r => setTimeout(r, 500));
329
345
  if (await isServerUp()) {
330
346
  process.stderr.write(`[cortex] Server ready on ${BASE_URL}\n`);
331
- // The server is detached + unref'd so it persists on its own. Its piped
332
- // stdio handles, however, keep THIS short-lived client's event loop alive
333
- // — so after a one-shot the client would hang until killed. Unref the
334
- // pipes so the client can exit cleanly once its request completes while
335
- // the background server keeps running.
336
- serverProcess.stdout?.unref?.();
337
- serverProcess.stderr?.unref?.();
338
347
  return;
339
348
  }
340
349
  }
341
- console.error('[cortex] Server failed to start within 15s');
350
+ console.error(`[cortex] Server failed to start within 15s (see ${logPath})`);
342
351
  process.exit(1);
343
352
  }
344
353
 
354
+ // Where the auto-started background server logs to. Honors CORTEX_HOME/HOME so
355
+ // it lands beside the rest of the global config (~/.cortex/server.log).
356
+ function getServerLogPath() {
357
+ const base = process.env.CORTEX_HOME || homedir() || process.cwd();
358
+ return join(base, '.cortex', 'server.log');
359
+ }
360
+
345
361
  async function ensureServer() {
346
362
  if (!(await isServerUp())) {
347
363
  await startServer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexus-cortex/cli",
3
- "version": "4.36.1",
3
+ "version": "4.36.2",
4
4
  "description": "Nexus Cortex CLI - Terminal interface for multi-provider LLM orchestration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",