rl-rockcli 0.0.2 → 0.0.4

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 (75) hide show
  1. package/README.md +400 -0
  2. package/index.js +51 -21
  3. package/package.json +3 -2
  4. package/commands/log/core/constants.js +0 -237
  5. package/commands/log/core/display.js +0 -370
  6. package/commands/log/core/search.js +0 -330
  7. package/commands/log/core/tail.js +0 -216
  8. package/commands/log/core/utils.js +0 -424
  9. package/commands/log.js +0 -298
  10. package/commands/sandbox/core/log-bridge.js +0 -119
  11. package/commands/sandbox/core/replay/analyzer.js +0 -311
  12. package/commands/sandbox/core/replay/batch-orchestrator.js +0 -536
  13. package/commands/sandbox/core/replay/batch-task.js +0 -369
  14. package/commands/sandbox/core/replay/concurrent-display.js +0 -70
  15. package/commands/sandbox/core/replay/concurrent-orchestrator.js +0 -170
  16. package/commands/sandbox/core/replay/data-source.js +0 -86
  17. package/commands/sandbox/core/replay/display.js +0 -231
  18. package/commands/sandbox/core/replay/executor.js +0 -634
  19. package/commands/sandbox/core/replay/history-fetcher.js +0 -124
  20. package/commands/sandbox/core/replay/index.js +0 -338
  21. package/commands/sandbox/core/replay/loghouse-data-source.js +0 -177
  22. package/commands/sandbox/core/replay/pid-mapping.js +0 -26
  23. package/commands/sandbox/core/replay/request.js +0 -109
  24. package/commands/sandbox/core/replay/worker.js +0 -166
  25. package/commands/sandbox/core/session.js +0 -346
  26. package/commands/sandbox/log-bridge.js +0 -2
  27. package/commands/sandbox/ray.js +0 -2
  28. package/commands/sandbox/replay/analyzer.js +0 -311
  29. package/commands/sandbox/replay/batch-orchestrator.js +0 -536
  30. package/commands/sandbox/replay/batch-task.js +0 -369
  31. package/commands/sandbox/replay/concurrent-display.js +0 -70
  32. package/commands/sandbox/replay/concurrent-orchestrator.js +0 -170
  33. package/commands/sandbox/replay/display.js +0 -231
  34. package/commands/sandbox/replay/executor.js +0 -634
  35. package/commands/sandbox/replay/history-fetcher.js +0 -118
  36. package/commands/sandbox/replay/index.js +0 -338
  37. package/commands/sandbox/replay/pid-mapping.js +0 -26
  38. package/commands/sandbox/replay/request.js +0 -109
  39. package/commands/sandbox/replay/worker.js +0 -166
  40. package/commands/sandbox/replay.js +0 -2
  41. package/commands/sandbox/session.js +0 -2
  42. package/commands/sandbox-original.js +0 -1393
  43. package/commands/sandbox.js +0 -499
  44. package/help/help.json +0 -1071
  45. package/help/middleware.js +0 -71
  46. package/help/renderer.js +0 -800
  47. package/lib/plugin-context.js +0 -40
  48. package/sdks/sandbox/core/client.js +0 -845
  49. package/sdks/sandbox/core/config.js +0 -70
  50. package/sdks/sandbox/core/types.js +0 -74
  51. package/sdks/sandbox/httpLogger.js +0 -251
  52. package/sdks/sandbox/index.js +0 -9
  53. package/utils/asciiArt.js +0 -138
  54. package/utils/bun-compat.js +0 -59
  55. package/utils/ciPipelines.js +0 -138
  56. package/utils/cli.js +0 -17
  57. package/utils/command-router.js +0 -79
  58. package/utils/configManager.js +0 -503
  59. package/utils/dependency-resolver.js +0 -135
  60. package/utils/eagleeye_traceid.js +0 -151
  61. package/utils/envDetector.js +0 -78
  62. package/utils/execution_logger.js +0 -415
  63. package/utils/featureManager.js +0 -68
  64. package/utils/firstTimeTip.js +0 -44
  65. package/utils/hook-manager.js +0 -125
  66. package/utils/http-logger.js +0 -264
  67. package/utils/i18n.js +0 -139
  68. package/utils/image-progress.js +0 -159
  69. package/utils/logger.js +0 -154
  70. package/utils/plugin-loader.js +0 -124
  71. package/utils/plugin-manager.js +0 -348
  72. package/utils/ray_cli_wrapper.js +0 -746
  73. package/utils/sandbox-client.js +0 -419
  74. package/utils/terminal.js +0 -32
  75. package/utils/tips.js +0 -106
@@ -1,424 +0,0 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
-
4
- // 加载集群表映射配置
5
- const clusterMappingPath = path.join(__dirname, '../../../config/cluster_table_mapping.json');
6
- let clusterTableMapping = { clusters: {} };
7
- if (fs.existsSync(clusterMappingPath)) {
8
- clusterTableMapping = JSON.parse(fs.readFileSync(clusterMappingPath, 'utf8'));
9
- }
10
-
11
- /**
12
- * 检查字段是否应该在格式化模式下被过滤
13
- * @param {string} fieldName - 字段名
14
- * @returns {boolean} 是否应该被过滤
15
- */
16
- function isBlacklisted(fieldName) {
17
- const { BLACKLIST_FIELDS } = require('./constants');
18
- // 黑名单所有以 @ 开头的字段(仅在格式化模式下)
19
- if (fieldName.startsWith('@')) return true;
20
- // 黑名单 BLACKLIST_FIELDS 中的字段
21
- if (BLACKLIST_FIELDS.includes(fieldName)) return true;
22
- return false;
23
- }
24
-
25
- /**
26
- * 解析时间字符串为时间戳
27
- * @param {string} timeStr - 时间字符串
28
- * @returns {number|null} 时间戳(毫秒)
29
- */
30
- function parseTime(timeStr) {
31
- if (!timeStr) return null;
32
-
33
- // 如果是纯数字,智能判断是秒级还是毫秒级时间戳
34
- if (/^\d+$/.test(timeStr)) {
35
- const timestamp = parseInt(timeStr);
36
- // Unix 时间戳标准是秒级的(10位数字),毫秒级通常是13位数字
37
- // 如果数字长度 <= 10,认为是秒级时间戳,需要转换为毫秒
38
- // 如果数字长度 > 10,认为是毫秒级时间戳,直接使用
39
- if (timestamp <= 9999999999) {
40
- return timestamp * 1000; // 秒级 -> 毫秒级
41
- } else {
42
- return timestamp; // 已经是毫秒级
43
- }
44
- }
45
-
46
- // 解析相对时间格式(如 "15m", "1h", "2d")
47
- const relativeMatch = timeStr.match(/^(\d+)([mhd])$/);
48
- if (relativeMatch) {
49
- const value = parseInt(relativeMatch[1]);
50
- const unit = relativeMatch[2];
51
- const now = Date.now();
52
- switch (unit) {
53
- case 'm': return now - value * 60 * 1000; // 分钟
54
- case 'h': return now - value * 60 * 60 * 1000; // 小时
55
- case 'd': return now - value * 24 * 60 * 60 * 1000; // 天
56
- }
57
- }
58
-
59
- // 尝试解析为日期字符串
60
- const date = new Date(timeStr);
61
- if (!isNaN(date.getTime())) {
62
- return date.getTime();
63
- }
64
-
65
- throw new Error(`无法解析时间格式: ${timeStr}`);
66
- }
67
-
68
- /**
69
- * 生成日志的唯一键,用于去重
70
- * @param {Object} log - 日志对象
71
- * @returns {string} 唯一键
72
- */
73
- function getLogUniqueKey(log) {
74
- const source = log['@source'] || '';
75
- const filename = log['@filename'] || '';
76
- const packId = log['@packId'] || 0;
77
- const logGroupOrder = log['@logGroupOrder'] || 0;
78
- return `${source}:${filename}:${packId}:${logGroupOrder}`;
79
- }
80
-
81
- /**
82
- * 从字段过滤器中提取 @source 和 @filename 的值
83
- * @param {Array} fieldFilters - 字段过滤器数组
84
- * @returns {Object} 包含 source 和 filename 的对象
85
- */
86
- function extractSourceAndFilename(fieldFilters) {
87
- let source = null;
88
- let filename = null;
89
-
90
- if (fieldFilters && Array.isArray(fieldFilters)) {
91
- fieldFilters.forEach(f => {
92
- const sourceMatch = f.match(/^@source=(.+)$/);
93
- const filenameMatch = f.match(/^@filename=(.+)$/);
94
-
95
- if (sourceMatch) {
96
- source = sourceMatch[1];
97
- }
98
- if (filenameMatch) {
99
- filename = filenameMatch[1];
100
- }
101
- });
102
- }
103
-
104
- return { source, filename };
105
- }
106
-
107
- /**
108
- * 检查两条日志是否连续(同IP、同文件,且序号相邻)
109
- * @param {Object} log1 - 前一条日志
110
- * @param {Object} log2 - 后一条日志
111
- * @returns {boolean} 是否连续
112
- */
113
- function areLogsConsecutive(log1, log2) {
114
- if (!log1 || !log2) return false;
115
-
116
- const source1 = log1['@source'] || '';
117
- const source2 = log2['@source'] || '';
118
- const filename1 = log1['@filename'] || '';
119
- const filename2 = log2['@filename'] || '';
120
-
121
- // 必须是同一IP和文件
122
- if (source1 !== source2 || filename1 !== filename2) return false;
123
-
124
- const packId1 = parseInt(log1['@packId']) || 0;
125
- const packId2 = parseInt(log2['@packId']) || 0;
126
- const order1 = parseInt(log1['@logGroupOrder']) || 0;
127
- const order2 = parseInt(log2['@logGroupOrder']) || 0;
128
-
129
- // 同一个packId,order差1
130
- if (packId1 === packId2 && order2 - order1 === 1) {
131
- return true;
132
- }
133
-
134
- // packId差1,且log1是某packId的最后一条,log2是下一个packId的第一条
135
- // 简化判断:packId差1且order2=0
136
- if (packId2 - packId1 === 1 && order2 === 0) {
137
- return true;
138
- }
139
-
140
- return false;
141
- }
142
-
143
- /**
144
- * 构建查询字符串
145
- * @param {Object} options - 查询选项
146
- * @param {string} options.keyword - 关键词
147
- * @param {Array} options.field - 字段过滤条件
148
- * @param {string} options.query - 直接查询字符串
149
- * @param {string} options.sandboxId - 沙箱ID
150
- * @param {string} options.logFile - 日志文件名
151
- * @param {string} options.role - 用户角色(用于权限控制)
152
- * @returns {string} 查询字符串
153
- */
154
- function buildQueryString({ keyword, field, query, sandboxId, logFile, role }) {
155
- // 条件引用:内网专有模块(开源版不存在)
156
- let Roles, ALLOWED_LOG_FILES;
157
- try {
158
- const access_control = require('../../../lib/access_control');
159
- Roles = access_control.Roles;
160
- ALLOWED_LOG_FILES = access_control.ALLOWED_LOG_FILES;
161
- } catch (e) {
162
- // 开源版:无权限控制,使用空实现
163
- Roles = {};
164
- ALLOWED_LOG_FILES = [];
165
- }
166
-
167
- let queryString = '';
168
-
169
- // 如果提供了 --query,直接使用
170
- if (query) {
171
- queryString = query;
172
- } else {
173
- // 否则从 keyword 和 field 构建
174
- const queryParts = [];
175
-
176
- // 添加关键词搜索
177
- if (keyword) {
178
- const keywords = Array.isArray(keyword) ? keyword : [keyword];
179
- keywords.forEach(kw => {
180
- if (kw && typeof kw === 'string') {
181
- // 直接使用关键词,不使用引号包裹
182
- queryParts.push(kw);
183
- }
184
- });
185
- }
186
-
187
- // 添加字段搜索
188
- if (field && field.length > 0) {
189
- field.forEach(f => {
190
- const negationMatch = f.match(/^([^!=]+)!=(.*)$/);
191
- // 比较运算符匹配:>=, <=, >, < (必须在等值运算符之前检查,避免错误的匹配)
192
- // 使用 \w+ 来匹配字段名,避免匹配到运算符字符
193
- const greaterEqualMatch = f.match(/^(\w+)>=(.*)$/);
194
- const lessEqualMatch = f.match(/^(\w+)<=(.*)$/);
195
- const greaterMatch = f.match(/^(\w+)>(.*)$/);
196
- const lessMatch = f.match(/^(\w+)<(.*)$/);
197
- const equalMatch = f.match(/^([^=]+)=(.*)$/);
198
-
199
- if (negationMatch) {
200
- const [, fieldName, fieldValue] = negationMatch;
201
- // 暂时注释掉 _exists_ 逻辑
202
- // queryParts.push(`_exists_:${fieldName} and not ${fieldName}:${fieldValue}`);
203
- queryParts.push(`not ${fieldName}:${fieldValue}`);
204
- } else if (greaterEqualMatch) {
205
- // status>=500 → status:[500 TO *}
206
- const [, fieldName, fieldValue] = greaterEqualMatch;
207
- queryParts.push(`${fieldName}:[${fieldValue} TO *}`);
208
- } else if (lessEqualMatch) {
209
- // status<=500 → status:{* TO 500]
210
- const [, fieldName, fieldValue] = lessEqualMatch;
211
- queryParts.push(`${fieldName}:{* TO ${fieldValue}]`);
212
- } else if (greaterMatch) {
213
- // status>500 → status:{500 TO *}
214
- const [, fieldName, fieldValue] = greaterMatch;
215
- queryParts.push(`${fieldName}:{${fieldValue} TO *}`);
216
- } else if (lessMatch) {
217
- // status<500 → status:{* TO 500}
218
- const [, fieldName, fieldValue] = lessMatch;
219
- queryParts.push(`${fieldName}:{* TO ${fieldValue}}`);
220
- } else if (equalMatch) {
221
- const [, fieldName, fieldValue] = equalMatch;
222
- // 暂时注释掉 _exists_ 逻辑
223
- // queryParts.push(`${fieldName}:${fieldValue} and _exists_:${fieldName}`);
224
- queryParts.push(`${fieldName}:${fieldValue}`);
225
- }
226
- });
227
- }
228
-
229
- queryString = queryParts.join(' and ');
230
- }
231
-
232
- // 添加沙箱ID过滤
233
- if (sandboxId) {
234
- let sandboxFilter;
235
- if (logFile) {
236
- // 指定了 logFile,精确匹配
237
- sandboxFilter = `@filename:/data/logs/${sandboxId}/${logFile}`;
238
- } else if (role === Roles.SUPER_ADMIN) {
239
- // 管理员:保持现有逻辑,可匹配多种来源(包含 swe-rex.log)
240
- sandboxFilter = `(sandbox_id:${sandboxId} or ${sandboxId} or x22${sandboxId} or x0A${sandboxId} or @filename:/data/logs/${sandboxId}/command.log or @filename:/data/logs/${sandboxId}/swe-rex.log or @filename:/data/logs/${sandboxId}/rocklet.log)`;
241
- } else {
242
- // 普通用户:只能通过 @filename 白名单路径查询
243
- const allowedPaths = ALLOWED_LOG_FILES
244
- .map(file => `@filename:/data/logs/${sandboxId}/${file}`)
245
- .join(' or ');
246
- sandboxFilter = `(${allowedPaths})`;
247
- }
248
-
249
- if (queryString) {
250
- queryString = `${sandboxFilter} and ${queryString}`;
251
- } else {
252
- queryString = sandboxFilter;
253
- }
254
- }
255
-
256
- return queryString;
257
- }
258
-
259
- /**
260
- * @param {string|null} cluster - 指定的集群名称
261
- * @returns {Array} 查询列表 [{ clusterName, logType, tableName }]
262
- */
263
- function getQueriesToRun(cluster) {
264
- // 条件引用:内网专有模块(开源版不存在)
265
- let unifiedClient;
266
- try {
267
- const unified_log_client = require('../../../utils/unified_log_client');
268
- unifiedClient = unified_log_client.client;
269
- } catch (e) {
270
- // 开源版:无统一日志客户端,使用空实现
271
- unifiedClient = null;
272
- }
273
-
274
- const queriesToRun = [];
275
- const queriedTables = new Set();
276
- const clustersToQuery = cluster
277
- ? [cluster]
278
- : Object.keys(clusterTableMapping.clusters);
279
-
280
- clustersToQuery.forEach(clusterName => {
281
- const clusterConfig = clusterTableMapping.clusters[clusterName];
282
- if (clusterConfig) {
283
- Object.keys(clusterConfig).forEach(logType => {
284
- const tableName = clusterConfig[logType];
285
- if (tableName && !queriedTables.has(tableName)) {
286
- queriedTables.add(tableName);
287
- queriesToRun.push({ clusterName, logType, tableName });
288
- }
289
- });
290
- } else if (clusterName === 'nt-c') {
291
- const tableName = unifiedClient.defaultTableName;
292
- if (!queriedTables.has(tableName)) {
293
- queriedTables.add(tableName);
294
- queriesToRun.push({ clusterName, logType: 'log', tableName });
295
- }
296
- }
297
- });
298
-
299
- return queriesToRun;
300
- }
301
-
302
- /**
303
- * 反转义日志字符串
304
- * 参考: python3 -c "import sys; print(sys.stdin.read().encode().decode('unicode_escape').encode('latin-1').decode('utf-8'))"
305
- * @param {string} str - 需要反转义的字符串
306
- * @returns {string|null} 反转义后的字符串
307
- */
308
- function unescapeLogString(str) {
309
- if (!str || str === '-') return null;
310
-
311
- // 处理 \xXX 格式的转义字符
312
- const hexEscaped = str.replace(/\\x([0-9A-Fa-f]{2})/g, (match, hex) => {
313
- return String.fromCharCode(parseInt(hex, 16));
314
- });
315
-
316
- // 处理 Unicode 转义序列 \uXXXX
317
- const unicodeEscaped = hexEscaped.replace(/\\u([0-9A-Fa-f]{4})/g, (match, hex) => {
318
- return String.fromCharCode(parseInt(hex, 16));
319
- });
320
-
321
- // 处理常见的转义字符
322
- const commonEscaped = unicodeEscaped
323
- .replace(/\\n/g, '\n')
324
- .replace(/\\r/g, '\r')
325
- .replace(/\\t/g, '\t')
326
- .replace(/\\"/g, '"')
327
- .replace(/\\\\/g, '\\')
328
- .replace(/\\'/g, "'");
329
-
330
- return commonEscaped;
331
- }
332
-
333
- /**
334
- * 从 nginx 日志中提取沙箱ID
335
- * 优先级:
336
- * 1. 从 URI 参数中提取 sandbox_id
337
- * 2. 从反转义后的 request_body 中提取(JSON 或表单格式)
338
- * @param {Object} log - nginx 日志对象
339
- * @returns {string|null} 沙箱ID,未找到返回 null
340
- */
341
- function extractSandboxIdFromNginxLog(log) {
342
- // 1. 从 URI 参数中提取 sandbox_id
343
- const uri = log.request_uri || '';
344
- const uriMatch = uri.match(/[?&]sandbox_id=([^&]+)/);
345
- if (uriMatch) {
346
- return uriMatch[1];
347
- }
348
-
349
- // 2. 从 request_body 中提取
350
- const requestBody = log.request_body || '';
351
- if (!requestBody || requestBody === '-') {
352
- return null;
353
- }
354
-
355
- try {
356
- // 反转义 request_body
357
- const unescapedBody = unescapeLogString(requestBody);
358
- if (!unescapedBody) {
359
- return null;
360
- }
361
-
362
- // 尝试解析为 JSON
363
- try {
364
- const jsonData = JSON.parse(unescapedBody);
365
- if (jsonData.sandbox_id) {
366
- return jsonData.sandbox_id;
367
- }
368
- } catch (e) {
369
- // 不是 JSON,尝试解析为表单格式
370
- }
371
-
372
- // 尝试解析为表单格式 (application/x-www-form-urlencoded)
373
- const formDataPairs = unescapedBody.split('&');
374
- for (const pair of formDataPairs) {
375
- const [key, value] = pair.split('=');
376
- if (key === 'sandbox_id' && value) {
377
- return decodeURIComponent(value);
378
- }
379
- }
380
-
381
- // 尝试解析为 multipart/form-data 格式
382
- const formDataMatch = unescapedBody.match(/name="sandbox_id"[\s\S]*?\r?\n\r?\n([\s\S]*?)(?:\r?\n--|$)/);
383
- if (formDataMatch) {
384
- return formDataMatch[1].trim();
385
- }
386
- } catch (error) {
387
- // 解析失败,返回 null
388
- }
389
-
390
- return null;
391
- }
392
-
393
- /**
394
- * 移除 URI 的前缀(如 /apis/envs/sandbox/v1/)
395
- * @param {string} uri - 原始 URI
396
- * @param {string} prefix - 要移除的前缀,默认为 /apis/envs/sandbox/v1/
397
- * @returns {string} 移除前缀后的 URI
398
- */
399
- function removeUriPrefix(uri, prefix = '/apis/envs/sandbox/v1/') {
400
- if (!uri || typeof uri !== 'string') {
401
- return uri || '';
402
- }
403
-
404
- // 移除前缀
405
- if (uri.startsWith(prefix)) {
406
- return uri.substring(prefix.length);
407
- }
408
-
409
- return uri;
410
- }
411
-
412
- module.exports = {
413
- clusterTableMapping,
414
- isBlacklisted,
415
- parseTime,
416
- getLogUniqueKey,
417
- extractSourceAndFilename,
418
- areLogsConsecutive,
419
- buildQueryString,
420
- getQueriesToRun,
421
- unescapeLogString,
422
- extractSandboxIdFromNginxLog,
423
- removeUriPrefix,
424
- };