@pixelbyte-software/pixcode 1.37.0 → 1.38.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.
package/server/index.js CHANGED
@@ -75,6 +75,7 @@ import geminiRoutes from './routes/gemini.js';
75
75
  import qwenRoutes from './routes/qwen.js';
76
76
  import pluginsRoutes from './routes/plugins.js';
77
77
  import messagesRoutes from './routes/messages.js';
78
+ import diagnosticsRoutes from './routes/diagnostics.js';
78
79
  import providerRoutes from './modules/providers/provider.routes.js';
79
80
  import {
80
81
  createA2ARouter,
@@ -320,6 +321,8 @@ const wss = new WebSocketServer({
320
321
 
321
322
  // Make WebSocket server available to routes
322
323
  app.locals.wss = wss;
324
+ app.locals.installMode = installMode;
325
+ app.locals.serverVersion = SERVER_VERSION;
323
326
  setNotificationWebSocketServer(wss);
324
327
 
325
328
  app.use(cors({ exposedHeaders: ['X-Refreshed-Token'] }));
@@ -391,6 +394,9 @@ app.use('/api/plugins', authenticateToken, pluginsRoutes);
391
394
  // Unified session messages route (protected)
392
395
  app.use('/api/sessions', authenticateToken, messagesRoutes);
393
396
 
397
+ // Diagnostics API Routes (protected)
398
+ app.use('/api/diagnostics', authenticateToken, diagnosticsRoutes);
399
+
394
400
  // Unified provider MCP routes (protected)
395
401
  app.use('/api/providers', authenticateToken, providerRoutes);
396
402
 
@@ -0,0 +1,15 @@
1
+ import express from 'express';
2
+
3
+ import { collectDiagnostics } from '../services/diagnostics.js';
4
+
5
+ const router = express.Router();
6
+
7
+ router.get('/', (req, res) => {
8
+ res.json(collectDiagnostics({
9
+ installMode: req.app.locals.installMode,
10
+ serverVersion: req.app.locals.serverVersion,
11
+ wss: req.app.locals.wss,
12
+ }));
13
+ });
14
+
15
+ export default router;
@@ -0,0 +1,105 @@
1
+ const SAFE_ENV_KEYS = [
2
+ 'NODE_ENV',
3
+ 'SERVER_PORT',
4
+ 'VITE_PORT',
5
+ 'HOST',
6
+ 'PORT',
7
+ 'DATABASE_PATH',
8
+ 'PIXCODE_NO_DAEMON',
9
+ 'PIXCODE_DISABLE_UPDATE_CHECK',
10
+ 'GITHUB_TOKEN',
11
+ 'NPM_TOKEN',
12
+ 'TELEGRAM_BOT_TOKEN',
13
+ 'ANTHROPIC_API_KEY',
14
+ 'OPENAI_API_KEY',
15
+ 'GEMINI_API_KEY',
16
+ ];
17
+
18
+ const SENSITIVE_KEY_PATTERN = /(authorization|cookie|credential|password|secret|token|api[_-]?key)/i;
19
+
20
+ function isSensitiveKey(key) {
21
+ return SENSITIVE_KEY_PATTERN.test(key);
22
+ }
23
+
24
+ function redactEnv(env = process.env) {
25
+ return SAFE_ENV_KEYS.reduce((acc, key) => {
26
+ if (!(key in env)) {
27
+ return acc;
28
+ }
29
+ acc[key] = isSensitiveKey(key) ? '[redacted]' : String(env[key]);
30
+ return acc;
31
+ }, {});
32
+ }
33
+
34
+ function providerCredentialState(env = process.env) {
35
+ return {
36
+ claude: Boolean(env.ANTHROPIC_API_KEY || env.CLAUDE_API_KEY),
37
+ codex: Boolean(env.OPENAI_API_KEY),
38
+ gemini: Boolean(env.GEMINI_API_KEY || env.GOOGLE_API_KEY),
39
+ telegram: Boolean(env.TELEGRAM_BOT_TOKEN),
40
+ github: Boolean(env.GITHUB_TOKEN),
41
+ npm: Boolean(env.NPM_TOKEN),
42
+ };
43
+ }
44
+
45
+ function normalizeMemory(memory) {
46
+ return Object.fromEntries(
47
+ Object.entries(memory).map(([key, value]) => [key, Number.isFinite(value) ? value : 0])
48
+ );
49
+ }
50
+
51
+ function resolveWebSocketClientCount(options) {
52
+ if (Number.isInteger(options.wsClientCount)) {
53
+ return options.wsClientCount;
54
+ }
55
+ return options.wss?.clients?.size || 0;
56
+ }
57
+
58
+ export function collectDiagnostics(options = {}) {
59
+ const now = options.now || new Date();
60
+ const env = options.env || process.env;
61
+ const versions = options.versions || process.versions;
62
+ const memoryUsage = options.memoryUsage || process.memoryUsage;
63
+ const uptime = options.uptime ?? process.uptime();
64
+
65
+ return {
66
+ status: 'ok',
67
+ timestamp: now.toISOString(),
68
+ version: options.serverVersion || '0.0.0',
69
+ installMode: options.installMode || 'unknown',
70
+ runtime: {
71
+ node: versions.node,
72
+ v8: versions.v8,
73
+ platform: options.platform || process.platform,
74
+ arch: options.arch || process.arch,
75
+ uptimeSeconds: Math.round(uptime),
76
+ },
77
+ memory: normalizeMemory(memoryUsage()),
78
+ websocket: {
79
+ clients: resolveWebSocketClientCount(options),
80
+ },
81
+ environment: redactEnv(env),
82
+ credentials: providerCredentialState(env),
83
+ notifications: {
84
+ telegramConfigured: Boolean(env.TELEGRAM_BOT_TOKEN),
85
+ webPushConfigured: Boolean(env.VAPID_PUBLIC_KEY && env.VAPID_PRIVATE_KEY),
86
+ },
87
+ };
88
+ }
89
+
90
+ export function redactDiagnostics(input) {
91
+ if (Array.isArray(input)) {
92
+ return input.map(item => redactDiagnostics(item));
93
+ }
94
+
95
+ if (!input || typeof input !== 'object') {
96
+ return input;
97
+ }
98
+
99
+ return Object.fromEntries(
100
+ Object.entries(input).map(([key, value]) => [
101
+ key,
102
+ isSensitiveKey(key) ? '[redacted]' : redactDiagnostics(value),
103
+ ])
104
+ );
105
+ }