vibex-sh 0.9.9 → 0.10.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/index.js +141 -7
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -56,6 +56,103 @@ function normalizeSessionId(sessionId) {
|
|
|
56
56
|
return sessionId;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Phase 1.8: Log Normalization for CLI
|
|
61
|
+
* Intelligently transforms log data into hybrid JSON structure
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
function normalizeLevel(level) {
|
|
65
|
+
if (!level) return 'debug';
|
|
66
|
+
const levelStr = String(level).toLowerCase();
|
|
67
|
+
if (['debug', 'dbg', 'trace'].includes(levelStr)) return 'debug';
|
|
68
|
+
if (['info', 'information', 'log'].includes(levelStr)) return 'info';
|
|
69
|
+
if (['warn', 'warning', 'wrn'].includes(levelStr)) return 'warn';
|
|
70
|
+
if (['error', 'err', 'exception', 'fatal', 'critical'].includes(levelStr)) return 'error';
|
|
71
|
+
return 'debug';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function extractMetrics(payload) {
|
|
75
|
+
const metrics = {};
|
|
76
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
77
|
+
return metrics;
|
|
78
|
+
}
|
|
79
|
+
if (payload.metrics && typeof payload.metrics === 'object' && !Array.isArray(payload.metrics)) {
|
|
80
|
+
for (const [key, value] of Object.entries(payload.metrics)) {
|
|
81
|
+
if (typeof value === 'number') {
|
|
82
|
+
metrics[key] = value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return metrics;
|
|
86
|
+
}
|
|
87
|
+
const knownContextFields = new Set([
|
|
88
|
+
'trace_id', 'traceId', 'user_id', 'userId', 'request_id', 'requestId',
|
|
89
|
+
'correlation_id', 'correlationId', 'span_id', 'spanId', 'session_id', 'sessionId',
|
|
90
|
+
'id', 'pid', 'port', 'year', 'timestamp', 'time', 'date', 'createdAt', 'updatedAt',
|
|
91
|
+
'datetime', 'ts', 'utc', 'iso', 'exc_info', 'exception', 'error', 'message', 'msg', 'level', 'severity', 'log_level'
|
|
92
|
+
]);
|
|
93
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
94
|
+
const keyLower = key.toLowerCase();
|
|
95
|
+
if (knownContextFields.has(keyLower)) continue;
|
|
96
|
+
if (keyLower.includes('timestamp') || keyLower.includes('time') || keyLower.includes('date')) continue;
|
|
97
|
+
if (typeof value === 'number') {
|
|
98
|
+
if (key.endsWith('_ms') || key.endsWith('_count') || key.endsWith('_size') ||
|
|
99
|
+
key.endsWith('Ms') || key.endsWith('Count') || key.endsWith('Size') ||
|
|
100
|
+
['cpu', 'memory', 'latency', 'response_time', 'duration'].some(pattern => keyLower.includes(pattern))) {
|
|
101
|
+
metrics[key] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return metrics;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function extractContext(payload) {
|
|
109
|
+
const context = {};
|
|
110
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
111
|
+
return context;
|
|
112
|
+
}
|
|
113
|
+
if (payload.context && typeof payload.context === 'object' && !Array.isArray(payload.context)) {
|
|
114
|
+
return payload.context;
|
|
115
|
+
}
|
|
116
|
+
const knownContextFields = {
|
|
117
|
+
'trace_id': 'trace_id', 'traceId': 'trace_id',
|
|
118
|
+
'user_id': 'user_id', 'userId': 'user_id',
|
|
119
|
+
'request_id': 'request_id', 'requestId': 'request_id',
|
|
120
|
+
'correlation_id': 'correlation_id', 'correlationId': 'correlation_id',
|
|
121
|
+
'span_id': 'span_id', 'spanId': 'span_id',
|
|
122
|
+
'session_id': 'session_id', 'sessionId': 'session_id',
|
|
123
|
+
};
|
|
124
|
+
for (const [field, normalizedKey] of Object.entries(knownContextFields)) {
|
|
125
|
+
if (payload[field] !== undefined) {
|
|
126
|
+
context[normalizedKey] = payload[field];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return context;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeToHybrid(message, level, payload) {
|
|
133
|
+
const merged = { ...(payload || {}) };
|
|
134
|
+
let normalizedMessage = message;
|
|
135
|
+
if (!normalizedMessage && merged.message) normalizedMessage = merged.message;
|
|
136
|
+
if (!normalizedMessage && merged.msg) normalizedMessage = merged.msg;
|
|
137
|
+
const normalizedLevel = normalizeLevel(level || merged.level || merged.severity || merged.log_level);
|
|
138
|
+
const metrics = extractMetrics(merged);
|
|
139
|
+
const context = extractContext(merged);
|
|
140
|
+
const annotation = merged._annotation;
|
|
141
|
+
const hybrid = {
|
|
142
|
+
message: normalizedMessage, // Can be null/undefined
|
|
143
|
+
level: normalizedLevel,
|
|
144
|
+
metrics: metrics,
|
|
145
|
+
context: context,
|
|
146
|
+
};
|
|
147
|
+
if (annotation) hybrid._annotation = annotation;
|
|
148
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
149
|
+
if (!['message', 'msg', 'level', 'severity', 'log_level', 'metrics', 'context', '_annotation'].includes(key)) {
|
|
150
|
+
if (!(key in hybrid)) hybrid[key] = value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return hybrid;
|
|
154
|
+
}
|
|
155
|
+
|
|
59
156
|
function deriveSocketUrl(webUrl) {
|
|
60
157
|
const url = new URL(webUrl);
|
|
61
158
|
|
|
@@ -505,12 +602,27 @@ async function main() {
|
|
|
505
602
|
return;
|
|
506
603
|
}
|
|
507
604
|
|
|
605
|
+
// Clear any existing reconnect timeout to prevent duplicate connections
|
|
606
|
+
if (reconnectTimeout) {
|
|
607
|
+
clearTimeout(reconnectTimeout);
|
|
608
|
+
reconnectTimeout = null;
|
|
609
|
+
}
|
|
610
|
+
|
|
508
611
|
connectionLock = true;
|
|
509
612
|
connectionState = 'connecting';
|
|
510
613
|
connectionStartTime = Date.now();
|
|
511
614
|
console.log(' 🔄 Connecting to WebSocket...');
|
|
512
615
|
|
|
513
616
|
try {
|
|
617
|
+
// Close existing socket if any (cleanup)
|
|
618
|
+
if (socket && socket.readyState !== WebSocket.CLOSED) {
|
|
619
|
+
try {
|
|
620
|
+
socket.close();
|
|
621
|
+
} catch (e) {
|
|
622
|
+
// Ignore errors when closing
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
514
626
|
socket = new WebSocket(`${socketUrl}?sessionId=${sessionId}`);
|
|
515
627
|
|
|
516
628
|
socket.onopen = () => {
|
|
@@ -855,15 +967,37 @@ async function main() {
|
|
|
855
967
|
let logData;
|
|
856
968
|
try {
|
|
857
969
|
const parsed = JSON.parse(trimmedLine);
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
970
|
+
// Phase 1.8: Normalize JSON to hybrid structure
|
|
971
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
972
|
+
const hybrid = normalizeToHybrid(null, null, parsed);
|
|
973
|
+
logData = {
|
|
974
|
+
type: 'json',
|
|
975
|
+
payload: hybrid,
|
|
976
|
+
timestamp: Date.now(),
|
|
977
|
+
};
|
|
978
|
+
} else {
|
|
979
|
+
// Parsed but not an object - treat as text
|
|
980
|
+
logData = {
|
|
981
|
+
type: 'json',
|
|
982
|
+
payload: {
|
|
983
|
+
message: trimmedLine,
|
|
984
|
+
level: 'debug',
|
|
985
|
+
metrics: {},
|
|
986
|
+
context: {},
|
|
987
|
+
},
|
|
988
|
+
timestamp: Date.now(),
|
|
989
|
+
};
|
|
990
|
+
}
|
|
863
991
|
} catch (e) {
|
|
992
|
+
// Phase 1.8: Text logs are now valuable - send as message field
|
|
864
993
|
logData = {
|
|
865
|
-
type: '
|
|
866
|
-
payload:
|
|
994
|
+
type: 'json',
|
|
995
|
+
payload: {
|
|
996
|
+
message: trimmedLine,
|
|
997
|
+
level: 'debug',
|
|
998
|
+
metrics: {},
|
|
999
|
+
context: {},
|
|
1000
|
+
},
|
|
867
1001
|
timestamp: Date.now(),
|
|
868
1002
|
};
|
|
869
1003
|
}
|