@pixelbyte-software/pixcode 1.49.3 → 1.49.5

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.
@@ -77,6 +77,7 @@ import { restoreBotFromConfig } from './services/telegram/bot.js';
77
77
  import { ensurePortOpen } from './utils/port-access.js';
78
78
  import { applyAllStoredCredentialsToEnv, } from './services/provider-credentials.js';
79
79
  import { primeCliBinPath } from './services/install-jobs.js';
80
+ import { buildHermesPathEnv, primeHermesPath } from './services/hermes-install-jobs.js';
80
81
  import { startEnabledPluginServers, stopAllPlugins, getPluginPort } from './utils/plugin-process-manager.js';
81
82
  import { initializeDatabase, sessionNamesDb, applyCustomSessionNames, apiKeysDb } from './database/db.js';
82
83
  import { setNotificationWebSocketServer } from './services/notification-orchestrator.js';
@@ -348,7 +349,6 @@ function buildHermesShellCommand(kind, env) {
348
349
  const configureScript = path.join(APP_ROOT, 'scripts', 'hermes', 'configure-pixcode-mcp.mjs');
349
350
  const isWindows = os.platform() === 'win32';
350
351
  const quote = isWindows ? shellQuotePowerShell : shellQuotePosix;
351
- const configure = `node ${quote(configureScript)}`;
352
352
  if (isWindows) {
353
353
  const setEnv = [
354
354
  `$env:PIXCODE_BASE_URL=${quote(env.PIXCODE_BASE_URL)}`,
@@ -385,10 +385,16 @@ function buildHermesShellCommand(kind, env) {
385
385
  'return $null;',
386
386
  '}',
387
387
  ].join(' ');
388
+ const configure = [
389
+ 'function Invoke-PixcodeHermesConfigure {',
390
+ `& node ${quote(configureScript)};`,
391
+ 'if ($LASTEXITCODE -ne 0) { Write-Warning "Pixcode MCP configure failed; starting Hermes anyway."; $global:LASTEXITCODE = 0; }',
392
+ '}',
393
+ ].join(' ');
388
394
  const installHermesIfMissing = [
389
395
  'function Install-HermesIfMissing {',
390
396
  '$script:HermesCmd = Resolve-HermesCommand;',
391
- 'if ($script:HermesCmd) { Write-Host "Hermes already installed:"; & $script:HermesCmd --version; return; }',
397
+ 'if ($script:HermesCmd) { & $script:HermesCmd --version *> $null; return; }',
392
398
  '$installer = Join-Path $env:TEMP "pixcode-hermes-install.ps1";',
393
399
  'Invoke-WebRequest -Uri "https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1" -UseBasicParsing -OutFile $installer;',
394
400
  '& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $installer -SkipSetup -Branch main;',
@@ -399,9 +405,9 @@ function buildHermesShellCommand(kind, env) {
399
405
  '}',
400
406
  ].join(' ');
401
407
  if (kind === 'pixcode:hermes:install') {
402
- return `${setEnv}; ${resolveHermesCommand}; ${installHermesIfMissing}; Install-HermesIfMissing; ${configure}`;
408
+ return `${setEnv}; ${resolveHermesCommand}; ${configure}; ${installHermesIfMissing}; Install-HermesIfMissing; Invoke-PixcodeHermesConfigure`;
403
409
  }
404
- return `${setEnv}; ${resolveHermesCommand}; ${installHermesIfMissing}; Install-HermesIfMissing; ${configure}; & $script:HermesCmd chat --toolsets "hermes-cli,mcp-pixcode"`;
410
+ return `${setEnv}; ${resolveHermesCommand}; ${configure}; ${installHermesIfMissing}; Install-HermesIfMissing; Invoke-PixcodeHermesConfigure; & $script:HermesCmd`;
405
411
  }
406
412
  const setEnv = [
407
413
  `PIXCODE_BASE_URL=${quote(env.PIXCODE_BASE_URL)}`,
@@ -424,15 +430,15 @@ function buildHermesShellCommand(kind, env) {
424
430
  const installHermesIfMissing = [
425
431
  'installHermesIfMissing() {',
426
432
  'HERMES_CMD="$(resolveHermesCommand 2>/dev/null || true)";',
427
- 'if [ -n "$HERMES_CMD" ]; then echo "Hermes already installed:"; "$HERMES_CMD" --version 2>/dev/null || true; return 0; fi;',
433
+ 'if [ -n "$HERMES_CMD" ]; then "$HERMES_CMD" --version >/dev/null 2>&1 || true; return 0; fi;',
428
434
  'echo "Hermes is not installed. Use Pixcode Settings > Hermes Agent > Install or repair, then start again." >&2;',
429
435
  'exit 127;',
430
436
  '}',
431
437
  ].join(' ');
432
438
  if (kind === 'pixcode:hermes:install') {
433
- return `${setEnv} sh -lc ${quote(`${resolveHermesCommand} ${installHermesIfMissing} installHermesIfMissing && node ${shellQuotePosix(configureScript)}`)}`;
439
+ return `${setEnv} sh -lc ${quote(`${resolveHermesCommand} ${installHermesIfMissing} installHermesIfMissing && { node ${shellQuotePosix(configureScript)} || echo "Pixcode MCP configure failed; continuing."; }`)}`;
434
440
  }
435
- return `${setEnv} sh -lc ${quote(`${resolveHermesCommand} ${installHermesIfMissing} installHermesIfMissing && node ${shellQuotePosix(configureScript)} && "$HERMES_CMD" chat --toolsets "hermes-cli,mcp-pixcode"`)}`;
441
+ return `${setEnv} sh -lc ${quote(`${resolveHermesCommand} ${installHermesIfMissing} installHermesIfMissing && { node ${shellQuotePosix(configureScript)} || echo "Pixcode MCP configure failed; starting Hermes anyway."; } && "$HERMES_CMD"`)}`;
436
442
  }
437
443
  // Single WebSocket server that handles both paths
438
444
  const wss = new WebSocketServer({
@@ -2110,6 +2116,7 @@ function handleShellConnection(ws, request) {
2110
2116
  const provider = data.provider || 'claude';
2111
2117
  let initialCommand = data.initialCommand;
2112
2118
  const hermesCommand = HERMES_SHELL_COMMANDS.has(initialCommand) ? initialCommand : null;
2119
+ const isHermesShellSession = Boolean(hermesCommand);
2113
2120
  if (hermesCommand) {
2114
2121
  const apiKey = getOrCreateHermesApiKey(request?.user?.id);
2115
2122
  if (!apiKey) {
@@ -2142,7 +2149,7 @@ function handleShellConnection(ws, request) {
2142
2149
  initialCommand.includes('auth login'));
2143
2150
  // Include command hash in session key so different commands get separate sessions
2144
2151
  const commandSuffix = isPlainShell && initialCommand
2145
- ? `_cmd_${Buffer.from(initialCommand).toString('base64').slice(0, 16)}`
2152
+ ? (isHermesShellSession ? '_hermes' : `_cmd_${Buffer.from(initialCommand).toString('base64').slice(0, 16)}`)
2146
2153
  : '';
2147
2154
  // Include provider in the key so a fresh "new session" in OpenCode
2148
2155
  // doesn't reattach to a cached Claude PTY for the same project (or
@@ -2364,17 +2371,17 @@ function handleShellConnection(ws, request) {
2364
2371
  const termCols = data.cols || 80;
2365
2372
  const termRows = data.rows || 24;
2366
2373
  console.log('📐 Using terminal dimensions:', termCols, 'x', termRows);
2374
+ const shellEnv = buildHermesPathEnv(process.env, {
2375
+ TERM: 'xterm-256color',
2376
+ COLORTERM: 'truecolor',
2377
+ FORCE_COLOR: '3',
2378
+ });
2367
2379
  shellProcess = pty.spawn(shell, shellArgs, {
2368
2380
  name: 'xterm-256color',
2369
2381
  cols: termCols,
2370
2382
  rows: termRows,
2371
2383
  cwd: resolvedProjectPath,
2372
- env: {
2373
- ...process.env,
2374
- TERM: 'xterm-256color',
2375
- COLORTERM: 'truecolor',
2376
- FORCE_COLOR: '3'
2377
- }
2384
+ env: shellEnv,
2378
2385
  });
2379
2386
  console.log('🟢 Shell process started with PTY, PID:', shellProcess.pid);
2380
2387
  ptySessionsMap.set(ptySessionKey, {
@@ -2385,7 +2392,8 @@ function handleShellConnection(ws, request) {
2385
2392
  projectPath,
2386
2393
  sessionId,
2387
2394
  provider,
2388
- isPlainShell
2395
+ isPlainShell,
2396
+ keepAliveUntilExit: isHermesShellSession,
2389
2397
  });
2390
2398
  // Handle data output
2391
2399
  shellProcess.onData((data) => {
@@ -2497,6 +2505,11 @@ function handleShellConnection(ws, request) {
2497
2505
  if (ptySessionKey) {
2498
2506
  const session = ptySessionsMap.get(ptySessionKey);
2499
2507
  if (session) {
2508
+ if (session.keepAliveUntilExit) {
2509
+ console.log('⏳ PTY session kept alive until process exit:', ptySessionKey);
2510
+ session.ws = null;
2511
+ return;
2512
+ }
2500
2513
  console.log('⏳ PTY session kept alive, will timeout in 30 minutes:', ptySessionKey);
2501
2514
  session.ws = null;
2502
2515
  session.timeoutId = setTimeout(() => {
@@ -3104,6 +3117,15 @@ async function startServer() {
3104
3117
  catch (err) {
3105
3118
  console.warn('[install-jobs] Failed to prime CLI bin path:', err?.message || err);
3106
3119
  }
3120
+ // Prime Hermes' known install locations separately so the project
3121
+ // terminal can resolve `hermes` even when Windows has not refreshed the
3122
+ // user's PATH for the current Pixcode process.
3123
+ try {
3124
+ primeHermesPath();
3125
+ }
3126
+ catch (err) {
3127
+ console.warn('[install-jobs] Failed to prime Hermes bin path:', err?.message || err);
3128
+ }
3107
3129
  // Restore any previously-configured Telegram bot. This is best-effort:
3108
3130
  // a bad token or network blip should warn, not crash the server.
3109
3131
  restoreBotFromConfig().catch((err) => {