@hecom/codearts 0.2.1 → 0.2.3

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.
@@ -40,11 +40,13 @@ exports.configCommand = configCommand;
40
40
  exports.updateProjectConfigCommand = updateProjectConfigCommand;
41
41
  exports.getAvailableProjectConfigs = getAvailableProjectConfigs;
42
42
  exports.showConfigCommand = showConfigCommand;
43
- const inquirer_1 = __importDefault(require("inquirer"));
43
+ const prompts_1 = require("@inquirer/prompts");
44
+ const picocolors_1 = __importDefault(require("picocolors"));
44
45
  const readline = __importStar(require("readline"));
45
46
  const business_service_1 = require("../services/business.service");
46
47
  const types_1 = require("../types");
47
48
  const config_loader_1 = require("../utils/config-loader");
49
+ const logger_1 = require("../utils/logger");
48
50
  /**
49
51
  * 清除终端上指定行数的内容
50
52
  * @param lines 要清除的行数
@@ -55,76 +57,71 @@ function clearLines(lines) {
55
57
  readline.clearLine(process.stdout, 0); // 清除当前行
56
58
  }
57
59
  }
60
+ async function inputRoleIds(existingValue) {
61
+ return await (0, prompts_1.input)({
62
+ message: '角色 ID(支持逗号分隔,如: 1,2,3):',
63
+ default: existingValue || '',
64
+ validate: (inputValue) => {
65
+ if (!inputValue.trim()) {
66
+ return '角色 ID 不能为空';
67
+ }
68
+ const ids = inputValue.split(',').map((id) => id.trim());
69
+ const allValid = ids.every((id) => /^\d+$/.test(id));
70
+ return allValid ? true : '角色 ID 必须是数字或逗号分隔的数字列表';
71
+ },
72
+ });
73
+ }
58
74
  /**
59
75
  * 配置角色 ID
60
76
  */
61
77
  async function configureRoleIds(businessService, projectId, existingValue) {
78
+ let roles = [];
62
79
  try {
63
- const roles = await businessService.getProjectRoles(projectId);
64
- if (roles.length === 0) {
65
- // 如果没有获取到角色,使用手动输入
66
- const { manualRoleId } = await inquirer_1.default.prompt([
67
- {
68
- type: 'input',
69
- name: 'manualRoleId',
70
- message: '角色 ID(支持逗号分隔,如: 1,2,3):',
71
- default: existingValue || '',
72
- validate: (input) => {
73
- if (!input.trim()) {
74
- return '角色 ID 不能为空';
75
- }
76
- const ids = input.split(',').map((id) => id.trim());
77
- const allValid = ids.every((id) => /^\d+$/.test(id));
78
- return allValid ? true : '角色 ID 必须是数字或逗号分隔的数字列表';
79
- },
80
- },
81
- ]);
82
- return manualRoleId;
83
- }
84
- else {
85
- // 使用多选框选择角色
86
- const roleChoices = roles.map((role) => ({
87
- name: `${role.role_name} (${role.role_id})`,
88
- value: role.role_id.toString(),
89
- checked: existingValue ? existingValue.split(',').includes(role.role_id.toString()) : false,
90
- }));
91
- const { selectedRoleIds } = await inquirer_1.default.prompt([
92
- {
93
- type: 'checkbox',
94
- name: 'selectedRoleIds',
95
- message: '请选择角色:',
96
- choices: roleChoices,
97
- validate: (input) => {
98
- if (input.length === 0) {
99
- return '至少选择一个角色';
100
- }
101
- return true;
102
- },
103
- },
104
- ]);
105
- return selectedRoleIds.join(',');
106
- }
80
+ roles = await businessService.getProjectRoles(projectId);
107
81
  }
108
82
  catch (error) {
109
- console.error('❌ 获取角色列表失败:', error);
83
+ logger_1.logger.error('❌ 获取角色列表失败:', error);
110
84
  // 如果获取失败,使用手动输入
111
- const { manualRoleId } = await inquirer_1.default.prompt([
112
- {
113
- type: 'input',
114
- name: 'manualRoleId',
115
- message: '角色 ID(支持逗号分隔,如: 1,2,3):',
116
- default: existingValue || '',
117
- validate: (input) => {
118
- if (!input.trim()) {
119
- return '角色 ID 不能为空';
120
- }
121
- const ids = input.split(',').map((id) => id.trim());
122
- const allValid = ids.every((id) => /^\d+$/.test(id));
123
- return allValid ? true : '角色 ID 必须是数字或逗号分隔的数字列表';
85
+ return await inputRoleIds(existingValue);
86
+ }
87
+ if (roles.length === 0) {
88
+ // 如果没有获取到角色,使用手动输入
89
+ return await inputRoleIds(existingValue);
90
+ }
91
+ else {
92
+ // 使用多选框选择角色
93
+ const roleChoices = roles.map((role) => ({
94
+ name: `${role.role_name} (${role.role_id})`,
95
+ value: role.role_id.toString(),
96
+ checked: existingValue ? existingValue.split(',').includes(role.role_id.toString()) : false,
97
+ }));
98
+ const selectedRoleIds = await (0, prompts_1.checkbox)({
99
+ message: '请选择角色:',
100
+ choices: roleChoices,
101
+ validate: (answer) => {
102
+ if (answer.length === 0) {
103
+ return '至少需要选择一个角色';
104
+ }
105
+ return true;
106
+ },
107
+ theme: {
108
+ style: {
109
+ help: (text) => `\x1b[90m${123}\x1b[0m`, // 灰色
110
+ keysHelpTip: (keys) => {
111
+ const actionMap = {
112
+ navigate: '上下移动',
113
+ select: '选择/取消',
114
+ all: '全选',
115
+ invert: '反选',
116
+ submit: '提交',
117
+ };
118
+ const tips = keys.map(([key, action]) => `${key} \x1b[90m${actionMap[action] || action}\x1b[0m`);
119
+ return tips.join(' • ');
120
+ },
124
121
  },
125
122
  },
126
- ]);
127
- return manualRoleId;
123
+ });
124
+ return selectedRoleIds.join(',');
128
125
  }
129
126
  }
130
127
  /**
@@ -144,27 +141,30 @@ const PROJECT_CONFIG_ITEMS = [
144
141
  // configure: configureCustomField,
145
142
  // },
146
143
  ];
144
+ async function inputPassword() {
145
+ return await (0, prompts_1.password)({
146
+ message: 'IAM 密码:',
147
+ mask: '*',
148
+ validate: (inputValue) => (inputValue.trim() ? true : 'IAM 密码不能为空'),
149
+ });
150
+ }
147
151
  /**
148
152
  * 交互式配置向导命令
149
153
  * 引导用户创建或更新全局配置文件
150
154
  */
151
155
  async function configCommand() {
152
- console.log('\n欢迎使用 Hecom CodeArts 配置向导');
153
- console.log('='.repeat(60));
154
- console.log('此向导将帮助您配置华为云 CodeArts API 访问凭证。');
155
- console.log(`配置将保存到: ${(0, config_loader_1.getGlobalConfigPath)()}\n`);
156
- const existingConfig = (0, config_loader_1.globalConfigExists)() ? (0, config_loader_1.readGlobalConfig)() : {};
157
- if ((0, config_loader_1.globalConfigExists)()) {
158
- const { overwrite } = await inquirer_1.default.prompt([
159
- {
160
- type: 'confirm',
161
- name: 'overwrite',
162
- message: '检测到已存在全局配置,是否覆盖?',
163
- default: false,
164
- },
165
- ]);
156
+ logger_1.logger.info('欢迎使用 Hecom CodeArts 配置向导');
157
+ logger_1.logger.info('='.repeat(60));
158
+ logger_1.logger.info('此向导将帮助您配置华为云 CodeArts API 访问凭证以及项目相关设置');
159
+ logger_1.logger.info(`配置将保存到: ${(0, config_loader_1.getConfigPath)()}\n`);
160
+ const existingConfig = (0, config_loader_1.configExists)() ? (0, config_loader_1.readConfig)() : {};
161
+ if ((0, config_loader_1.configExists)()) {
162
+ const overwrite = await (0, prompts_1.confirm)({
163
+ message: '检测到已存在全局配置,是否覆盖?',
164
+ default: true,
165
+ });
166
166
  if (!overwrite) {
167
- console.log('\n已取消配置。');
167
+ logger_1.logger.info('\n已取消配置。');
168
168
  return;
169
169
  }
170
170
  }
@@ -180,53 +180,52 @@ async function configCommand() {
180
180
  password: '',
181
181
  };
182
182
  while (!credentialsValid) {
183
- iamAnswers = await inquirer_1.default.prompt([
184
- {
185
- type: 'input',
186
- name: 'iamEndpoint',
183
+ iamAnswers = {
184
+ iamEndpoint: await (0, prompts_1.input)({
187
185
  message: 'IAM 认证端点:',
188
186
  default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT] ||
189
187
  'https://iam.cn-north-4.myhuaweicloud.com',
190
- validate: (input) => (input.trim() ? true : 'IAM 认证端点不能为空'),
191
- },
192
- {
193
- type: 'input',
194
- name: 'region',
188
+ validate: (inputValue) => (inputValue.trim() ? true : 'IAM 认证端点不能为空'),
189
+ }),
190
+ region: await (0, prompts_1.input)({
195
191
  message: '华为云区域:',
196
192
  default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_REGION] || 'cn-north-4',
197
- validate: (input) => (input.trim() ? true : '华为云区域不能为空'),
198
- },
199
- {
200
- type: 'input',
201
- name: 'codeartsUrl',
193
+ validate: (inputValue) => (inputValue.trim() ? true : '华为云区域不能为空'),
194
+ }),
195
+ codeartsUrl: await (0, prompts_1.input)({
202
196
  message: 'CodeArts API 地址:',
203
197
  default: existingConfig[types_1.ConfigKey.CODEARTS_BASE_URL] ||
204
198
  'https://projectman-ext.cn-north-4.myhuaweicloud.cn',
205
- validate: (input) => (input.trim() ? true : 'CodeArts API 地址不能为空'),
206
- },
207
- {
208
- type: 'input',
209
- name: 'domain',
199
+ validate: (inputValue) => (inputValue.trim() ? true : 'CodeArts API 地址不能为空'),
200
+ }),
201
+ domain: await (0, prompts_1.input)({
210
202
  message: '华为云账号名:',
211
203
  default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_DOMAIN] || '',
212
- validate: (input) => (input.trim() ? true : '华为云账号名不能为空'),
213
- },
214
- {
215
- type: 'input',
216
- name: 'username',
204
+ validate: (inputValue) => (inputValue.trim() ? true : '华为云账号名不能为空'),
205
+ }),
206
+ username: await (0, prompts_1.input)({
217
207
  message: 'IAM 用户名:',
218
208
  default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_USERNAME] || '',
219
- validate: (input) => (input.trim() ? true : 'IAM 用户名不能为空'),
220
- },
221
- {
222
- type: 'password',
223
- name: 'password',
224
- message: 'IAM 密码:',
225
- mask: '*',
226
- default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD] || '',
227
- validate: (input) => (input.trim() ? true : 'IAM 密码不能为空'),
228
- },
229
- ]);
209
+ validate: (inputValue) => (inputValue.trim() ? true : 'IAM 用户名不能为空'),
210
+ }),
211
+ password: '',
212
+ };
213
+ // 处理密码:如果存在旧密码,询问是否重用
214
+ if (existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD]) {
215
+ const useExistingPassword = await (0, prompts_1.confirm)({
216
+ message: 'IAM 密码: 是否使用已保存的密码?',
217
+ default: true,
218
+ });
219
+ if (useExistingPassword) {
220
+ iamAnswers.password = existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD];
221
+ }
222
+ else {
223
+ iamAnswers.password = await inputPassword();
224
+ }
225
+ }
226
+ else {
227
+ iamAnswers.password = await inputPassword();
228
+ }
230
229
  // 创建 BusinessService 实例
231
230
  businessService = new business_service_1.BusinessService({
232
231
  iamEndpoint: iamAnswers.iamEndpoint,
@@ -244,17 +243,13 @@ async function configCommand() {
244
243
  }
245
244
  else {
246
245
  const errorMessage = `❌ IAM 凭证验证失败: ${validationResult.error}\n`;
247
- console.error(errorMessage);
248
- const { retry } = await inquirer_1.default.prompt([
249
- {
250
- type: 'confirm',
251
- name: 'retry',
252
- message: '是否重新配置 IAM 凭证?',
253
- default: true,
254
- },
255
- ]);
246
+ logger_1.logger.error(errorMessage);
247
+ const retry = await (0, prompts_1.confirm)({
248
+ message: '是否重新配置 IAM 凭证?',
249
+ default: true,
250
+ });
256
251
  if (!retry) {
257
- console.log('\n已取消配置。');
252
+ logger_1.logger.info('\n已取消配置。');
258
253
  return;
259
254
  }
260
255
  // 计算需要清除的行数
@@ -266,51 +261,39 @@ async function configCommand() {
266
261
  }
267
262
  // 第二阶段:获取项目列表并选择项目
268
263
  let projectId;
264
+ let projects = [];
265
+ async function inputProjectId() {
266
+ return await (0, prompts_1.input)({
267
+ message: '项目 ID:',
268
+ default: existingConfig[types_1.ConfigKey.PROJECT_ID] || '',
269
+ validate: (inputValue) => (inputValue.trim() ? true : '项目 ID 不能为空'),
270
+ });
271
+ }
269
272
  try {
270
- const projects = await businessService.getProjects(100);
271
- if (projects.length === 0) {
272
- const { manualProjectId } = await inquirer_1.default.prompt([
273
- {
274
- type: 'input',
275
- name: 'manualProjectId',
276
- message: '项目 ID:',
277
- default: existingConfig[types_1.ConfigKey.PROJECT_ID] || '',
278
- validate: (input) => (input.trim() ? true : '项目 ID 不能为空'),
279
- },
280
- ]);
281
- projectId = manualProjectId;
282
- }
283
- else {
284
- const projectChoices = projects.map((p) => ({
285
- name: `${p.project_name} (${p.project_id})`,
286
- value: p.project_id,
287
- }));
288
- const { selectedProjectId } = await inquirer_1.default.prompt([
289
- {
290
- type: 'list',
291
- name: 'selectedProjectId',
292
- message: '请选择项目:',
293
- choices: projectChoices,
294
- default: existingConfig[types_1.ConfigKey.PROJECT_ID] || projectChoices[0]?.value,
295
- },
296
- ]);
297
- projectId = selectedProjectId;
298
- }
273
+ projects = await businessService.getProjects(100);
299
274
  }
300
275
  catch (error) {
301
- const errorMsg = error instanceof Error ? error.message : String(error);
302
- console.error('❌ 获取项目列表失败:', errorMsg, '\n');
276
+ logger_1.logger.error(`❌ 获取项目列表失败: `, error);
303
277
  // 获取失败,使用手动输入
304
- const { manualProjectId } = await inquirer_1.default.prompt([
305
- {
306
- type: 'input',
307
- name: 'manualProjectId',
308
- message: '项目 ID:',
309
- default: existingConfig[types_1.ConfigKey.PROJECT_ID] || '',
310
- validate: (input) => (input.trim() ? true : '项目 ID 不能为空'),
311
- },
312
- ]);
313
- projectId = manualProjectId;
278
+ projectId = await inputProjectId();
279
+ }
280
+ if (projects.length === 0) {
281
+ projectId = await inputProjectId();
282
+ }
283
+ else {
284
+ const projectChoices = projects.map((p) => ({
285
+ name: `${p.project_name} (${p.project_id})`,
286
+ value: p.project_id,
287
+ }));
288
+ // 确定默认项目 ID
289
+ const existingProjectId = existingConfig[types_1.ConfigKey.PROJECT_ID];
290
+ const isExistingProjectValid = existingProjectId && projects.some((p) => p.project_id === existingProjectId);
291
+ const defaultProjectId = isExistingProjectValid ? existingProjectId : projectChoices[0]?.value;
292
+ projectId = await (0, prompts_1.select)({
293
+ message: '请选择项目:',
294
+ choices: projectChoices,
295
+ default: defaultProjectId,
296
+ });
314
297
  }
315
298
  // 第三阶段:配置项目相关配置
316
299
  const projectConfigs = {};
@@ -330,13 +313,13 @@ async function configCommand() {
330
313
  ...projectConfigs,
331
314
  };
332
315
  try {
333
- (0, config_loader_1.writeGlobalConfig)(finalConfig);
334
- console.log('\n✅ 全局配置已成功保存');
335
- console.log(`配置文件位置: ${(0, config_loader_1.getGlobalConfigPath)()}`);
336
- console.log('\n提示:配置文件包含敏感信息,请妥善保管。');
316
+ (0, config_loader_1.writeConfig)(finalConfig);
317
+ logger_1.logger.success('\n✅ 全局配置已成功保存');
318
+ logger_1.logger.info(`配置文件位置: ${(0, config_loader_1.getConfigPath)()}`);
319
+ logger_1.logger.info('\n提示:配置文件包含敏感信息,请妥善保管。');
337
320
  }
338
321
  catch (error) {
339
- console.error('\n❌ 保存配置文件失败:', error);
322
+ logger_1.logger.error('\n❌ 保存配置文件失败:', error);
340
323
  process.exit(1);
341
324
  }
342
325
  }
@@ -346,32 +329,22 @@ async function configCommand() {
346
329
  */
347
330
  async function updateProjectConfigCommand(configKey) {
348
331
  // 检查配置文件是否存在
349
- if (!(0, config_loader_1.globalConfigExists)()) {
350
- console.error('\n❌ 全局配置文件不存在,请先运行 `npx @hecom/codearts config` 创建配置。');
332
+ if (!(0, config_loader_1.configExists)()) {
333
+ logger_1.logger.error('\n❌ 全局配置文件不存在,请先运行 `npx @hecom/codearts config` 创建配置。');
351
334
  process.exit(1);
352
335
  }
353
336
  // 查找对应的配置项
354
337
  const configItem = PROJECT_CONFIG_ITEMS.find((item) => item.key === configKey);
355
338
  if (!configItem) {
356
- console.error(`\n❌ 未知的配置项: ${configKey}`);
357
- console.log(`\n可用的配置项:`);
339
+ logger_1.logger.error(`\n❌ 未知的配置项: ${configKey}`);
340
+ logger_1.logger.info(`\n可用的配置项:`);
358
341
  PROJECT_CONFIG_ITEMS.forEach((item) => {
359
- console.log(` - ${item.key}: ${item.label}`);
342
+ logger_1.logger.info(` - ${item.key}: ${item.label}`);
360
343
  });
361
344
  process.exit(1);
362
345
  }
363
346
  // 读取现有配置
364
- const existingConfig = (0, config_loader_1.readGlobalConfig)();
365
- // 检查必要的配置是否存在
366
- if (!existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_USERNAME] ||
367
- !existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD]) {
368
- console.error('\n❌ 全局配置不完整,请先运行 `npx @hecom/codearts config` 完成配置。');
369
- process.exit(1);
370
- }
371
- if (!existingConfig[types_1.ConfigKey.PROJECT_ID]) {
372
- console.error('\n❌ 项目 ID 未配置,请先运行 `npx @hecom/codearts config` 完成配置。');
373
- process.exit(1);
374
- }
347
+ const existingConfig = (0, config_loader_1.readConfig)();
375
348
  // 创建 BusinessService 实例
376
349
  const businessService = new business_service_1.BusinessService({
377
350
  iamEndpoint: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT],
@@ -390,12 +363,12 @@ async function updateProjectConfigCommand(configKey) {
390
363
  [configKey]: newValue,
391
364
  };
392
365
  try {
393
- (0, config_loader_1.writeGlobalConfig)(updatedConfig);
394
- console.log(`\n✅ ${configItem.label}已成功更新`);
395
- console.log(`配置文件位置: ${(0, config_loader_1.getGlobalConfigPath)()}`);
366
+ (0, config_loader_1.writeConfig)(updatedConfig);
367
+ logger_1.logger.success(`\n✅ ${configItem.label}已成功更新`);
368
+ logger_1.logger.info(`配置文件位置: ${(0, config_loader_1.getConfigPath)()}`);
396
369
  }
397
370
  catch (error) {
398
- console.error('\n❌ 保存配置文件失败:', error);
371
+ logger_1.logger.error('\n❌ 保存配置文件失败:', error);
399
372
  process.exit(1);
400
373
  }
401
374
  }
@@ -413,7 +386,7 @@ async function showConfigCommand() {
413
386
  // 获取最终合并后的配置
414
387
  const config = (0, config_loader_1.getConfig)();
415
388
  // 按类别显示配置
416
- console.log('\n【华为云 IAM 凭证】');
389
+ logger_1.logger.info(picocolors_1.default.cyan('【华为云 IAM 凭证】'));
417
390
  const iamKeys = [
418
391
  types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT,
419
392
  types_1.ConfigKey.HUAWEI_CLOUD_REGION,
@@ -424,9 +397,10 @@ async function showConfigCommand() {
424
397
  for (const key of iamKeys) {
425
398
  const value = config[key] || '(未配置)';
426
399
  const displayValue = key.includes('PASSWORD') && value !== '(未配置)' ? '********' : value;
427
- console.log(` ${formatKeyName(key)}: ${displayValue}`);
400
+ logger_1.logger.info(` ${formatKeyName(key)}: ${displayValue}`);
428
401
  }
429
- console.log('\n【CodeArts 配置】');
402
+ logger_1.logger.info();
403
+ logger_1.logger.info(picocolors_1.default.cyanBright('【CodeArts 配置】'));
430
404
  const codeartsKeys = [
431
405
  types_1.ConfigKey.CODEARTS_BASE_URL,
432
406
  types_1.ConfigKey.PROJECT_ID,
@@ -434,7 +408,7 @@ async function showConfigCommand() {
434
408
  ];
435
409
  for (const key of codeartsKeys) {
436
410
  const value = config[key] || '(未配置)';
437
- console.log(` ${formatKeyName(key)}: ${value}`);
411
+ logger_1.logger.info(` ${formatKeyName(key)}: ${value}`);
438
412
  }
439
413
  }
440
414
  /**
@@ -1,9 +1,4 @@
1
- import { BusinessService } from '../services/business.service';
2
1
  import { CliOptions } from '../utils/config-loader';
3
- /**
4
- * 生成单个角色的日报
5
- */
6
- export declare function generateDailyReport(businessService: BusinessService, projectId: string, roleId: number, targetDate: string): Promise<void>;
7
2
  /**
8
3
  * daily 命令入口
9
4
  */