rl-rockcli 0.0.8 → 0.0.10

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 (162) hide show
  1. package/commands/attach/basic-repl.js +212 -0
  2. package/commands/attach/cleanup-history.js +189 -0
  3. package/commands/attach/cleanup-manager.js +163 -0
  4. package/commands/attach/copy-ui/copyRepl.js +195 -0
  5. package/commands/attach/copy-ui/index.js +7 -0
  6. package/commands/attach/copy-ui/render/outputBlock.js +25 -0
  7. package/commands/attach/copy-ui/viewport/viewport.js +23 -0
  8. package/commands/attach/copy-ui/viewport/wheel.js +14 -0
  9. package/commands/attach/history-manager.js +507 -0
  10. package/commands/attach/history-session.js +48 -0
  11. package/commands/attach/ink-repl/InkREPL.js +1507 -0
  12. package/commands/attach/ink-repl/builtinCommands.js +1253 -0
  13. package/commands/attach/ink-repl/components/ConnectingScreen.js +76 -0
  14. package/commands/attach/ink-repl/components/Console.js +191 -0
  15. package/commands/attach/ink-repl/components/DetailView.js +148 -0
  16. package/commands/attach/ink-repl/components/DropdownMenu.js +86 -0
  17. package/commands/attach/ink-repl/components/InputArea.js +125 -0
  18. package/commands/attach/ink-repl/components/InputLine.js +18 -0
  19. package/commands/attach/ink-repl/components/OutputArea.js +22 -0
  20. package/commands/attach/ink-repl/components/OutputItem.js +96 -0
  21. package/commands/attach/ink-repl/components/ShellLayout.js +61 -0
  22. package/commands/attach/ink-repl/components/Spinner.js +79 -0
  23. package/commands/attach/ink-repl/components/StatusBar.js +106 -0
  24. package/commands/attach/ink-repl/components/WelcomeBanner.js +48 -0
  25. package/commands/attach/ink-repl/contexts/LayoutContext.js +12 -0
  26. package/commands/attach/ink-repl/contexts/ThemeContext.js +43 -0
  27. package/commands/attach/ink-repl/hooks/useFunctionKeys.js +70 -0
  28. package/commands/attach/ink-repl/hooks/useMouse.js +162 -0
  29. package/commands/attach/ink-repl/hooks/useResources.js +132 -0
  30. package/commands/attach/ink-repl/hooks/useSpinner.js +49 -0
  31. package/commands/attach/ink-repl/index.js +112 -0
  32. package/commands/attach/ink-repl/package.json +3 -0
  33. package/commands/attach/ink-repl/replState.js +947 -0
  34. package/commands/attach/ink-repl/shortcuts/defaultKeybindings.js +138 -0
  35. package/commands/attach/ink-repl/shortcuts/index.js +332 -0
  36. package/commands/attach/ink-repl/themes/defaultDark.js +18 -0
  37. package/commands/attach/ink-repl/themes/defaultLight.js +18 -0
  38. package/commands/attach/ink-repl/themes/index.js +4 -0
  39. package/commands/attach/ink-repl/themes/themeManager.js +45 -0
  40. package/commands/attach/ink-repl/themes/themeTokens.js +15 -0
  41. package/commands/attach/ink-repl/utils/atCompletion.js +346 -0
  42. package/commands/attach/ink-repl/utils/clipboard.js +50 -0
  43. package/commands/attach/ink-repl/utils/consoleLogger.js +81 -0
  44. package/commands/attach/ink-repl/utils/exitCodeHandler.js +49 -0
  45. package/commands/attach/ink-repl/utils/exitCodeTips.js +56 -0
  46. package/commands/attach/ink-repl/utils/formatTime.js +12 -0
  47. package/commands/attach/ink-repl/utils/outputSelection.js +120 -0
  48. package/commands/attach/ink-repl/utils/outputViewport.js +77 -0
  49. package/commands/attach/ink-repl/utils/paginatedFileLoading.js +76 -0
  50. package/commands/attach/ink-repl/utils/paramHint.js +60 -0
  51. package/commands/attach/ink-repl/utils/parseError.js +174 -0
  52. package/commands/attach/ink-repl/utils/pathCompletion.js +167 -0
  53. package/commands/attach/ink-repl/utils/remotePathSafety.js +56 -0
  54. package/commands/attach/ink-repl/utils/replSelection.js +205 -0
  55. package/commands/attach/ink-repl/utils/responseFormatter.js +127 -0
  56. package/commands/attach/ink-repl/utils/textWrap.js +117 -0
  57. package/commands/attach/ink-repl/utils/truncate.js +115 -0
  58. package/commands/attach/opentui-repl/App.tsx +891 -0
  59. package/commands/attach/opentui-repl/builtinCommands.ts +80 -0
  60. package/commands/attach/opentui-repl/components/ConfirmDialog.tsx +116 -0
  61. package/commands/attach/opentui-repl/components/ConnectingScreen.tsx +131 -0
  62. package/commands/attach/opentui-repl/components/Console.tsx +73 -0
  63. package/commands/attach/opentui-repl/components/DetailView.tsx +45 -0
  64. package/commands/attach/opentui-repl/components/DropdownMenu.tsx +130 -0
  65. package/commands/attach/opentui-repl/components/ExecutionStatus.tsx +66 -0
  66. package/commands/attach/opentui-repl/components/Header.tsx +24 -0
  67. package/commands/attach/opentui-repl/components/OutputArea.tsx +25 -0
  68. package/commands/attach/opentui-repl/components/OutputBlock.tsx +108 -0
  69. package/commands/attach/opentui-repl/components/PromptInput.tsx +109 -0
  70. package/commands/attach/opentui-repl/components/StatusBar.tsx +63 -0
  71. package/commands/attach/opentui-repl/components/Toast.tsx +65 -0
  72. package/commands/attach/opentui-repl/components/WelcomeBanner.tsx +41 -0
  73. package/commands/attach/opentui-repl/contexts/ReplContext.tsx +137 -0
  74. package/commands/attach/opentui-repl/contexts/SessionContext.tsx +32 -0
  75. package/commands/attach/opentui-repl/contexts/ThemeContext.tsx +70 -0
  76. package/commands/attach/opentui-repl/contexts/ToastContext.tsx +69 -0
  77. package/commands/attach/opentui-repl/contexts/toast-logic.js +71 -0
  78. package/commands/attach/opentui-repl/hooks/useResources.ts +102 -0
  79. package/commands/attach/opentui-repl/hooks/useSpinner.ts +46 -0
  80. package/commands/attach/opentui-repl/index.js +99 -0
  81. package/commands/attach/opentui-repl/keybindings.ts +39 -0
  82. package/commands/attach/opentui-repl/package.json +3 -0
  83. package/commands/attach/opentui-repl/render.tsx +72 -0
  84. package/commands/attach/opentui-repl/tsconfig.json +12 -0
  85. package/commands/attach/repl.js +791 -0
  86. package/commands/attach/sandbox-id-resolver.js +56 -0
  87. package/commands/attach/session-manager.js +307 -0
  88. package/commands/attach/ui-mode.js +146 -0
  89. package/commands/log/core/constants.js +237 -0
  90. package/commands/log/core/display.js +370 -0
  91. package/commands/log/core/search.js +330 -0
  92. package/commands/log/core/tail.js +216 -0
  93. package/commands/log/core/utils.js +424 -0
  94. package/commands/log.js +298 -0
  95. package/commands/sandbox/core/log-bridge.js +119 -0
  96. package/commands/sandbox/core/replay/analyzer.js +311 -0
  97. package/commands/sandbox/core/replay/batch-orchestrator.js +536 -0
  98. package/commands/sandbox/core/replay/batch-task.js +369 -0
  99. package/commands/sandbox/core/replay/concurrent-display.js +70 -0
  100. package/commands/sandbox/core/replay/concurrent-orchestrator.js +170 -0
  101. package/commands/sandbox/core/replay/data-source.js +86 -0
  102. package/commands/sandbox/core/replay/display.js +231 -0
  103. package/commands/sandbox/core/replay/executor.js +634 -0
  104. package/commands/sandbox/core/replay/history-fetcher.js +124 -0
  105. package/commands/sandbox/core/replay/index.js +338 -0
  106. package/commands/sandbox/core/replay/loghouse-data-source.js +177 -0
  107. package/commands/sandbox/core/replay/pid-mapping.js +26 -0
  108. package/commands/sandbox/core/replay/request.js +109 -0
  109. package/commands/sandbox/core/replay/worker.js +166 -0
  110. package/commands/sandbox/core/session.js +346 -0
  111. package/commands/sandbox/log-bridge.js +2 -0
  112. package/commands/sandbox/ray.js +2 -0
  113. package/commands/sandbox/replay/analyzer.js +311 -0
  114. package/commands/sandbox/replay/batch-orchestrator.js +536 -0
  115. package/commands/sandbox/replay/batch-task.js +369 -0
  116. package/commands/sandbox/replay/concurrent-display.js +70 -0
  117. package/commands/sandbox/replay/concurrent-orchestrator.js +170 -0
  118. package/commands/sandbox/replay/display.js +231 -0
  119. package/commands/sandbox/replay/executor.js +634 -0
  120. package/commands/sandbox/replay/history-fetcher.js +118 -0
  121. package/commands/sandbox/replay/index.js +338 -0
  122. package/commands/sandbox/replay/pid-mapping.js +26 -0
  123. package/commands/sandbox/replay/request.js +109 -0
  124. package/commands/sandbox/replay/worker.js +166 -0
  125. package/commands/sandbox/replay.js +2 -0
  126. package/commands/sandbox/session.js +2 -0
  127. package/commands/sandbox-original.js +1393 -0
  128. package/commands/sandbox.js +499 -0
  129. package/help/help.json +1071 -0
  130. package/help/middleware.js +71 -0
  131. package/help/renderer.js +800 -0
  132. package/index.js +5 -15
  133. package/lib/plugin-context.js +40 -0
  134. package/package.json +2 -2
  135. package/sdks/sandbox/core/client.js +845 -0
  136. package/sdks/sandbox/core/config.js +70 -0
  137. package/sdks/sandbox/core/types.js +74 -0
  138. package/sdks/sandbox/httpLogger.js +251 -0
  139. package/sdks/sandbox/index.js +9 -0
  140. package/utils/asciiArt.js +138 -0
  141. package/utils/bun-compat.js +59 -0
  142. package/utils/ciPipelines.js +138 -0
  143. package/utils/cli.js +17 -0
  144. package/utils/command-router.js +79 -0
  145. package/utils/configManager.js +503 -0
  146. package/utils/dependency-resolver.js +135 -0
  147. package/utils/eagleeye_traceid.js +151 -0
  148. package/utils/envDetector.js +78 -0
  149. package/utils/execution_logger.js +415 -0
  150. package/utils/featureManager.js +68 -0
  151. package/utils/firstTimeTip.js +44 -0
  152. package/utils/hook-manager.js +125 -0
  153. package/utils/http-logger.js +264 -0
  154. package/utils/i18n.js +139 -0
  155. package/utils/image-progress.js +159 -0
  156. package/utils/logger.js +154 -0
  157. package/utils/plugin-loader.js +124 -0
  158. package/utils/plugin-manager.js +348 -0
  159. package/utils/ray_cli_wrapper.js +746 -0
  160. package/utils/sandbox-client.js +419 -0
  161. package/utils/terminal.js +32 -0
  162. package/utils/tips.js +106 -0
@@ -0,0 +1,237 @@
1
+ // 日志显示字段配置
2
+ const DISPLAY_FIELDS = [
3
+ 'timestamp',
4
+ 'time',
5
+ 'time_iso8601',
6
+ 'ms',
7
+ 'level',
8
+ 'sandbox_id',
9
+ 'trace_id',
10
+ 'sent_http_request_id',
11
+ 'request_id',
12
+ 'callId',
13
+ 'logger',
14
+ 'thread',
15
+ 'field',
16
+ 'method',
17
+ 'class',
18
+ 'request_method',
19
+ 'url',
20
+ 'request_uri',
21
+ 'host',
22
+ 'status',
23
+ 'status_code',
24
+ 'process_time',
25
+ 'request_time_usec',
26
+ 'request_length',
27
+ 'body_bytes_sent',
28
+ 'upstream_response_tim',
29
+ 'message',
30
+ 'request',
31
+ "headers",
32
+ "http_Authorization",
33
+ "http_x_cluster",
34
+ "http_user_id",
35
+ "http_x_experiment_id",
36
+ "http_user_agent",
37
+ 'request_body',
38
+ 'response',
39
+ 'filename',
40
+ 'lineno',
41
+ 'site',
42
+ 'exc_info',
43
+ 'exception',
44
+ 'command_id',
45
+ 'event',
46
+ 'content',
47
+ ];
48
+
49
+ // 字段颜色映射(ANSI 颜色代码)
50
+ // 1. 内置字段(@ 开头):灰色 - 系统元数据,通常不需要关注
51
+ // 2. 一般情况不需要关注的字段:灰色 - 辅助信息,低价值
52
+ // 3. 普通字段:青色 - 常规字段,有一定价值
53
+ // 4. 重点字段:粗体青色 - 核心标识,最重要的字段
54
+ const FIELD_COLOR_MAP = {
55
+ // 1. 内置字段(@ 开头)- 灰色
56
+ '@timestamp': '\x1b[0;90m',
57
+ '@source': '\x1b[0;90m',
58
+ '@app_group': '\x1b[0;90m',
59
+ '@hostname': '\x1b[0;90m',
60
+ '@filename': '\x1b[0;90m',
61
+ '@packId': '\x1b[0;90m',
62
+ '@logGroupOrder': '\x1b[0;90m',
63
+ '@topic': '\x1b[0;90m',
64
+ '@app_stage': '\x1b[0;90m',
65
+ '@site': '\x1b[0;90m',
66
+
67
+ // 2. 一般情况不需要关注的字段 - 灰色
68
+ 'ms': '\x1b[0;90m',
69
+ 'logger': '\x1b[0;90m',
70
+ 'lineno': '\x1b[0;90m',
71
+ 'site': '\x1b[0;90m',
72
+ 'command_id': '\x1b[0;90m',
73
+ 'sent_http_request_id': '\x1b[0;90m',
74
+
75
+ // 3. 普通字段 - 青色
76
+ 'method': '\x1b[0;36m',
77
+ 'request_method': '\x1b[0;36m',
78
+ 'field': '\x1b[0;36m',
79
+ 'url': '\x1b[0;36m',
80
+ 'request_uri': '\x1b[0;36m',
81
+ 'status_code': '\x1b[0;36m',
82
+ 'process_time': '\x1b[0;36m',
83
+ 'request': '\x1b[0;36m',
84
+ 'request_body': '\x1b[0;36m',
85
+ 'response': '\x1b[0;36m',
86
+ 'filename': '\x1b[0;36m',
87
+ 'event': '\x1b[0;36m',
88
+ 'content': '\x1b[0;36m',
89
+ 'sandbox_id': '\x1b[0;36m',
90
+ 'trace_id': '\x1b[0;36m',
91
+
92
+ // 4. 重点字段 - 粗体青色
93
+ 'timestamp': '\x1b[1;36m',
94
+ 'time': '\x1b[1;36m',
95
+ 'time_iso8601': '\x1b[1;36m',
96
+ 'level': '\x1b[1;36m',
97
+ 'message': '\x1b[1;36m',
98
+ 'exc_info': '\x1b[1;31m', // 异常信息 - 红色粗体
99
+ 'exception': '\x1b[1;31m', // 异常 - 红色粗体
100
+ 'error': '\x1b[1;31m', // 错误 - 红色粗体
101
+ };
102
+
103
+ // 默认字段颜色
104
+ const DEFAULT_FIELD_COLOR = '\x1b[0;90m';
105
+
106
+ // 字段黑名单(格式化模式下不显示)
107
+ const BLACKLIST_FIELDS = [
108
+ 'sent_http_X_App_Id',
109
+ 'scheme',
110
+ 'remote_user',
111
+ 'http_x_opensearch_rem',
112
+ 'http_x_opensearch_sec',
113
+ 'sent_http_Application',
114
+ 'http_Content_Md5',
115
+ 'http_date',
116
+ 'sent_http_X_App_Name',
117
+ 'http_Authorization',
118
+ 'http_x_opensearch_non',
119
+ 'sent_http_X_Aliyun_Us',
120
+ 'sent_http_X_AppGroup_',
121
+ 'upstream_response_tim',
122
+ ];
123
+
124
+ // 需要截断的字段
125
+ const TRUNCATE_FIELDS = ['message', 'content', 'event'];
126
+
127
+ // Group by field mapping
128
+ const GROUP_BY_FIELDS = {
129
+ 'file': '@filename',
130
+ 'ip': '@source',
131
+ 'app': '@app_group',
132
+ 'hostname': '@hostname',
133
+ 'cluster': '_cluster',
134
+ };
135
+
136
+ // Group by field display labels
137
+ const GROUP_BY_LABELS = {
138
+ '@filename': 'File',
139
+ '@source': 'IP',
140
+ '@app_group': 'App',
141
+ '@hostname': 'Machine',
142
+ '_cluster': 'Cluster',
143
+ };
144
+
145
+ // ANSI 颜色代码
146
+ const ANSI_COLORS = {
147
+ RESET: '\x1b[0m',
148
+ BOLD: '\x1b[1m',
149
+ CYAN: '\x1b[36m',
150
+ YELLOW: '\x1b[33m',
151
+ GREEN: '\x1b[32m',
152
+ MAGENTA: '\x1b[35m',
153
+ BLUE: '\x1b[34m',
154
+ RED: '\x1b[31m',
155
+ GRAY: '\x1b[90m',
156
+ };
157
+
158
+ // 查询时间范围(分钟)
159
+ const TIME_RANGES = [15, 60, 180, 720, 1800, 43200];
160
+
161
+ // 分页大小
162
+ const PAGE_SIZE = 1000;
163
+
164
+ // 日志文件名
165
+ // 注意:内网专有日志路径已移到 config/internal_log_config.json
166
+ const LOG_FILES = {
167
+ // 保留空对象,由具体命令从配置文件加载
168
+ };
169
+
170
+
171
+ // 字段补全列表(用于 -f 参数的自动补全)
172
+ const FIELD_COMPLETION_LIST = [
173
+ // 系统内置字段(@ 开头)
174
+ '@filename',
175
+ '@source',
176
+ '@app_group',
177
+ '@hostname',
178
+ '@timestamp',
179
+ '@packId',
180
+ '@logGroupOrder',
181
+ '@topic',
182
+ '@app_stage',
183
+ '@site',
184
+
185
+ // 常规字段
186
+ 'status',
187
+ 'status_code',
188
+ 'level',
189
+ 'message',
190
+ 'method',
191
+ 'request_method',
192
+ 'url',
193
+ 'request_uri',
194
+ 'host',
195
+ 'sandbox_id',
196
+ 'trace_id',
197
+ 'thread',
198
+ 'logger',
199
+ 'field',
200
+ 'process_time',
201
+ 'request_time_usec',
202
+ 'request_length',
203
+ 'body_bytes_sent',
204
+ 'request',
205
+ 'headers',
206
+ 'http_Authorization',
207
+ 'http_x_cluster',
208
+ 'http_user_id',
209
+ 'http_x_experiment_id',
210
+ 'http_user_agent',
211
+ 'request_body',
212
+ 'response',
213
+ 'filename',
214
+ 'lineno',
215
+ 'site',
216
+ 'exc_info',
217
+ 'exception',
218
+ 'command_id',
219
+ 'event',
220
+ 'content',
221
+ 'error',
222
+ ];
223
+
224
+ module.exports = {
225
+ DISPLAY_FIELDS,
226
+ FIELD_COLOR_MAP,
227
+ DEFAULT_FIELD_COLOR,
228
+ BLACKLIST_FIELDS,
229
+ TRUNCATE_FIELDS,
230
+ GROUP_BY_FIELDS,
231
+ GROUP_BY_LABELS,
232
+ ANSI_COLORS,
233
+ TIME_RANGES,
234
+ PAGE_SIZE,
235
+ LOG_FILES,
236
+ FIELD_COMPLETION_LIST,
237
+ };
@@ -0,0 +1,370 @@
1
+ const { DISPLAY_FIELDS, FIELD_COLOR_MAP, DEFAULT_FIELD_COLOR, TRUNCATE_FIELDS, ANSI_COLORS, GROUP_BY_FIELDS } = require('./constants');
2
+ const { isBlacklisted } = require('./utils');
3
+
4
+ // 分组字段显示名称
5
+ const GROUP_BY_LABELS = {
6
+ '@filename': 'File',
7
+ '@source': 'IP',
8
+ '@app_group': 'App',
9
+ '@hostname': 'Machine',
10
+ '_cluster': 'Cluster',
11
+ };
12
+
13
+ function highlightKeyword(text, keyword) {
14
+ if (!keyword || !text) return text;
15
+
16
+ const HIGHLIGHT_BG = '\x1b[43m';
17
+ const HIGHLIGHT_FG = '\x1b[30m';
18
+ const RESET = '\x1b[0m\x1b[49m';
19
+
20
+ const keywords = Array.isArray(keyword) ? keyword : [keyword];
21
+
22
+ let result = text;
23
+ keywords.forEach(kw => {
24
+ if (kw && typeof kw === 'string') {
25
+ const escapedKeyword = kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
26
+ const regex = new RegExp(`(${escapedKeyword})`, 'gi');
27
+ result = result.replace(regex, `${HIGHLIGHT_BG}${HIGHLIGHT_FG}$1${RESET}`);
28
+ }
29
+ });
30
+
31
+ return result;
32
+ }
33
+
34
+ function highlightFieldName(fieldName, keyword, originalColor) {
35
+ if (!keyword || !fieldName) return originalColor + fieldName + '\x1b[0m';
36
+
37
+ const HIGHLIGHT_BG = '\x1b[43m';
38
+ const HIGHLIGHT_FG = '\x1b[30m';
39
+ const RESET = '\x1b[0m\x1b[49m';
40
+
41
+ const keywords = Array.isArray(keyword) ? keyword : [keyword];
42
+
43
+ const shouldHighlight = keywords.some(kw => kw && typeof kw === 'string' && fieldName === kw);
44
+
45
+ if (shouldHighlight) {
46
+ return `${HIGHLIGHT_BG}${HIGHLIGHT_FG}${fieldName}${RESET}`;
47
+ }
48
+
49
+ return originalColor + fieldName + '\x1b[0m';
50
+ }
51
+
52
+ function truncateValue(value, fieldName, truncate) {
53
+ if (truncate === 0) {
54
+ return value;
55
+ }
56
+
57
+ if (TRUNCATE_FIELDS.includes(fieldName)) {
58
+ const strValue = String(value);
59
+ if (strValue.length > truncate) {
60
+ return strValue.substring(0, truncate) + '...[truncated]';
61
+ }
62
+ }
63
+ return value;
64
+ }
65
+
66
+ function applyHighlightAndTruncate(value, fieldName, truncate, keyword, highlight) {
67
+ let strValue = typeof value === 'object' ? JSON.stringify(value) : String(value);
68
+
69
+ strValue = truncateValue(strValue, fieldName, truncate);
70
+
71
+ if (keyword && highlight) {
72
+ strValue = highlightKeyword(strValue, keyword);
73
+ }
74
+
75
+ return strValue;
76
+ }
77
+
78
+ function getFieldColor(fieldName, isTTY) {
79
+ if (!isTTY) return '';
80
+ return FIELD_COLOR_MAP[fieldName] || DEFAULT_FIELD_COLOR;
81
+ }
82
+
83
+ function formatLogEntry(log, raw, logFormat, columns, truncate, keyword, highlight) {
84
+ const isTTY = process.stdout.isTTY;
85
+ const RESET = ANSI_COLORS.RESET;
86
+
87
+ let columnFilter = null;
88
+ if (columns) {
89
+ columnFilter = columns.split(',').map(c => c.trim());
90
+ }
91
+
92
+ if (logFormat === 'json') {
93
+ const output = {};
94
+ Object.keys(log).forEach(key => {
95
+ if (key.startsWith('@')) return;
96
+ if (columnFilter && !columnFilter.includes(key)) return;
97
+ if (log[key] === null || log[key] === undefined) return;
98
+ output[key] = log[key];
99
+ });
100
+ console.log(JSON.stringify(output));
101
+ return;
102
+ }
103
+
104
+ if (logFormat === 'columns') {
105
+ const fieldsToDisplay = columnFilter || Object.keys(log).filter(k => !k.startsWith('@'));
106
+ fieldsToDisplay.forEach(key => {
107
+ if (log[key] === null || log[key] === undefined) return;
108
+ const color = getFieldColor(key, isTTY);
109
+ const value = applyHighlightAndTruncate(log[key], key, truncate, keyword, highlight);
110
+ const highlightedKey = highlightFieldName(key, keyword && highlight ? keyword : [], color);
111
+ console.log(`${highlightedKey}: ${value}`);
112
+ });
113
+ console.log('');
114
+ return;
115
+ }
116
+
117
+ let formattedFields = [];
118
+
119
+ if (raw) {
120
+ const atFields = Object.keys(log).filter(fieldName =>
121
+ fieldName.startsWith('@') &&
122
+ (!columnFilter || columnFilter.includes(fieldName)) &&
123
+ log[fieldName] !== null &&
124
+ log[fieldName] !== undefined
125
+ ).sort();
126
+
127
+ const displayFields = DISPLAY_FIELDS.filter(fieldName =>
128
+ !fieldName.startsWith('@') &&
129
+ log.hasOwnProperty(fieldName) &&
130
+ log[fieldName] !== null &&
131
+ log[fieldName] !== undefined &&
132
+ (!columnFilter || columnFilter.includes(fieldName))
133
+ );
134
+
135
+ const otherFields = Object.keys(log).filter(fieldName =>
136
+ !fieldName.startsWith('@') &&
137
+ !DISPLAY_FIELDS.includes(fieldName) &&
138
+ (!columnFilter || columnFilter.includes(fieldName)) &&
139
+ log[fieldName] !== null &&
140
+ log[fieldName] !== undefined
141
+ ).sort();
142
+
143
+ atFields.forEach(fieldName => {
144
+ const color = getFieldColor(fieldName, isTTY);
145
+ const value = applyHighlightAndTruncate(log[fieldName], fieldName, truncate, keyword, highlight);
146
+ const highlightedKey = highlightFieldName(fieldName, keyword && highlight ? keyword : [], color);
147
+
148
+ let formattedValue = value;
149
+ if (typeof value === 'string' && (value.includes(' ') || value.includes('"') || value.includes("'") || value.includes('=') || value.includes('\n') || value.includes('\r'))) {
150
+ formattedValue = `"${value.replace(/"/g, '\\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r')}"`;
151
+ }
152
+
153
+ formattedFields.push(`${highlightedKey}=${formattedValue}${RESET}`);
154
+ });
155
+
156
+ displayFields.forEach(fieldName => {
157
+ const color = getFieldColor(fieldName, isTTY);
158
+ const value = applyHighlightAndTruncate(log[fieldName], fieldName, truncate, keyword, highlight);
159
+ const highlightedKey = highlightFieldName(fieldName, keyword && highlight ? keyword : [], color);
160
+
161
+ let formattedValue = value;
162
+ if (typeof value === 'string' && (value.includes(' ') || value.includes('"') || value.includes("'") || value.includes('=') || value.includes('\n') || value.includes('\r'))) {
163
+ formattedValue = `"${value.replace(/"/g, '\\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r')}"`;
164
+ }
165
+
166
+ formattedFields.push(`${highlightedKey}=${formattedValue}${RESET}`);
167
+ });
168
+
169
+ otherFields.forEach(fieldName => {
170
+ const color = getFieldColor(fieldName, isTTY);
171
+ const value = applyHighlightAndTruncate(log[fieldName], fieldName, truncate, keyword, highlight);
172
+ const highlightedKey = highlightFieldName(fieldName, keyword && highlight ? keyword : [], color);
173
+
174
+ let formattedValue = value;
175
+ if (typeof value === 'string' && (value.includes(' ') || value.includes('"') || value.includes("'") || value.includes('=') || value.includes('\n') || value.includes('\r'))) {
176
+ formattedValue = `"${value.replace(/"/g, '\\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r')}"`;
177
+ }
178
+
179
+ formattedFields.push(`${highlightedKey}=${formattedValue}${RESET}`);
180
+ });
181
+ } else {
182
+ const fieldsToDisplay = columnFilter || DISPLAY_FIELDS;
183
+
184
+ fieldsToDisplay.forEach(fieldName => {
185
+ if (!log.hasOwnProperty(fieldName)) return;
186
+ if (!raw && isBlacklisted(fieldName)) return;
187
+ if (log[fieldName] === null || log[fieldName] === undefined) return;
188
+
189
+ const color = getFieldColor(fieldName, isTTY);
190
+ const value = applyHighlightAndTruncate(log[fieldName], fieldName, truncate, keyword, highlight);
191
+ const highlightedKey = highlightFieldName(fieldName, keyword && highlight ? keyword : [], color);
192
+
193
+ let formattedValue = value;
194
+ if (typeof value === 'string' && (value.includes(' ') || value.includes('"') || value.includes("'") || value.includes('=') || value.includes('\n') || value.includes('\r'))) {
195
+ formattedValue = `"${value.replace(/"/g, '\\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r')}"`;
196
+ }
197
+
198
+ formattedFields.push(`${highlightedKey}=${formattedValue}${RESET}`);
199
+ });
200
+ }
201
+
202
+ if (formattedFields.length > 0) {
203
+ console.log(formattedFields.join(' '));
204
+ }
205
+ }
206
+
207
+ function displayLogsWithContext(logs, raw, logFormat, multilines, columns, truncate, keyword, highlight, showContextSeparators, groupByFields = null) {
208
+ if (!logs || logs.length === 0) {
209
+ return;
210
+ }
211
+
212
+ if (!showContextSeparators) {
213
+ displayLogs(logs, raw, logFormat, multilines, columns, truncate, keyword, highlight, groupByFields);
214
+ return;
215
+ }
216
+
217
+ const isTTY = process.stdout.isTTY;
218
+ const CONTEXT_SEPARATOR = isTTY ? '\x1b[90m--\x1b[0m' : '--';
219
+
220
+ const { areLogsConsecutive } = require('./utils');
221
+ let previousLog = null;
222
+ const logsToDisplay = [];
223
+
224
+ logs.forEach((log) => {
225
+ if (previousLog !== null && !areLogsConsecutive(previousLog, log)) {
226
+ logsToDisplay.push({ _isSeparator: true });
227
+ }
228
+ logsToDisplay.push(log);
229
+ previousLog = log;
230
+ });
231
+
232
+ logsToDisplay.forEach((item) => {
233
+ if (item._isSeparator) {
234
+ console.log(CONTEXT_SEPARATOR);
235
+ } else {
236
+ formatLogEntry(item, raw, logFormat, columns, truncate, keyword, highlight);
237
+ }
238
+ });
239
+ }
240
+
241
+ function displayLogs(logs, raw, logFormat, multilines, columns, truncate, keyword, highlight, groupByFields = null) {
242
+ if (!logs || logs.length === 0) {
243
+ return;
244
+ }
245
+
246
+ const isTTY = process.stdout.isTTY;
247
+ const RESET = ANSI_COLORS.RESET;
248
+
249
+ let columnFilter = null;
250
+ if (columns) {
251
+ columnFilter = columns.split(',').map(c => c.trim());
252
+ }
253
+
254
+ const resolvedGroupByFields = groupByFields
255
+ ? groupByFields.map(field => GROUP_BY_FIELDS[field] || field)
256
+ : [];
257
+
258
+ let sortedLogs = logs;
259
+ if (resolvedGroupByFields.length > 0) {
260
+ sortedLogs = [...logs].sort((a, b) => {
261
+ for (const field of resolvedGroupByFields) {
262
+ const valA = a[field] || 'unknown';
263
+ const valB = b[field] || 'unknown';
264
+ if (valA !== valB) {
265
+ return String(valA).localeCompare(String(valB));
266
+ }
267
+ }
268
+ const timeA = parseInt(a['@timestamp']) || 0;
269
+ const timeB = parseInt(b['@timestamp']) || 0;
270
+ return timeA - timeB;
271
+ });
272
+ }
273
+
274
+ let previousGroupValues = resolvedGroupByFields.map(() => null);
275
+
276
+ sortedLogs.forEach((log, index) => {
277
+ const currentGroupValues = resolvedGroupByFields.map(field => log[field] || 'unknown');
278
+
279
+ if (resolvedGroupByFields.length > 0) {
280
+ const isFirstLog = index === 0;
281
+ const hasChanged = !isFirstLog && currentGroupValues.some((value, i) => value !== previousGroupValues[i]);
282
+
283
+ if (hasChanged || isFirstLog) {
284
+ if (!isFirstLog) {
285
+ console.log('');
286
+ if (isTTY) {
287
+ console.log('\x1b[90m' + '─'.repeat(80) + '\x1b[0m');
288
+ } else {
289
+ console.log('─'.repeat(80));
290
+ }
291
+ }
292
+
293
+ resolvedGroupByFields.forEach((field, i) => {
294
+ if (isFirstLog || currentGroupValues[i] !== previousGroupValues[i]) {
295
+ const label = GROUP_BY_LABELS[field] || field;
296
+ const value = currentGroupValues[i];
297
+ if (isTTY) {
298
+ console.log('\x1b[36;1m' + `📦 ${label}: ${value}` + '\x1b[0m');
299
+ } else {
300
+ console.log(`${label}: ${value}`);
301
+ }
302
+ }
303
+ });
304
+ }
305
+ }
306
+
307
+ if (multilines && logFormat !== 'columns') {
308
+ let fieldsToDisplay;
309
+
310
+ if (raw) {
311
+ if (columnFilter) {
312
+ fieldsToDisplay = columnFilter;
313
+ } else {
314
+ const atFields = Object.keys(log).filter(fieldName =>
315
+ fieldName.startsWith('@') &&
316
+ log[fieldName] !== null &&
317
+ log[fieldName] !== undefined
318
+ ).sort();
319
+
320
+ const displayFields = DISPLAY_FIELDS.filter(fieldName =>
321
+ !fieldName.startsWith('@') &&
322
+ log.hasOwnProperty(fieldName) &&
323
+ log[fieldName] !== null &&
324
+ log[fieldName] !== undefined
325
+ );
326
+
327
+ const otherFields = Object.keys(log).filter(fieldName =>
328
+ !fieldName.startsWith('@') &&
329
+ !DISPLAY_FIELDS.includes(fieldName) &&
330
+ log[fieldName] !== null &&
331
+ log[fieldName] !== undefined
332
+ ).sort();
333
+
334
+ fieldsToDisplay = [...atFields, ...displayFields, ...otherFields];
335
+ }
336
+ } else {
337
+ fieldsToDisplay = columnFilter || Object.keys(log).filter(k => !k.startsWith('@'));
338
+ }
339
+
340
+ fieldsToDisplay.forEach(fieldName => {
341
+ if (!log.hasOwnProperty(fieldName)) return;
342
+ if (!raw && isBlacklisted(fieldName)) return;
343
+ if (log[fieldName] === null || log[fieldName] === undefined) return;
344
+
345
+ const color = getFieldColor(fieldName, isTTY);
346
+ const value = applyHighlightAndTruncate(log[fieldName], fieldName, truncate, keyword, highlight);
347
+ const highlightedKey = highlightFieldName(fieldName, keyword && highlight ? keyword : [], color);
348
+
349
+ console.log(`${highlightedKey}=${value}${RESET}`);
350
+ });
351
+
352
+ console.log('\x1b[90m' + '='.repeat(80) + '\x1b[0m');
353
+ } else {
354
+ formatLogEntry(log, raw, logFormat, columns, truncate, keyword, highlight);
355
+ }
356
+
357
+ previousGroupValues = currentGroupValues;
358
+ });
359
+ }
360
+
361
+ module.exports = {
362
+ highlightKeyword,
363
+ highlightFieldName,
364
+ truncateValue,
365
+ applyHighlightAndTruncate,
366
+ getFieldColor,
367
+ formatLogEntry,
368
+ displayLogsWithContext,
369
+ displayLogs,
370
+ };