@pixelbyte-software/pixcode 1.53.2 → 1.53.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixelbyte-software/pixcode",
3
- "version": "1.53.2",
3
+ "version": "1.53.3",
4
4
  "description": "Self-hosted AI coding agent control room for Claude Code, Cursor CLI, OpenAI Codex, Gemini CLI, Qwen Code, and OpenCode with chat, files, shell, Git, orchestration, API keys, Telegram, MCP, plugins, themes, and desktop/server deployment.",
5
5
  "type": "module",
6
6
  "main": "dist-server/server/index.js",
package/server/index.js CHANGED
@@ -273,6 +273,59 @@ function getActiveUpdateJob() {
273
273
  return null;
274
274
  }
275
275
 
276
+ function countByProvider(items) {
277
+ return items.reduce((counts, item) => {
278
+ const provider = item?.provider || 'unknown';
279
+ counts[provider] = (counts[provider] || 0) + 1;
280
+ return counts;
281
+ }, {});
282
+ }
283
+
284
+ function getActiveProviderWorkSummary() {
285
+ const sessions = [
286
+ ...getActiveClaudeSDKSessions().map((id) => ({ id, provider: 'claude' })),
287
+ ...getActiveCursorSessions().map((session) => ({ ...session, provider: 'cursor' })),
288
+ ...getActiveCodexSessions().map((session) => ({ ...session, provider: 'codex' })),
289
+ ...getActiveGeminiSessions().map((session) => ({ ...session, provider: 'gemini' })),
290
+ ...getActiveQwenSessions().map((session) => ({ ...session, provider: 'qwen' })),
291
+ ...getActiveOpencodeSessions().map((session) => ({ ...session, provider: 'opencode' })),
292
+ ];
293
+
294
+ return {
295
+ total: sessions.length,
296
+ byProvider: countByProvider(sessions),
297
+ };
298
+ }
299
+
300
+ function getActivePtyWorkSummary() {
301
+ const activePtys = Array.from(ptySessionsMap.values())
302
+ .filter((session) => session?.pty && session.lifecycleState === 'running')
303
+ .map((session) => ({
304
+ provider: session.isPlainShell ? 'plain-shell' : (session.provider || 'unknown'),
305
+ connected: Boolean(session.ws && session.ws.readyState === WebSocket.OPEN),
306
+ }));
307
+
308
+ return {
309
+ total: activePtys.length,
310
+ connected: activePtys.filter((session) => session.connected).length,
311
+ detached: activePtys.filter((session) => !session.connected).length,
312
+ byProvider: countByProvider(activePtys),
313
+ };
314
+ }
315
+
316
+ function getActiveWorkSummary() {
317
+ const pty = getActivePtyWorkSummary();
318
+ const agents = getActiveProviderWorkSummary();
319
+ const total = pty.total + agents.total;
320
+
321
+ return {
322
+ hasActiveWork: total > 0,
323
+ total,
324
+ pty,
325
+ agents,
326
+ };
327
+ }
328
+
276
329
  async function readLatestPixcodePackageMetadata() {
277
330
  const registryRes = await fetch('https://registry.npmjs.org/@pixelbyte-software/pixcode');
278
331
  if (!registryRes.ok) throw new Error(`Registry returned HTTP ${registryRes.status}`);
@@ -375,8 +428,10 @@ async function runCommandUpdateJob(job, updateCommand, updateCwd) {
375
428
  cwd: updateCwd,
376
429
  env: process.env,
377
430
  shell: true,
431
+ detached: true,
378
432
  stdio: ['ignore', 'pipe', 'pipe'],
379
433
  });
434
+ try { child.unref(); } catch { /* noop */ }
380
435
  child.stdout?.on('data', (data) => appendUpdateJobLog(job, 'stdout', data.toString()));
381
436
  child.stderr?.on('data', (data) => appendUpdateJobLog(job, 'stderr', data.toString()));
382
437
  child.on('error', reject);
@@ -1635,6 +1690,7 @@ app.get('/api/system/update-state', authenticateToken, (req, res) => {
1635
1690
  success: true,
1636
1691
  state: readSystemUpdateState(),
1637
1692
  activeJob: snapshotUpdateJob(getActiveUpdateJob()),
1693
+ activeWork: getActiveWorkSummary(),
1638
1694
  currentVersion: SERVER_VERSION,
1639
1695
  installMode,
1640
1696
  capabilities: {
@@ -2035,6 +2091,16 @@ app.post('/api/system/update', authenticateToken, requireAdmin, requireApiScope(
2035
2091
  // (systemd/pm2/daemon manager) can bring the server back on the new code.
2036
2092
  // Foreground installs without a wrapper will simply stop; the UI reports this.
2037
2093
  app.post('/api/system/restart', authenticateToken, requireAdmin, requireApiScope('system:restart'), (req, res) => {
2094
+ const forceRestart = req.body?.force === true || req.query.force === 'true';
2095
+ const activeWork = getActiveWorkSummary();
2096
+ if (!forceRestart && activeWork.hasActiveWork) {
2097
+ return res.status(409).json({
2098
+ success: false,
2099
+ error: 'Active terminal or agent sessions are running. Confirm restart to interrupt them.',
2100
+ activeWork,
2101
+ });
2102
+ }
2103
+
2038
2104
  res.json({
2039
2105
  success: true,
2040
2106
  version: SERVER_VERSION,