@hecom/codearts 0.2.0 → 0.2.1
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/README.md +34 -169
- package/dist/bin/cli.js +26 -15
- package/dist/commands/bug.command.js +1 -1
- package/dist/commands/config.command.d.ts +25 -0
- package/dist/commands/config.command.js +417 -71
- package/dist/services/api.service.d.ts +9 -2
- package/dist/services/api.service.js +12 -4
- package/dist/services/business.service.d.ts +21 -1
- package/dist/services/business.service.js +66 -1
- package/dist/types/index.d.ts +43 -0
- package/dist/types/index.js +34 -1
- package/dist/utils/config-loader.d.ts +28 -9
- package/dist/utils/config-loader.js +195 -31
- package/package.json +1 -3
- package/.env.example +0 -20
- package/dist/utils/global-config.d.ts +0 -24
- package/dist/utils/global-config.js +0 -153
|
@@ -1,11 +1,149 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.configCommand = configCommand;
|
|
40
|
+
exports.updateProjectConfigCommand = updateProjectConfigCommand;
|
|
41
|
+
exports.getAvailableProjectConfigs = getAvailableProjectConfigs;
|
|
42
|
+
exports.showConfigCommand = showConfigCommand;
|
|
7
43
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
-
const
|
|
44
|
+
const readline = __importStar(require("readline"));
|
|
45
|
+
const business_service_1 = require("../services/business.service");
|
|
46
|
+
const types_1 = require("../types");
|
|
47
|
+
const config_loader_1 = require("../utils/config-loader");
|
|
48
|
+
/**
|
|
49
|
+
* 清除终端上指定行数的内容
|
|
50
|
+
* @param lines 要清除的行数
|
|
51
|
+
*/
|
|
52
|
+
function clearLines(lines) {
|
|
53
|
+
for (let i = 0; i < lines; i++) {
|
|
54
|
+
readline.moveCursor(process.stdout, 0, -1); // 光标向上移动一行
|
|
55
|
+
readline.clearLine(process.stdout, 0); // 清除当前行
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 配置角色 ID
|
|
60
|
+
*/
|
|
61
|
+
async function configureRoleIds(businessService, projectId, existingValue) {
|
|
62
|
+
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
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error('❌ 获取角色列表失败:', error);
|
|
110
|
+
// 如果获取失败,使用手动输入
|
|
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 必须是数字或逗号分隔的数字列表';
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
return manualRoleId;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 第三阶段项目配置项列表
|
|
132
|
+
* 未来可以在这里添加更多配置项
|
|
133
|
+
*/
|
|
134
|
+
const PROJECT_CONFIG_ITEMS = [
|
|
135
|
+
{
|
|
136
|
+
key: types_1.ConfigKey.ROLE_ID,
|
|
137
|
+
label: '角色配置',
|
|
138
|
+
configure: configureRoleIds,
|
|
139
|
+
},
|
|
140
|
+
// 未来可以在这里添加更多配置项,例如:
|
|
141
|
+
// {
|
|
142
|
+
// key: ConfigKey.CUSTOM_FIELD,
|
|
143
|
+
// label: '自定义字段配置',
|
|
144
|
+
// configure: configureCustomField,
|
|
145
|
+
// },
|
|
146
|
+
];
|
|
9
147
|
/**
|
|
10
148
|
* 交互式配置向导命令
|
|
11
149
|
* 引导用户创建或更新全局配置文件
|
|
@@ -14,9 +152,9 @@ async function configCommand() {
|
|
|
14
152
|
console.log('\n欢迎使用 Hecom CodeArts 配置向导');
|
|
15
153
|
console.log('='.repeat(60));
|
|
16
154
|
console.log('此向导将帮助您配置华为云 CodeArts API 访问凭证。');
|
|
17
|
-
console.log(`配置将保存到: ${(0,
|
|
18
|
-
const existingConfig = (0,
|
|
19
|
-
if ((0,
|
|
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)()) {
|
|
20
158
|
const { overwrite } = await inquirer_1.default.prompt([
|
|
21
159
|
{
|
|
22
160
|
type: 'confirm',
|
|
@@ -30,76 +168,171 @@ async function configCommand() {
|
|
|
30
168
|
return;
|
|
31
169
|
}
|
|
32
170
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
171
|
+
// 第一阶段:IAM 凭证配置
|
|
172
|
+
let credentialsValid = false;
|
|
173
|
+
let businessService = null;
|
|
174
|
+
let iamAnswers = {
|
|
175
|
+
iamEndpoint: '',
|
|
176
|
+
region: '',
|
|
177
|
+
codeartsUrl: '',
|
|
178
|
+
domain: '',
|
|
179
|
+
username: '',
|
|
180
|
+
password: '',
|
|
181
|
+
};
|
|
182
|
+
while (!credentialsValid) {
|
|
183
|
+
iamAnswers = await inquirer_1.default.prompt([
|
|
184
|
+
{
|
|
185
|
+
type: 'input',
|
|
186
|
+
name: 'iamEndpoint',
|
|
187
|
+
message: 'IAM 认证端点:',
|
|
188
|
+
default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT] ||
|
|
189
|
+
'https://iam.cn-north-4.myhuaweicloud.com',
|
|
190
|
+
validate: (input) => (input.trim() ? true : 'IAM 认证端点不能为空'),
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
type: 'input',
|
|
194
|
+
name: 'region',
|
|
195
|
+
message: '华为云区域:',
|
|
196
|
+
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',
|
|
202
|
+
message: 'CodeArts API 地址:',
|
|
203
|
+
default: existingConfig[types_1.ConfigKey.CODEARTS_BASE_URL] ||
|
|
204
|
+
'https://projectman-ext.cn-north-4.myhuaweicloud.cn',
|
|
205
|
+
validate: (input) => (input.trim() ? true : 'CodeArts API 地址不能为空'),
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
type: 'input',
|
|
209
|
+
name: 'domain',
|
|
210
|
+
message: '华为云账号名:',
|
|
211
|
+
default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_DOMAIN] || '',
|
|
212
|
+
validate: (input) => (input.trim() ? true : '华为云账号名不能为空'),
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
type: 'input',
|
|
216
|
+
name: 'username',
|
|
217
|
+
message: 'IAM 用户名:',
|
|
218
|
+
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
|
+
]);
|
|
230
|
+
// 创建 BusinessService 实例
|
|
231
|
+
businessService = new business_service_1.BusinessService({
|
|
232
|
+
iamEndpoint: iamAnswers.iamEndpoint,
|
|
233
|
+
region: iamAnswers.region,
|
|
234
|
+
endpoint: iamAnswers.codeartsUrl,
|
|
235
|
+
username: iamAnswers.username,
|
|
236
|
+
password: iamAnswers.password,
|
|
237
|
+
domainName: iamAnswers.domain,
|
|
238
|
+
enableLogging: false,
|
|
239
|
+
});
|
|
240
|
+
// 验证凭证
|
|
241
|
+
const validationResult = await businessService.validateCredentials();
|
|
242
|
+
if (validationResult.success) {
|
|
243
|
+
credentialsValid = true;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
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
|
+
]);
|
|
256
|
+
if (!retry) {
|
|
257
|
+
console.log('\n已取消配置。');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// 计算需要清除的行数
|
|
261
|
+
// 6个配置问题 + 错误信息行数 + 1行重试提示
|
|
262
|
+
const errorLines = errorMessage.split('\n').length - 1; // 减1因为最后一个\n不产生新行
|
|
263
|
+
const totalLines = 6 + errorLines + 1;
|
|
264
|
+
clearLines(totalLines);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// 第二阶段:获取项目列表并选择项目
|
|
268
|
+
let projectId;
|
|
269
|
+
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
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
302
|
+
console.error('❌ 获取项目列表失败:', errorMsg, '\n');
|
|
303
|
+
// 获取失败,使用手动输入
|
|
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 不能为空'),
|
|
93
311
|
},
|
|
94
|
-
|
|
95
|
-
|
|
312
|
+
]);
|
|
313
|
+
projectId = manualProjectId;
|
|
314
|
+
}
|
|
315
|
+
// 第三阶段:配置项目相关配置
|
|
316
|
+
const projectConfigs = {};
|
|
317
|
+
for (const configItem of PROJECT_CONFIG_ITEMS) {
|
|
318
|
+
const value = await configItem.configure(businessService, projectId, existingConfig[configItem.key]);
|
|
319
|
+
projectConfigs[configItem.key] = value;
|
|
320
|
+
}
|
|
321
|
+
// 合并所有配置(转换为标准环境变量名)
|
|
322
|
+
const finalConfig = {
|
|
323
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT]: iamAnswers.iamEndpoint,
|
|
324
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_REGION]: iamAnswers.region,
|
|
325
|
+
[types_1.ConfigKey.CODEARTS_BASE_URL]: iamAnswers.codeartsUrl,
|
|
326
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_DOMAIN]: iamAnswers.domain,
|
|
327
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_USERNAME]: iamAnswers.username,
|
|
328
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD]: iamAnswers.password,
|
|
329
|
+
[types_1.ConfigKey.PROJECT_ID]: projectId,
|
|
330
|
+
...projectConfigs,
|
|
331
|
+
};
|
|
96
332
|
try {
|
|
97
|
-
(0,
|
|
333
|
+
(0, config_loader_1.writeGlobalConfig)(finalConfig);
|
|
98
334
|
console.log('\n✅ 全局配置已成功保存');
|
|
99
|
-
console.log(`配置文件位置: ${(0,
|
|
100
|
-
console.log('\n您现在可以在任何目录使用以下命令:');
|
|
101
|
-
console.log(' codearts daily # 生成日报');
|
|
102
|
-
console.log(' codearts work-hour # 生成年度工时统计');
|
|
335
|
+
console.log(`配置文件位置: ${(0, config_loader_1.getGlobalConfigPath)()}`);
|
|
103
336
|
console.log('\n提示:配置文件包含敏感信息,请妥善保管。');
|
|
104
337
|
}
|
|
105
338
|
catch (error) {
|
|
@@ -107,3 +340,116 @@ async function configCommand() {
|
|
|
107
340
|
process.exit(1);
|
|
108
341
|
}
|
|
109
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* 单独更新某个项目配置项
|
|
345
|
+
* @param configKey 配置项的键名(例如 ConfigKey.ROLE_ID)
|
|
346
|
+
*/
|
|
347
|
+
async function updateProjectConfigCommand(configKey) {
|
|
348
|
+
// 检查配置文件是否存在
|
|
349
|
+
if (!(0, config_loader_1.globalConfigExists)()) {
|
|
350
|
+
console.error('\n❌ 全局配置文件不存在,请先运行 `npx @hecom/codearts config` 创建配置。');
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
// 查找对应的配置项
|
|
354
|
+
const configItem = PROJECT_CONFIG_ITEMS.find((item) => item.key === configKey);
|
|
355
|
+
if (!configItem) {
|
|
356
|
+
console.error(`\n❌ 未知的配置项: ${configKey}`);
|
|
357
|
+
console.log(`\n可用的配置项:`);
|
|
358
|
+
PROJECT_CONFIG_ITEMS.forEach((item) => {
|
|
359
|
+
console.log(` - ${item.key}: ${item.label}`);
|
|
360
|
+
});
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
// 读取现有配置
|
|
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
|
+
}
|
|
375
|
+
// 创建 BusinessService 实例
|
|
376
|
+
const businessService = new business_service_1.BusinessService({
|
|
377
|
+
iamEndpoint: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT],
|
|
378
|
+
region: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_REGION],
|
|
379
|
+
endpoint: existingConfig[types_1.ConfigKey.CODEARTS_BASE_URL],
|
|
380
|
+
username: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_USERNAME],
|
|
381
|
+
password: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD],
|
|
382
|
+
domainName: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_DOMAIN],
|
|
383
|
+
enableLogging: false,
|
|
384
|
+
});
|
|
385
|
+
// 执行配置
|
|
386
|
+
const newValue = await configItem.configure(businessService, existingConfig[types_1.ConfigKey.PROJECT_ID], existingConfig[configKey]);
|
|
387
|
+
// 更新配置
|
|
388
|
+
const updatedConfig = {
|
|
389
|
+
...existingConfig,
|
|
390
|
+
[configKey]: newValue,
|
|
391
|
+
};
|
|
392
|
+
try {
|
|
393
|
+
(0, config_loader_1.writeGlobalConfig)(updatedConfig);
|
|
394
|
+
console.log(`\n✅ ${configItem.label}已成功更新`);
|
|
395
|
+
console.log(`配置文件位置: ${(0, config_loader_1.getGlobalConfigPath)()}`);
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
console.error('\n❌ 保存配置文件失败:', error);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* 获取所有可用的项目配置项
|
|
404
|
+
*/
|
|
405
|
+
function getAvailableProjectConfigs() {
|
|
406
|
+
return PROJECT_CONFIG_ITEMS;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* 显示当前配置
|
|
410
|
+
*
|
|
411
|
+
*/
|
|
412
|
+
async function showConfigCommand() {
|
|
413
|
+
// 获取最终合并后的配置
|
|
414
|
+
const config = (0, config_loader_1.getConfig)();
|
|
415
|
+
// 按类别显示配置
|
|
416
|
+
console.log('\n【华为云 IAM 凭证】');
|
|
417
|
+
const iamKeys = [
|
|
418
|
+
types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT,
|
|
419
|
+
types_1.ConfigKey.HUAWEI_CLOUD_REGION,
|
|
420
|
+
types_1.ConfigKey.HUAWEI_CLOUD_USERNAME,
|
|
421
|
+
types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD,
|
|
422
|
+
types_1.ConfigKey.HUAWEI_CLOUD_DOMAIN,
|
|
423
|
+
];
|
|
424
|
+
for (const key of iamKeys) {
|
|
425
|
+
const value = config[key] || '(未配置)';
|
|
426
|
+
const displayValue = key.includes('PASSWORD') && value !== '(未配置)' ? '********' : value;
|
|
427
|
+
console.log(` ${formatKeyName(key)}: ${displayValue}`);
|
|
428
|
+
}
|
|
429
|
+
console.log('\n【CodeArts 配置】');
|
|
430
|
+
const codeartsKeys = [
|
|
431
|
+
types_1.ConfigKey.CODEARTS_BASE_URL,
|
|
432
|
+
types_1.ConfigKey.PROJECT_ID,
|
|
433
|
+
types_1.ConfigKey.ROLE_ID,
|
|
434
|
+
];
|
|
435
|
+
for (const key of codeartsKeys) {
|
|
436
|
+
const value = config[key] || '(未配置)';
|
|
437
|
+
console.log(` ${formatKeyName(key)}: ${value}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* 格式化配置项名称(用于显示)
|
|
442
|
+
*/
|
|
443
|
+
function formatKeyName(key) {
|
|
444
|
+
const nameMap = {
|
|
445
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT]: 'IAM 认证端点',
|
|
446
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_REGION]: '华为云区域',
|
|
447
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_USERNAME]: 'IAM 用户名',
|
|
448
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD]: 'IAM 密码',
|
|
449
|
+
[types_1.ConfigKey.HUAWEI_CLOUD_DOMAIN]: '华为云账号名',
|
|
450
|
+
[types_1.ConfigKey.CODEARTS_BASE_URL]: 'CodeArts API 地址',
|
|
451
|
+
[types_1.ConfigKey.PROJECT_ID]: '项目 ID',
|
|
452
|
+
[types_1.ConfigKey.ROLE_ID]: '角色 ID',
|
|
453
|
+
};
|
|
454
|
+
return nameMap[key] || key;
|
|
455
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiResponse, CachedToken, HuaweiCloudConfig, ListChildIssuesV2Response, ListIssuesV4Request, ListIssuesV4Response, ListProjectIterationsV4Request, ListProjectIterationsV4Response, ProjectListResponse, ProjectMemberListResponse, ProjectMemberQueryParams, ProjectQueryParams, ShowProjectWorkHoursRequest, ShowProjectWorkHoursResponse } from '../types';
|
|
1
|
+
import { ApiResponse, CachedToken, HuaweiCloudConfig, ListChildIssuesV2Response, ListChildIssuesV4Response, ListIssuesV4Request, ListIssuesV4Response, ListProjectIterationsV4Request, ListProjectIterationsV4Response, ProjectListResponse, ProjectMemberListResponse, ProjectMemberQueryParams, ProjectQueryParams, ShowProjectWorkHoursRequest, ShowProjectWorkHoursResponse } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* 华为云CodeArts API服务类
|
|
4
4
|
* 支持IAM Token认证和CodeArts API调用
|
|
@@ -58,8 +58,15 @@ export declare class ApiService {
|
|
|
58
58
|
/**
|
|
59
59
|
* 查询子工作项 (ListChildIssuesV4)
|
|
60
60
|
* 获取指定工作项的所有子工作项
|
|
61
|
+
*
|
|
62
|
+
* FIXME: 华为云接口有问题,只能查询 15 条数据
|
|
61
63
|
*/
|
|
62
|
-
getChildIssues(projectId: string, issueId: string
|
|
64
|
+
getChildIssues(projectId: string, issueId: string): Promise<ApiResponse<ListChildIssuesV4Response>>;
|
|
65
|
+
/**
|
|
66
|
+
* 查询子工作项 (ListChildIssuesV2)
|
|
67
|
+
* 获取指定工作项的所有子工作项
|
|
68
|
+
*/
|
|
69
|
+
getChildIssuesV2(projectId: string, issueId: string, pageSize?: number, pageNo?: number): Promise<ApiResponse<ListChildIssuesV2Response>>;
|
|
63
70
|
/**
|
|
64
71
|
* 创建工作项
|
|
65
72
|
*/
|
|
@@ -313,11 +313,19 @@ class ApiService {
|
|
|
313
313
|
/**
|
|
314
314
|
* 查询子工作项 (ListChildIssuesV4)
|
|
315
315
|
* 获取指定工作项的所有子工作项
|
|
316
|
+
*
|
|
317
|
+
* FIXME: 华为云接口有问题,只能查询 15 条数据
|
|
316
318
|
*/
|
|
317
|
-
async getChildIssues(projectId, issueId
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
319
|
+
async getChildIssues(projectId, issueId) {
|
|
320
|
+
return this.request(`/v4/projects/${projectId}/issues/${issueId}/child`, {
|
|
321
|
+
method: 'GET',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* 查询子工作项 (ListChildIssuesV2)
|
|
326
|
+
* 获取指定工作项的所有子工作项
|
|
327
|
+
*/
|
|
328
|
+
async getChildIssuesV2(projectId, issueId, pageSize = 100, pageNo = 1) {
|
|
321
329
|
return this.request('/v2/issues/child-issue-list', {
|
|
322
330
|
method: 'POST',
|
|
323
331
|
data: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AllWorkHourStats, HuaweiCloudConfig, IssueItem, IssueItemV2, IterationInfo, ProjectMember, WorkHourStats, WorkProgressStats } from '../types';
|
|
1
|
+
import { AllWorkHourStats, HuaweiCloudConfig, IssueItem, IssueItemV2, IterationInfo, ProjectMember, ProjectRole, WorkHourStats, WorkProgressStats } from '../types';
|
|
2
2
|
import { ApiService } from './api.service';
|
|
3
3
|
/**
|
|
4
4
|
* 业务服务类
|
|
@@ -19,6 +19,12 @@ export declare class BusinessService {
|
|
|
19
19
|
* @returns 指定角色的成员列表
|
|
20
20
|
*/
|
|
21
21
|
getMembersByRoleId(projectId: string, roleId: number): Promise<ProjectMember[]>;
|
|
22
|
+
/**
|
|
23
|
+
* 获取项目中的所有角色列表(去重)
|
|
24
|
+
* @param projectId 项目ID
|
|
25
|
+
* @returns 项目中的所有角色列表
|
|
26
|
+
*/
|
|
27
|
+
getProjectRoles(projectId: string): Promise<ProjectRole[]>;
|
|
22
28
|
/**
|
|
23
29
|
* 获取指定日期之后的迭代列表
|
|
24
30
|
* @param projectId 项目ID
|
|
@@ -86,4 +92,18 @@ export declare class BusinessService {
|
|
|
86
92
|
* @returns 所有子工作项列表
|
|
87
93
|
*/
|
|
88
94
|
getChildIssues(projectId: string, issueId: string): Promise<IssueItemV2[]>;
|
|
95
|
+
/**
|
|
96
|
+
* 验证 IAM 凭证是否有效
|
|
97
|
+
* @returns 验证结果,包含成功状态和可能的错误信息
|
|
98
|
+
*/
|
|
99
|
+
validateCredentials(): Promise<{
|
|
100
|
+
success: boolean;
|
|
101
|
+
error?: string;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* 获取项目列表
|
|
105
|
+
* @param limit 返回的项目数量限制,默认100
|
|
106
|
+
* @returns 项目列表
|
|
107
|
+
*/
|
|
108
|
+
getProjects(limit?: number): Promise<import('../types').Project[]>;
|
|
89
109
|
}
|