rl-rockcli 0.0.7 → 0.0.8
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 +15 -5
- package/package.json +2 -2
- package/commands/log/core/constants.js +0 -237
- package/commands/log/core/display.js +0 -370
- package/commands/log/core/search.js +0 -330
- package/commands/log/core/tail.js +0 -216
- package/commands/log/core/utils.js +0 -424
- package/commands/log.js +0 -298
- package/commands/sandbox/core/log-bridge.js +0 -119
- package/commands/sandbox/core/replay/analyzer.js +0 -311
- package/commands/sandbox/core/replay/batch-orchestrator.js +0 -536
- package/commands/sandbox/core/replay/batch-task.js +0 -369
- package/commands/sandbox/core/replay/concurrent-display.js +0 -70
- package/commands/sandbox/core/replay/concurrent-orchestrator.js +0 -170
- package/commands/sandbox/core/replay/data-source.js +0 -86
- package/commands/sandbox/core/replay/display.js +0 -231
- package/commands/sandbox/core/replay/executor.js +0 -634
- package/commands/sandbox/core/replay/history-fetcher.js +0 -124
- package/commands/sandbox/core/replay/index.js +0 -338
- package/commands/sandbox/core/replay/loghouse-data-source.js +0 -177
- package/commands/sandbox/core/replay/pid-mapping.js +0 -26
- package/commands/sandbox/core/replay/request.js +0 -109
- package/commands/sandbox/core/replay/worker.js +0 -166
- package/commands/sandbox/core/session.js +0 -346
- package/commands/sandbox/log-bridge.js +0 -2
- package/commands/sandbox/ray.js +0 -2
- package/commands/sandbox/replay/analyzer.js +0 -311
- package/commands/sandbox/replay/batch-orchestrator.js +0 -536
- package/commands/sandbox/replay/batch-task.js +0 -369
- package/commands/sandbox/replay/concurrent-display.js +0 -70
- package/commands/sandbox/replay/concurrent-orchestrator.js +0 -170
- package/commands/sandbox/replay/display.js +0 -231
- package/commands/sandbox/replay/executor.js +0 -634
- package/commands/sandbox/replay/history-fetcher.js +0 -118
- package/commands/sandbox/replay/index.js +0 -338
- package/commands/sandbox/replay/pid-mapping.js +0 -26
- package/commands/sandbox/replay/request.js +0 -109
- package/commands/sandbox/replay/worker.js +0 -166
- package/commands/sandbox/replay.js +0 -2
- package/commands/sandbox/session.js +0 -2
- package/commands/sandbox-original.js +0 -1393
- package/commands/sandbox.js +0 -499
- package/help/help.json +0 -1071
- package/help/middleware.js +0 -71
- package/help/renderer.js +0 -800
- package/lib/plugin-context.js +0 -40
- package/sdks/sandbox/core/client.js +0 -845
- package/sdks/sandbox/core/config.js +0 -70
- package/sdks/sandbox/core/types.js +0 -74
- package/sdks/sandbox/httpLogger.js +0 -251
- package/sdks/sandbox/index.js +0 -9
- package/utils/asciiArt.js +0 -138
- package/utils/bun-compat.js +0 -59
- package/utils/ciPipelines.js +0 -138
- package/utils/cli.js +0 -17
- package/utils/command-router.js +0 -79
- package/utils/configManager.js +0 -503
- package/utils/dependency-resolver.js +0 -135
- package/utils/eagleeye_traceid.js +0 -151
- package/utils/envDetector.js +0 -78
- package/utils/execution_logger.js +0 -415
- package/utils/featureManager.js +0 -68
- package/utils/firstTimeTip.js +0 -44
- package/utils/hook-manager.js +0 -125
- package/utils/http-logger.js +0 -264
- package/utils/i18n.js +0 -139
- package/utils/image-progress.js +0 -159
- package/utils/logger.js +0 -154
- package/utils/plugin-loader.js +0 -124
- package/utils/plugin-manager.js +0 -348
- package/utils/ray_cli_wrapper.js +0 -746
- package/utils/sandbox-client.js +0 -419
- package/utils/terminal.js +0 -32
- package/utils/tips.js +0 -106
package/utils/configManager.js
DELETED
|
@@ -1,503 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const os = require('os');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const logger = require('./logger');
|
|
5
|
-
|
|
6
|
-
class ConfigManager {
|
|
7
|
-
constructor() {
|
|
8
|
-
// 旧配置文件路径(用于向后兼容)
|
|
9
|
-
this.oldConfigDir = path.join(os.homedir(), '.rock-cli');
|
|
10
|
-
this.oldConfigPath = path.join(this.oldConfigDir, 'settings.json');
|
|
11
|
-
|
|
12
|
-
// 新配置文件路径(统一配置)
|
|
13
|
-
this.newConfigDir = path.join(os.homedir(), '.rock');
|
|
14
|
-
this.newConfigPath = path.join(this.newConfigDir, 'settings.json');
|
|
15
|
-
|
|
16
|
-
// 默认使用新配置路径
|
|
17
|
-
this.configPath = this.newConfigPath;
|
|
18
|
-
this.configDir = this.newConfigDir;
|
|
19
|
-
|
|
20
|
-
this.ensureConfigDir();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 确保配置目录存在
|
|
25
|
-
*/
|
|
26
|
-
ensureConfigDir() {
|
|
27
|
-
if (!fs.existsSync(this.configDir)) {
|
|
28
|
-
fs.mkdirpSync(this.configDir, { recursive: true });
|
|
29
|
-
logger.info(`Created config directory: ${this.configDir}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 读取配置 - 支持新旧格式
|
|
35
|
-
*/
|
|
36
|
-
readConfig() {
|
|
37
|
-
// 优先读取新配置文件
|
|
38
|
-
if (fs.existsSync(this.newConfigPath)) {
|
|
39
|
-
try {
|
|
40
|
-
const configData = fs.readJsonSync(this.newConfigPath);
|
|
41
|
-
return configData;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
logger.error(`Failed to read new config file: ${error.message}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 如果新配置文件不存在,尝试读取旧配置文件
|
|
48
|
-
if (fs.existsSync(this.oldConfigPath)) {
|
|
49
|
-
try {
|
|
50
|
-
const configData = fs.readJsonSync(this.oldConfigPath);
|
|
51
|
-
// 将旧格式转换为新格式
|
|
52
|
-
const newFormatConfig = this.convertOldToNewFormat(configData);
|
|
53
|
-
// 保存到新位置以实现迁移
|
|
54
|
-
this.saveToNewConfig(newFormatConfig);
|
|
55
|
-
return newFormatConfig;
|
|
56
|
-
} catch (error) {
|
|
57
|
-
logger.error(`Failed to read old config file: ${error.message}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* 将旧配置格式转换为新格式
|
|
66
|
-
*/
|
|
67
|
-
convertOldToNewFormat(oldConfig) {
|
|
68
|
-
const newConfig = { sandbox: {}, test: {} };
|
|
69
|
-
|
|
70
|
-
// 将旧配置映射到新格式的 sandbox 部分
|
|
71
|
-
for (const [key, value] of Object.entries(oldConfig)) {
|
|
72
|
-
if (key === 'base_url' || key === 'api_key' || key === 'cluster' ||
|
|
73
|
-
key === 'user_id' || key === 'experiment_id') {
|
|
74
|
-
newConfig.sandbox[key] = value;
|
|
75
|
-
} else if (key === 'private_token' || key === 'whale_apikey' ||
|
|
76
|
-
key === 'code_private_token' || key === 'whale_api_key') {
|
|
77
|
-
newConfig.test[key] = value;
|
|
78
|
-
} else {
|
|
79
|
-
// 其他配置项也放入 sandbox(作为默认)
|
|
80
|
-
newConfig.sandbox[key] = value;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return newConfig;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* 保存到新配置文件
|
|
89
|
-
*/
|
|
90
|
-
saveToNewConfig(config) {
|
|
91
|
-
try {
|
|
92
|
-
fs.writeJsonSync(this.newConfigPath, config, { spaces: 2 });
|
|
93
|
-
logger.info(`Config saved to: ${this.newConfigPath}`);
|
|
94
|
-
return true;
|
|
95
|
-
} catch (error) {
|
|
96
|
-
logger.error(`Failed to write new config file: ${error.message}`);
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 写入配置
|
|
103
|
-
*/
|
|
104
|
-
writeConfig(config) {
|
|
105
|
-
return this.saveToNewConfig(config);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 获取特定配置项(支持旧格式兼容)
|
|
110
|
-
*/
|
|
111
|
-
getConfig(key) {
|
|
112
|
-
const config = this.readConfig();
|
|
113
|
-
|
|
114
|
-
// 如果配置已经是新格式(有 sandbox 和 test 部分)
|
|
115
|
-
if (config.sandbox || config.test) {
|
|
116
|
-
// 检查 test 部分
|
|
117
|
-
if (config.test && config.test.hasOwnProperty(key)) {
|
|
118
|
-
return config.test[key];
|
|
119
|
-
}
|
|
120
|
-
// 检查 sandbox 部分
|
|
121
|
-
if (config.sandbox && config.sandbox.hasOwnProperty(key)) {
|
|
122
|
-
return config.sandbox[key];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 否则按旧格式处理
|
|
127
|
-
return config[key];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 设置特定配置项(支持新格式)
|
|
132
|
-
*/
|
|
133
|
-
setConfig(key, value) {
|
|
134
|
-
const config = this.readConfig();
|
|
135
|
-
|
|
136
|
-
// 检查是否是测试相关的配置键
|
|
137
|
-
const testKeys = ['private_token', 'whale_apikey', 'code_private_token', 'whale_api_key'];
|
|
138
|
-
if (testKeys.includes(key)) {
|
|
139
|
-
if (!config.test) config.test = {};
|
|
140
|
-
config.test[key] = value;
|
|
141
|
-
} else {
|
|
142
|
-
// 其他配置默认放在 sandbox 部分
|
|
143
|
-
if (!config.sandbox) config.sandbox = {};
|
|
144
|
-
config.sandbox[key] = value;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return this.writeConfig(config);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* 删除特定配置项
|
|
152
|
-
*/
|
|
153
|
-
deleteConfig(key) {
|
|
154
|
-
const config = this.readConfig();
|
|
155
|
-
|
|
156
|
-
if (config.test && config.test.hasOwnProperty(key)) {
|
|
157
|
-
delete config.test[key];
|
|
158
|
-
} else if (config.sandbox && config.sandbox.hasOwnProperty(key)) {
|
|
159
|
-
delete config.sandbox[key];
|
|
160
|
-
} else {
|
|
161
|
-
delete config[key];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return this.writeConfig(config);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* 设置沙箱配置
|
|
169
|
-
*/
|
|
170
|
-
setSandboxConfig(key, value) {
|
|
171
|
-
const config = this.readConfig();
|
|
172
|
-
if (!config.sandbox) config.sandbox = {};
|
|
173
|
-
config.sandbox[key] = value;
|
|
174
|
-
return this.writeConfig(config);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* 获取沙箱配置
|
|
179
|
-
*/
|
|
180
|
-
getSandboxConfig(key) {
|
|
181
|
-
const config = this.readConfig();
|
|
182
|
-
if (config.sandbox && config.sandbox.hasOwnProperty(key)) {
|
|
183
|
-
return config.sandbox[key];
|
|
184
|
-
}
|
|
185
|
-
return undefined;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* 获取 UI 配置
|
|
190
|
-
*/
|
|
191
|
-
getUIConfig(key, defaultValue = undefined) {
|
|
192
|
-
const config = this.readConfig();
|
|
193
|
-
if (config.ui && Object.prototype.hasOwnProperty.call(config.ui, key)) {
|
|
194
|
-
return config.ui[key];
|
|
195
|
-
}
|
|
196
|
-
return defaultValue;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* 设置 UI 配置
|
|
201
|
-
*/
|
|
202
|
-
setUIConfig(key, value) {
|
|
203
|
-
const config = this.readConfig();
|
|
204
|
-
if (!config.ui) config.ui = {};
|
|
205
|
-
config.ui[key] = value;
|
|
206
|
-
return this.writeConfig(config);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* 设置测试配置
|
|
211
|
-
*/
|
|
212
|
-
setTestConfig(key, value) {
|
|
213
|
-
const config = this.readConfig();
|
|
214
|
-
if (!config.test) config.test = {};
|
|
215
|
-
config.test[key] = value;
|
|
216
|
-
return this.writeConfig(config);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* 获取测试配置
|
|
221
|
-
*/
|
|
222
|
-
getTestConfig(key) {
|
|
223
|
-
const config = this.readConfig();
|
|
224
|
-
if (config.test && config.test.hasOwnProperty(key)) {
|
|
225
|
-
return config.test[key];
|
|
226
|
-
}
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* 获取配置路径
|
|
232
|
-
*/
|
|
233
|
-
getConfigPath() {
|
|
234
|
-
return this.configPath;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Get API key with priority: ROCK_API_KEY env > CLI arg > config file
|
|
239
|
-
* @param {Object} argv - Command line arguments
|
|
240
|
-
* @returns {string|null} - API key or null if not found
|
|
241
|
-
*/
|
|
242
|
-
getApiKeyWithPriority(argv = {}) {
|
|
243
|
-
// Priority 1: Environment variable
|
|
244
|
-
if (process.env.ROCK_API_KEY) {
|
|
245
|
-
return process.env.ROCK_API_KEY;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Priority 2: CLI argument
|
|
249
|
-
if (argv.apiKey) {
|
|
250
|
-
return argv.apiKey;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Priority 3: Config file
|
|
254
|
-
const config = this.readConfig();
|
|
255
|
-
if (config?.sandbox?.api_key) {
|
|
256
|
-
return config.sandbox.api_key;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Get cluster with priority: ROCK_CLUSTER env > CLI arg > config file > default
|
|
264
|
-
* @param {Object} argv - Command line arguments
|
|
265
|
-
* @returns {string} - Cluster or default 'zb'
|
|
266
|
-
*/
|
|
267
|
-
getClusterWithPriority(argv = {}) {
|
|
268
|
-
// Priority 1: Environment variable
|
|
269
|
-
if (process.env.ROCK_CLUSTER) {
|
|
270
|
-
return process.env.ROCK_CLUSTER;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Priority 2: CLI argument
|
|
274
|
-
if (argv.cluster) {
|
|
275
|
-
return argv.cluster;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Priority 3: Config file
|
|
279
|
-
const config = this.readConfig();
|
|
280
|
-
if (config?.sandbox?.cluster) {
|
|
281
|
-
return config.sandbox.cluster;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Default value
|
|
285
|
-
return 'zb';
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Get user ID with priority: ROCK_USER_ID env > CLI arg > config file
|
|
290
|
-
* @param {Object} argv - Command line arguments
|
|
291
|
-
* @returns {string|null} - User ID or null if not found
|
|
292
|
-
*/
|
|
293
|
-
getUserIdWithPriority(argv = {}) {
|
|
294
|
-
// Priority 1: Environment variable
|
|
295
|
-
if (process.env.ROCK_USER_ID) {
|
|
296
|
-
return process.env.ROCK_USER_ID;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Priority 2: CLI argument
|
|
300
|
-
if (argv.userId) {
|
|
301
|
-
return argv.userId;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Priority 3: Config file
|
|
305
|
-
const config = this.readConfig();
|
|
306
|
-
if (config?.sandbox?.user_id) {
|
|
307
|
-
return config.sandbox.user_id;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Get experiment ID with priority: ROCK_EXPERIMENT_ID env > CLI arg > config file
|
|
315
|
-
* @param {Object} argv - Command line arguments
|
|
316
|
-
* @returns {string|null} - Experiment ID or null if not found
|
|
317
|
-
*/
|
|
318
|
-
getExperimentIdWithPriority(argv = {}) {
|
|
319
|
-
// Priority 1: Environment variable
|
|
320
|
-
if (process.env.ROCK_EXPERIMENT_ID) {
|
|
321
|
-
return process.env.ROCK_EXPERIMENT_ID;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Priority 2: CLI argument
|
|
325
|
-
if (argv.experimentId) {
|
|
326
|
-
return argv.experimentId;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Priority 3: Config file
|
|
330
|
-
const config = this.readConfig();
|
|
331
|
-
if (config?.sandbox?.experiment_id) {
|
|
332
|
-
return config.sandbox.experiment_id;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return null;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* 获取脱敏后的配置(用于显示)
|
|
340
|
-
*/
|
|
341
|
-
getMaskedConfig() {
|
|
342
|
-
const config = this.readConfig();
|
|
343
|
-
|
|
344
|
-
// 如果是新格式,对各部分分别脱敏
|
|
345
|
-
if (config.sandbox || config.test) {
|
|
346
|
-
const maskedConfig = {};
|
|
347
|
-
if (config.sandbox) {
|
|
348
|
-
maskedConfig.sandbox = {};
|
|
349
|
-
for (const [key, value] of Object.entries(config.sandbox)) {
|
|
350
|
-
if (typeof value === 'string' && (key.toLowerCase().includes('token') || key.toLowerCase().includes('key') || key.toLowerCase().includes('secret'))) {
|
|
351
|
-
maskedConfig.sandbox[key] = this.maskValue(value);
|
|
352
|
-
} else {
|
|
353
|
-
maskedConfig.sandbox[key] = value;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
if (config.test) {
|
|
358
|
-
maskedConfig.test = {};
|
|
359
|
-
for (const [key, value] of Object.entries(config.test)) {
|
|
360
|
-
if (typeof value === 'string' && (key.toLowerCase().includes('token') || key.toLowerCase().includes('key') || key.toLowerCase().includes('secret'))) {
|
|
361
|
-
maskedConfig.test[key] = this.maskValue(value);
|
|
362
|
-
} else {
|
|
363
|
-
maskedConfig.test[key] = value;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return maskedConfig;
|
|
368
|
-
} else {
|
|
369
|
-
// 旧格式处理
|
|
370
|
-
const maskedConfig = {};
|
|
371
|
-
for (const [key, value] of Object.entries(config)) {
|
|
372
|
-
if (typeof value === 'string' && (key.toLowerCase().includes('token') || key.toLowerCase().includes('key'))) {
|
|
373
|
-
maskedConfig[key] = this.maskValue(value);
|
|
374
|
-
} else {
|
|
375
|
-
maskedConfig[key] = value;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return maskedConfig;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* 脱敏处理
|
|
384
|
-
*/
|
|
385
|
-
maskValue(value) {
|
|
386
|
-
if (typeof value !== 'string') {
|
|
387
|
-
return value;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (value.length <= 8) {
|
|
391
|
-
return '*'.repeat(Math.max(value.length, 4));
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// 保留前2位和后2位,中间用星号代替
|
|
395
|
-
return value.substring(0, 2) + '*'.repeat(value.length - 4) + value.substring(value.length - 2);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* 获取插件配置
|
|
400
|
-
* @param {string} pluginName - 插件名称
|
|
401
|
-
* @returns {Object} - 插件配置对象
|
|
402
|
-
*/
|
|
403
|
-
getPluginConfig(pluginName) {
|
|
404
|
-
const config = this.readConfig();
|
|
405
|
-
const plugins = config.plugins || [];
|
|
406
|
-
|
|
407
|
-
// 旧格式:数组
|
|
408
|
-
if (Array.isArray(plugins)) {
|
|
409
|
-
return {}; // 旧格式不支持配置
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// 新格式:对象
|
|
413
|
-
const pluginConfig = plugins[pluginName];
|
|
414
|
-
return pluginConfig?.config || {};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* 获取所有插件配置(支持新旧格式转换)
|
|
419
|
-
* @returns {Object} - 插件配置对象
|
|
420
|
-
*/
|
|
421
|
-
getPlugins() {
|
|
422
|
-
const config = this.readConfig();
|
|
423
|
-
const plugins = config.plugins || [];
|
|
424
|
-
|
|
425
|
-
// 旧格式:转换为新格式
|
|
426
|
-
if (Array.isArray(plugins)) {
|
|
427
|
-
return plugins.reduce((acc, path, index) => {
|
|
428
|
-
const name = `plugin-${index}`;
|
|
429
|
-
acc[name] = {
|
|
430
|
-
path: path,
|
|
431
|
-
config: {},
|
|
432
|
-
enabled: true
|
|
433
|
-
};
|
|
434
|
-
return acc;
|
|
435
|
-
}, {});
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// 新格式:直接返回
|
|
439
|
-
return plugins;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* 检查插件是否启用
|
|
444
|
-
* @param {string} pluginName - 插件名称
|
|
445
|
-
* @returns {boolean} - 是否启用
|
|
446
|
-
*/
|
|
447
|
-
isPluginEnabled(pluginName) {
|
|
448
|
-
const plugins = this.getPlugins();
|
|
449
|
-
const pluginConfig = plugins[pluginName];
|
|
450
|
-
|
|
451
|
-
// 插件不存在,返回 false
|
|
452
|
-
if (!pluginConfig) {
|
|
453
|
-
return false;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// enabled 字段不存在,默认启用
|
|
457
|
-
if (pluginConfig.enabled === undefined) {
|
|
458
|
-
return true;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// 返回 enabled 的值
|
|
462
|
-
return pluginConfig.enabled === true;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* 设置插件配置(兼容旧格式)
|
|
467
|
-
* @param {Array<string>} plugins - 插件路径数组(旧格式)
|
|
468
|
-
* @returns {boolean}
|
|
469
|
-
*/
|
|
470
|
-
setPlugins(plugins) {
|
|
471
|
-
const config = this.readConfig();
|
|
472
|
-
config.plugins = plugins;
|
|
473
|
-
return this.writeConfig(config);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* 获取全局配置(带优先级合并)
|
|
478
|
-
* @param {Object} argv - 命令行参数对象
|
|
479
|
-
* @returns {Object} - 合并后的全局配置
|
|
480
|
-
*/
|
|
481
|
-
getGlobalConfigWithPriority(argv = {}) {
|
|
482
|
-
const config = this.readConfig();
|
|
483
|
-
const sandboxConfig = config.sandbox || {};
|
|
484
|
-
|
|
485
|
-
// 实现优先级合并逻辑
|
|
486
|
-
const mergedConfig = {
|
|
487
|
-
...config,
|
|
488
|
-
sandbox: {
|
|
489
|
-
...sandboxConfig,
|
|
490
|
-
// 使用优先级函数获取所有参数
|
|
491
|
-
api_key: this.getApiKeyWithPriority(argv),
|
|
492
|
-
base_url: argv.baseUrl || sandboxConfig.base_url,
|
|
493
|
-
cluster: this.getClusterWithPriority(argv),
|
|
494
|
-
user_id: this.getUserIdWithPriority(argv),
|
|
495
|
-
experiment_id: this.getExperimentIdWithPriority(argv)
|
|
496
|
-
}
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
return mergedConfig;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
module.exports = new ConfigManager();
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
const logger = require('./logger');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 依赖关系解析器
|
|
5
|
-
* 用于解析插件加载顺序,检测循环依赖
|
|
6
|
-
*/
|
|
7
|
-
class DependencyResolver {
|
|
8
|
-
/**
|
|
9
|
-
* 解析插件加载顺序
|
|
10
|
-
* @param {Array<{name: string, dependsOn?: string[]}>} plugins - 插件列表
|
|
11
|
-
* @returns {Array<string>} - 加载顺序(插件名称数组)
|
|
12
|
-
* @throws {Error} - 如果存在循环依赖
|
|
13
|
-
*/
|
|
14
|
-
resolveLoadOrder(plugins) {
|
|
15
|
-
if (!plugins || plugins.length === 0) {
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// 构建依赖图
|
|
20
|
-
const graph = {};
|
|
21
|
-
const allPlugins = new Set();
|
|
22
|
-
|
|
23
|
-
plugins.forEach(plugin => {
|
|
24
|
-
const { name, dependsOn = [] } = plugin;
|
|
25
|
-
allPlugins.add(name);
|
|
26
|
-
|
|
27
|
-
if (!graph[name]) {
|
|
28
|
-
graph[name] = [];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
dependsOn.forEach(dep => {
|
|
32
|
-
graph[name].push(dep);
|
|
33
|
-
allPlugins.add(dep);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// 确保所有插件都在图中
|
|
38
|
-
allPlugins.forEach(name => {
|
|
39
|
-
if (!graph[name]) {
|
|
40
|
-
graph[name] = [];
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// 检测环
|
|
45
|
-
if (this._hasCycle(graph)) {
|
|
46
|
-
throw new Error('Circular dependency detected in plugins');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// 拓扑排序
|
|
50
|
-
return this._topologicalSort(graph);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 检测依赖图中是否存在环
|
|
55
|
-
* @private
|
|
56
|
-
*/
|
|
57
|
-
_hasCycle(graph) {
|
|
58
|
-
const visited = new Set();
|
|
59
|
-
const recursionStack = new Set();
|
|
60
|
-
|
|
61
|
-
const dfs = (node) => {
|
|
62
|
-
if (recursionStack.has(node)) {
|
|
63
|
-
return true; // 发现环
|
|
64
|
-
}
|
|
65
|
-
if (visited.has(node)) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
visited.add(node);
|
|
70
|
-
recursionStack.add(node);
|
|
71
|
-
|
|
72
|
-
const neighbors = graph[node] || [];
|
|
73
|
-
for (const neighbor of neighbors) {
|
|
74
|
-
if (dfs(neighbor)) {
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
recursionStack.delete(node);
|
|
80
|
-
return false;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
for (const node of Object.keys(graph)) {
|
|
84
|
-
if (!visited.has(node)) {
|
|
85
|
-
if (dfs(node)) {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 拓扑排序
|
|
96
|
-
* @private
|
|
97
|
-
*/
|
|
98
|
-
_topologicalSort(graph) {
|
|
99
|
-
const outDegree = {};
|
|
100
|
-
const queue = [];
|
|
101
|
-
const result = [];
|
|
102
|
-
|
|
103
|
-
// 计算出度(这个节点依赖多少个节点)
|
|
104
|
-
for (const node of Object.keys(graph)) {
|
|
105
|
-
outDegree[node] = (graph[node] || []).length;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 将出度为 0 的节点(不依赖任何节点的)加入队列
|
|
109
|
-
for (const node of Object.keys(outDegree)) {
|
|
110
|
-
if (outDegree[node] === 0) {
|
|
111
|
-
queue.push(node);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// 拓扑排序
|
|
116
|
-
while (queue.length > 0) {
|
|
117
|
-
const node = queue.shift();
|
|
118
|
-
result.push(node);
|
|
119
|
-
|
|
120
|
-
// node 已经加载完成,减少依赖 node 的节点的出度
|
|
121
|
-
for (const dependent of Object.keys(graph)) {
|
|
122
|
-
if (graph[dependent].includes(node)) {
|
|
123
|
-
outDegree[dependent]--;
|
|
124
|
-
if (outDegree[dependent] === 0) {
|
|
125
|
-
queue.push(dependent);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return result;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
module.exports = DependencyResolver;
|