@pixelbyte-software/pixcode 1.49.10 → 1.50.0

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 (28) hide show
  1. package/dist/assets/{index-DVpGrdpT.js → index-81sOpj25.js} +182 -182
  2. package/dist/assets/index-DMz0zv6T.css +32 -0
  3. package/dist/hermes-agent.png +0 -0
  4. package/dist/index.html +2 -2
  5. package/dist-server/server/index.js +77 -1
  6. package/dist-server/server/index.js.map +1 -1
  7. package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +40 -7
  8. package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +1 -1
  9. package/dist-server/server/services/hermes-gateway.js +115 -1
  10. package/dist-server/server/services/hermes-gateway.js.map +1 -1
  11. package/dist-server/server/services/hermes-install-jobs.js +9 -1
  12. package/dist-server/server/services/hermes-install-jobs.js.map +1 -1
  13. package/package.json +1 -1
  14. package/scripts/hermes/configure-pixcode-mcp.mjs +37 -1
  15. package/scripts/hermes/pixcode-mcp-server.mjs +166 -8
  16. package/scripts/smoke/hermes-api-install.mjs +4 -4
  17. package/scripts/smoke/hermes-gateway-persistence.mjs +104 -0
  18. package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +27 -0
  19. package/scripts/smoke/hermes-rest-chat-live.mjs +4 -0
  20. package/scripts/smoke/hermes-rest-codex-launch.mjs +11 -4
  21. package/scripts/smoke/hermes-rest-gateway.mjs +9 -1
  22. package/scripts/smoke/hermes-settings-commands.mjs +166 -0
  23. package/scripts/smoke/pixcode-workbench-1-48.mjs +13 -5
  24. package/server/index.js +94 -1
  25. package/server/modules/orchestration/hermes/hermes.routes.ts +45 -7
  26. package/server/services/hermes-gateway.js +128 -1
  27. package/server/services/hermes-install-jobs.js +11 -1
  28. package/dist/assets/index-BT6txdBK.css +0 -32
@@ -319,6 +319,30 @@ function resolvePublicBaseUrl(request) {
319
319
  const host = headers['x-forwarded-host'] || headers.host || `127.0.0.1:${process.env.SERVER_PORT || process.env.PORT || '3001'}`;
320
320
  return `${proto}://${String(host).split(',')[0].trim()}`;
321
321
  }
322
+ function resolveHermesMcpBaseUrl() {
323
+ const configured = process.env.PIXCODE_INTERNAL_BASE_URL || process.env.PIXCODE_HERMES_BASE_URL;
324
+ if (configured)
325
+ return configured.replace(/\/$/, '');
326
+ return `http://127.0.0.1:${process.env.SERVER_PORT || process.env.PORT || '3001'}`;
327
+ }
328
+ function quoteBashArg(value) {
329
+ return `'${String(value).replace(/'/g, "'\\''")}'`;
330
+ }
331
+ function quotePowerShellArg(value) {
332
+ return `"${String(value).replace(/`/g, '``').replace(/\$/g, '`$').replace(/"/g, '`"')}"`;
333
+ }
334
+ const HERMES_CLI_COMMAND_PATTERN = /^hermes(?:\s+[A-Za-z0-9._:/=@+-]+)*\s*$/;
335
+ function isHermesCliCommand(command) {
336
+ return typeof command === 'string' && HERMES_CLI_COMMAND_PATTERN.test(command.trim());
337
+ }
338
+ function buildHermesCliCommand(command) {
339
+ const hermesCommand = typeof command === 'string' && command.trim() ? command.trim() : 'hermes';
340
+ const configureScript = path.join(APP_ROOT, 'scripts', 'hermes', 'configure-pixcode-mcp.mjs');
341
+ if (os.platform() === 'win32') {
342
+ return `& ${quotePowerShellArg(process.execPath)} ${quotePowerShellArg(configureScript)} *> $null; ${hermesCommand}`;
343
+ }
344
+ return `${quoteBashArg(process.execPath)} ${quoteBashArg(configureScript)} >/dev/null 2>&1; exec ${hermesCommand}`;
345
+ }
322
346
  function getOrCreateHermesApiKey(userId) {
323
347
  if (!userId)
324
348
  return null;
@@ -407,6 +431,45 @@ app.post('/api/shell/sessions/terminate', authenticateToken, (req, res) => {
407
431
  const killedSessions = killProviderPtySessions(projectPath, provider);
408
432
  res.json({ success: true, killedSessions });
409
433
  });
434
+ app.get('/api/shell/sessions/provider-output', authenticateToken, (req, res) => {
435
+ const provider = String(req.query.provider || 'claude');
436
+ const projectPath = typeof req.query.projectPath === 'string' && req.query.projectPath.trim()
437
+ ? req.query.projectPath.trim()
438
+ : null;
439
+ const maxChars = Math.min(20000, Math.max(1000, Number.parseInt(String(req.query.maxChars || '12000'), 10) || 12000));
440
+ if (!SHELL_CLI_PROVIDERS.has(provider)) {
441
+ return res.status(400).json({ error: 'Unsupported provider' });
442
+ }
443
+ const requestedProjectPath = projectPath ? path.resolve(projectPath) : null;
444
+ let matchedSession = null;
445
+ for (const session of ptySessionsMap.values()) {
446
+ if (session?.provider === provider &&
447
+ !session?.isPlainShell &&
448
+ (!requestedProjectPath || path.resolve(session.projectPath || os.homedir()) === requestedProjectPath)) {
449
+ if (!matchedSession || (session.updatedAt || 0) > (matchedSession.updatedAt || 0)) {
450
+ matchedSession = session;
451
+ }
452
+ }
453
+ }
454
+ if (!matchedSession) {
455
+ return res.json({
456
+ active: false,
457
+ provider,
458
+ projectPath: requestedProjectPath,
459
+ output: '',
460
+ message: 'No active provider terminal session found for this project.',
461
+ });
462
+ }
463
+ const rawOutput = matchedSession.buffer.join('').slice(-maxChars);
464
+ res.json({
465
+ active: true,
466
+ provider,
467
+ projectPath: path.resolve(matchedSession.projectPath || os.homedir()),
468
+ sessionId: matchedSession.sessionId || null,
469
+ updatedAt: matchedSession.updatedAt || null,
470
+ output: stripAnsiSequences(rawOutput),
471
+ });
472
+ });
410
473
  // Authentication routes (public)
411
474
  app.use('/api/auth', authRoutes);
412
475
  // Projects API Routes (protected)
@@ -2012,10 +2075,14 @@ function handleShellConnection(ws, request) {
2012
2075
  const provider = data.provider || 'claude';
2013
2076
  const initialCommand = data.initialCommand;
2014
2077
  const isPlainShell = data.isPlainShell || (!!initialCommand && !hasSession) || provider === 'plain-shell';
2078
+ const isHermesCliLaunch = isPlainShell && isHermesCliCommand(initialCommand);
2015
2079
  const forceNewSession = Boolean(data.forceNewSession);
2016
2080
  const shellPermissionMode = normalizeShellPermissionMode(data.permissionMode);
2017
2081
  const shellSkipPermissions = Boolean(data.skipPermissions);
2018
2082
  const shellPermissionFlags = buildProviderShellPermissionFlags(provider, shellPermissionMode, shellSkipPermissions);
2083
+ const hermesApiKey = isHermesCliLaunch
2084
+ ? getOrCreateHermesApiKey(request.user?.id ?? request.user?.userId ?? null)
2085
+ : null;
2019
2086
  urlDetectionBuffer = '';
2020
2087
  announcedAuthUrls.clear();
2021
2088
  // Login commands should never reuse cached sessions — each login
@@ -2131,7 +2198,9 @@ function handleShellConnection(ws, request) {
2131
2198
  let shellCommand;
2132
2199
  if (isPlainShell) {
2133
2200
  // Plain shell mode without an initial command must stay interactive.
2134
- shellCommand = initialCommand || null;
2201
+ shellCommand = isHermesCliLaunch
2202
+ ? buildHermesCliCommand(initialCommand)
2203
+ : initialCommand || null;
2135
2204
  }
2136
2205
  else if (provider === 'cursor') {
2137
2206
  const command = buildProviderShellCommand('cursor-agent', shellPermissionFlags);
@@ -2257,6 +2326,11 @@ function handleShellConnection(ws, request) {
2257
2326
  TERM: 'xterm-256color',
2258
2327
  COLORTERM: 'truecolor',
2259
2328
  FORCE_COLOR: '3',
2329
+ ...(isHermesCliLaunch ? {
2330
+ PIXCODE_BASE_URL: resolveHermesMcpBaseUrl(),
2331
+ PIXCODE_API_KEY: hermesApiKey || '',
2332
+ PIXCODE_APP_ROOT: APP_ROOT,
2333
+ } : {}),
2260
2334
  });
2261
2335
  shellProcess = pty.spawn(shell, shellArgs, {
2262
2336
  name: 'xterm-256color',
@@ -2276,12 +2350,14 @@ function handleShellConnection(ws, request) {
2276
2350
  provider,
2277
2351
  isPlainShell,
2278
2352
  keepAliveUntilExit: false,
2353
+ updatedAt: Date.now(),
2279
2354
  });
2280
2355
  // Handle data output
2281
2356
  shellProcess.onData((data) => {
2282
2357
  const session = ptySessionsMap.get(ptySessionKey);
2283
2358
  if (!session)
2284
2359
  return;
2360
+ session.updatedAt = Date.now();
2285
2361
  if (session.buffer.length < 5000) {
2286
2362
  session.buffer.push(data);
2287
2363
  }