chandao4 0.1.0

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 (49) hide show
  1. package/.env.example +10 -0
  2. package/CHANGELOG.md +21 -0
  3. package/LICENSE +21 -0
  4. package/README.md +184 -0
  5. package/dist/commands/bug.d.ts +3 -0
  6. package/dist/commands/bug.js +373 -0
  7. package/dist/commands/config.d.ts +2 -0
  8. package/dist/commands/config.js +38 -0
  9. package/dist/commands/login.d.ts +3 -0
  10. package/dist/commands/login.js +83 -0
  11. package/dist/commands/product.d.ts +3 -0
  12. package/dist/commands/product.js +41 -0
  13. package/dist/commands/project.d.ts +3 -0
  14. package/dist/commands/project.js +70 -0
  15. package/dist/commands/task.d.ts +3 -0
  16. package/dist/commands/task.js +445 -0
  17. package/dist/config/config.d.ts +17 -0
  18. package/dist/config/config.js +216 -0
  19. package/dist/config/defaults.d.ts +5 -0
  20. package/dist/config/defaults.js +23 -0
  21. package/dist/core/api-client.d.ts +20 -0
  22. package/dist/core/api-client.js +127 -0
  23. package/dist/core/auth.d.ts +44 -0
  24. package/dist/core/auth.js +244 -0
  25. package/dist/core/errors.d.ts +17 -0
  26. package/dist/core/errors.js +61 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +125 -0
  29. package/dist/services/bug.service.d.ts +59 -0
  30. package/dist/services/bug.service.js +232 -0
  31. package/dist/services/product.service.d.ts +12 -0
  32. package/dist/services/product.service.js +43 -0
  33. package/dist/services/project.service.d.ts +18 -0
  34. package/dist/services/project.service.js +35 -0
  35. package/dist/services/task.service.d.ts +55 -0
  36. package/dist/services/task.service.js +254 -0
  37. package/dist/types/api.d.ts +31 -0
  38. package/dist/types/api.js +3 -0
  39. package/dist/types/config.d.ts +18 -0
  40. package/dist/types/config.js +3 -0
  41. package/dist/types/models.d.ts +65 -0
  42. package/dist/types/models.js +33 -0
  43. package/dist/utils/format.d.ts +38 -0
  44. package/dist/utils/format.js +201 -0
  45. package/dist/utils/prompt.d.ts +12 -0
  46. package/dist/utils/prompt.js +154 -0
  47. package/dist/utils/validators.d.ts +14 -0
  48. package/dist/utils/validators.js +42 -0
  49. package/package.json +63 -0
package/.env.example ADDED
@@ -0,0 +1,10 @@
1
+ # 禅道服务器 URL
2
+ ZENTAO_URL=https://your-company.chandao.com
3
+
4
+ # 登录凭据
5
+ ZENTAO_USERNAME=
6
+ ZENTAO_PASSWORD=
7
+
8
+ # 可选:API Key 认证(优先于密码)
9
+ # ZENTAO_CODE=
10
+ # ZENTAO_TOKEN=
package/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # 更新日志
2
+
3
+ 所有显著改动按版本记录于此。
4
+
5
+ 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-06-25
10
+
11
+ ### Added
12
+
13
+ - 查询命令:`bug my/list/show`、`task my/list/show`、`project list/show`、`product list`、`status`
14
+ - 写入命令:`bug create/update/delete`、`task create/update/delete`
15
+ - 登录管理:`login` / `logout`,凭据保存到 `~/.chandao4/config.json`
16
+ - 配置管理:`config show/set`
17
+ - 支持禅道企业版 4.1.3+,通过 Session Cookie 认证,无需管理员后台权限
18
+ - GitHub Actions 自动发布到 npm(基于 Trusted Publishing / OIDC)
19
+
20
+ [Unreleased]: https://github.com/gaoshang212/chandao4/compare/v0.1.0...HEAD
21
+ [0.1.0]: https://github.com/gaoshang212/chandao4/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gaoshang212
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # 禅道 CLI (chandao4)
2
+
3
+ 禅道命令行工具,支持 Bug、任务、项目和产品管理。适用于禅道企业版 4.1.3+。
4
+
5
+ > ⚠️ **当前状态**:查询类命令(`list` / `show` / `my` / `status`)已稳定可用;写入类命令(`create` / `update` / `delete`)仍在完善中,使用时请自行验证结果。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install -g chandao4
11
+ ```
12
+
13
+ 或本地开发:
14
+
15
+ ```bash
16
+ git clone <repo>
17
+ cd chandao4
18
+ npm install
19
+ npm run build
20
+ npm link
21
+ ```
22
+
23
+ ## 快速开始
24
+
25
+ ### 1. 配置
26
+
27
+ **推荐:交互式登录**
28
+
29
+ ```bash
30
+ chandao4 login
31
+ ```
32
+
33
+ 按提示输入禅道地址、用户名、密码,自动验证并保存。
34
+
35
+ **或手动配置环境变量**
36
+
37
+ ```bash
38
+ cp .env.example .env
39
+ # 编辑 .env 填写你的禅道信息
40
+ ```
41
+
42
+ ```bash
43
+ # .env 文件内容
44
+ ZENTAO_URL=https://your-company.chandao.com
45
+ ZENTAO_USERNAME=your-username
46
+ ZENTAO_PASSWORD=your-password
47
+ ```
48
+
49
+ ### 2. 测试连接
50
+
51
+ ```bash
52
+ chandao4 status
53
+ ```
54
+
55
+ ## 命令参考
56
+
57
+ ### 登录管理
58
+
59
+ ```bash
60
+ # 交互式登录
61
+ chandao4 login
62
+
63
+ # 清除本地凭据
64
+ chandao4 logout
65
+ ```
66
+
67
+ ### 项目管理
68
+
69
+ ```bash
70
+ # 列出项目
71
+ chandao4 project list
72
+ chandao4 project list --limit 10 --page 1
73
+
74
+ # 查看项目详情
75
+ chandao4 project show 1001
76
+ ```
77
+
78
+ ### Bug 管理
79
+
80
+ ```bash
81
+ # 获取当前账号的 Bug
82
+ chandao4 bug my
83
+ chandao4 bug my --limit 10 --page 1
84
+
85
+ # 列出 Bug
86
+ chandao4 bug list --product 2001
87
+ chandao4 bug list --product 2001 --status active --limit 10
88
+
89
+ # 查看 Bug 详情
90
+ chandao4 bug show 4001
91
+
92
+ # 创建 Bug
93
+ chandao4 bug create --product 2001
94
+ chandao4 bug create --product 2001 --title "登录页面报错" --severity 2 --priority 2
95
+
96
+ # 更新 Bug
97
+ chandao4 bug update 4001 --status resolved --assigned-to zhangsan
98
+
99
+ # 删除 Bug
100
+ chandao4 bug delete 4001
101
+ chandao4 bug delete 4001 --force
102
+ ```
103
+
104
+ ### 任务管理
105
+
106
+ ```bash
107
+ # 获取当前账号的任务
108
+ chandao4 task my
109
+ chandao4 task my --limit 10 --page 1
110
+
111
+ # 列出任务
112
+ chandao4 task list -p 1001
113
+ chandao4 task list -p 1001 --status doing --limit 10
114
+
115
+ # 查看任务详情
116
+ chandao4 task show 3001
117
+
118
+ # 创建任务
119
+ chandao4 task create -p 1001
120
+ chandao4 task create -p 1001 --name "实现登录接口" --priority 2 --estimate 4
121
+
122
+ # 更新任务
123
+ chandao4 task update 3001 --status doing --assigned-to lisi
124
+ chandao4 task update 3001 --consumed 3.5 --left 4.5
125
+
126
+ # 删除任务
127
+ chandao4 task delete 3001 -p 1001
128
+ ```
129
+
130
+ ### 产品管理
131
+
132
+ ```bash
133
+ # 列出产品
134
+ chandao4 product list
135
+ chandao4 product list --limit 10 --page 2
136
+ ```
137
+
138
+ ### JSON 输出
139
+
140
+ 所有命令支持 `--json` 选项用于脚本集成:
141
+
142
+ ```bash
143
+ chandao4 project list --json | jq '.[] | {id, name}'
144
+ chandao4 task list -p 1001 --json | jq '.[] | {id, name, status}'
145
+ chandao4 bug my --json
146
+ ```
147
+
148
+ ### 配置管理
149
+
150
+ ```bash
151
+ chandao4 config show
152
+ chandao4 config set server.url https://your-company.chandao.com
153
+ chandao4 config set server.username yourname
154
+ ```
155
+
156
+ ## 开发
157
+
158
+ ```bash
159
+ npm run dev -- status
160
+ npm run dev -- bug show 4001
161
+ npm run dev -- task list -p 1001
162
+ npm run build
163
+ npm run check
164
+ ```
165
+
166
+ ## 技术栈
167
+
168
+ - TypeScript + Commander.js
169
+ - Axios (HTTP 客户端)
170
+ - cli-table3 + Chalk (输出格式化)
171
+
172
+ ## 认证
173
+
174
+ 使用用户名/密码登录获取 Session Cookie,无需管理员后台权限。
175
+
176
+ 方案:登入 → Session Cookie → 所有后续请求携带 Cookie 认证。
177
+
178
+ ## 兼容性
179
+
180
+ | 版本 | 认证方式 | 状态 |
181
+ |------|---------|------|
182
+ | 禅道企业版 4.1.3 | Session Cookie | 测试通过 |
183
+ | 禅道开源版 12.5.3+ | Session Cookie | 理论兼容 |
184
+ | 其他版本 | - | 未测试 |
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import { BugService } from '../services/bug.service';
3
+ export declare function createBugCommand(bugService: BugService, getUseJson: () => boolean): Command;
@@ -0,0 +1,373 @@
1
+ "use strict";
2
+ // Bug 命令
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createBugCommand = createBugCommand;
8
+ const commander_1 = require("commander");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const format_1 = require("../utils/format");
11
+ const prompt_1 = require("../utils/prompt");
12
+ const validators_1 = require("../utils/validators");
13
+ const config_1 = require("../config/config");
14
+ function createBugCommand(bugService, getUseJson) {
15
+ const bug = new commander_1.Command('bug')
16
+ .description('Bug 管理:查看、创建、更新、删除 Bug');
17
+ // bug my - 获取当前用户的 Bug
18
+ bug.command('my')
19
+ .description('获取当前账号的 Bug')
20
+ .option('-l, --limit <n>', '显示条数', (v) => parseInt(v, 10), 20)
21
+ .option('--page <n>', '页码', (v) => parseInt(v, 10), 1)
22
+ .action(async (options) => {
23
+ try {
24
+ const { bugs, total } = await bugService.getMyBugs({
25
+ limit: options.limit,
26
+ page: options.page,
27
+ });
28
+ if (bugs.length === 0) {
29
+ console.log(chalk_1.default.gray('没有找到你的 Bug'));
30
+ }
31
+ else if (getUseJson()) {
32
+ console.log((0, format_1.formatJson)(bugs));
33
+ }
34
+ else {
35
+ console.log((0, format_1.formatBugTable)(bugs));
36
+ const pageInfo = options.limit
37
+ ? `第 ${options.page || 1} 页,共 ${total} 条`
38
+ : `共 ${total} 条`;
39
+ console.log(chalk_1.default.gray(`\n${pageInfo}`));
40
+ }
41
+ }
42
+ catch (err) {
43
+ process.exit(1);
44
+ }
45
+ });
46
+ // bug list
47
+ bug.command('list')
48
+ .description('列出 Bug')
49
+ .option('-p, --product <id>', '产品 ID', (v) => parseInt(v, 10))
50
+ .option('-s, --status <status>', '状态过滤 (active|resolved|closed)')
51
+ .option('-l, --limit <n>', '显示条数', (v) => parseInt(v, 10), 20)
52
+ .option('--page <n>', '页码', (v) => parseInt(v, 10), 1)
53
+ .action(async (options) => {
54
+ if (!options.product) {
55
+ console.error(chalk_1.default.red('错误: 需要指定 --product <id>'));
56
+ process.exit(1);
57
+ }
58
+ if (options.status && !(0, validators_1.isValidBugStatus)(options.status)) {
59
+ console.error(chalk_1.default.red(`错误: 无效的状态 "${options.status}",有效值: active, resolved, closed`));
60
+ process.exit(1);
61
+ }
62
+ try {
63
+ const { bugs, total } = await bugService.getList(options.product, {
64
+ status: options.status,
65
+ limit: options.limit,
66
+ page: options.page,
67
+ });
68
+ if (bugs.length === 0) {
69
+ console.log(chalk_1.default.gray('没有找到 Bug'));
70
+ }
71
+ else if (getUseJson()) {
72
+ console.log((0, format_1.formatJson)(bugs));
73
+ }
74
+ else {
75
+ console.log((0, format_1.formatBugTable)(bugs));
76
+ const pageInfo = options.limit
77
+ ? `第 ${options.page || 1} 页,共 ${total} 条`
78
+ : `共 ${total} 条`;
79
+ console.log(chalk_1.default.gray(`\n${pageInfo}`));
80
+ }
81
+ }
82
+ catch (err) {
83
+ process.exit(1);
84
+ }
85
+ });
86
+ // bug show
87
+ bug.command('show <id>')
88
+ .description('查看 Bug 详情')
89
+ .action(async (id) => {
90
+ const bugId = parseInt(id, 10);
91
+ if (!(0, validators_1.isValidId)(bugId)) {
92
+ console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
93
+ process.exit(1);
94
+ }
95
+ try {
96
+ const bug = await bugService.getDetail(bugId);
97
+ if (!bug) {
98
+ console.error(chalk_1.default.red(`错误: Bug #${bugId} 未找到`));
99
+ process.exit(1);
100
+ }
101
+ if (getUseJson()) {
102
+ console.log((0, format_1.formatJson)(bug));
103
+ }
104
+ else {
105
+ console.log((0, format_1.formatBugDetail)(bug));
106
+ }
107
+ }
108
+ catch (err) {
109
+ process.exit(1);
110
+ }
111
+ });
112
+ // bug create
113
+ bug.command('create')
114
+ .description('创建 Bug(交互式)')
115
+ .requiredOption('-p, --product <id>', '产品 ID', (v) => parseInt(v, 10))
116
+ .option('-t, --title <title>', 'Bug 标题')
117
+ .option('-s, --severity <n>', '严重程度 (1-4)', (v) => parseInt(v, 10))
118
+ .option('-P, --priority <n>', '优先级 (1-4)', (v) => parseInt(v, 10))
119
+ .option('--type <type>', '类型', 'code')
120
+ .option('-a, --assigned-to <user>', '指派给(默认当前登录账号)')
121
+ .option('--opened-build <build>', '发现版本 (build ID,多个用逗号分隔)')
122
+ .option('--deadline <date>', '截止日期 (YYYY-MM-DD)')
123
+ .option('--steps <text>', '重现步骤')
124
+ .action(async (options) => {
125
+ try {
126
+ let title = options.title;
127
+ let severity = options.severity;
128
+ let priority = options.priority;
129
+ let assignedTo = options.assignedTo;
130
+ let openedBuild = options.openedBuild;
131
+ let deadline = options.deadline;
132
+ const rawCfg = (0, config_1.loadRawConfig)();
133
+ const today = new Date().toISOString().split('T')[0];
134
+ if (!title) {
135
+ title = await (0, prompt_1.prompt)('Bug 标题');
136
+ if (!title) {
137
+ console.error(chalk_1.default.red('错误: Bug 标题不能为空'));
138
+ process.exit(1);
139
+ }
140
+ }
141
+ if (!severity) {
142
+ const input = await (0, prompt_1.prompt)('严重程度 (1=致命, 2=严重, 3=一般, 4=建议)', '3');
143
+ severity = parseInt(input, 10);
144
+ }
145
+ if (!(0, validators_1.isValidSeverity)(severity)) {
146
+ console.error(chalk_1.default.red(`错误: 无效的严重程度 "${severity}",有效值: 1-4`));
147
+ process.exit(1);
148
+ }
149
+ if (!priority) {
150
+ const input = await (0, prompt_1.prompt)('优先级 (1=紧急, 2=高, 3=中, 4=低)', '3');
151
+ priority = parseInt(input, 10);
152
+ }
153
+ if (!(0, validators_1.isValidPriority)(priority)) {
154
+ console.error(chalk_1.default.red(`错误: 无效的优先级 "${priority}",有效值: 1-4`));
155
+ process.exit(1);
156
+ }
157
+ const defaultUser = rawCfg.server.username || '';
158
+ assignedTo = assignedTo || await (0, prompt_1.prompt)('指派给', defaultUser) || defaultUser;
159
+ openedBuild = openedBuild || await (0, prompt_1.prompt)('发现版本 (build ID,默认 trunk)', 'trunk') || 'trunk';
160
+ deadline = deadline || await (0, prompt_1.prompt)('截止日期 (YYYY-MM-DD)', today) || today;
161
+ const bug = await bugService.create(options.product, {
162
+ title,
163
+ severity,
164
+ priority,
165
+ type: options.type,
166
+ assignedTo,
167
+ openedBuild,
168
+ deadline,
169
+ steps: options.steps,
170
+ });
171
+ if (bug) {
172
+ console.log(chalk_1.default.green(`✓ Bug #${bug.id} 创建成功: ${bug.title}`));
173
+ }
174
+ else {
175
+ console.log(chalk_1.default.green('✓ Bug 创建成功'));
176
+ }
177
+ }
178
+ catch (err) {
179
+ process.exit(1);
180
+ }
181
+ });
182
+ // bug update
183
+ bug.command('update <id>')
184
+ .description('更新 Bug')
185
+ .option('-t, --title <title>', 'Bug 标题')
186
+ .option('-s, --status <status>', '状态 (active|resolved|closed)')
187
+ .option('--severity <n>', '严重程度 (1-4)', (v) => parseInt(v, 10))
188
+ .option('--priority <n>', '优先级 (1-4)', (v) => parseInt(v, 10))
189
+ .option('-a, --assigned-to <user>', '指派给')
190
+ .option('--deadline <date>', '截止日期 (YYYY-MM-DD)')
191
+ .option('--steps <text>', '重现步骤')
192
+ .action(async (id, options) => {
193
+ const bugId = parseInt(id, 10);
194
+ if (!(0, validators_1.isValidId)(bugId)) {
195
+ console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
196
+ process.exit(1);
197
+ }
198
+ if (options.status && !(0, validators_1.isValidBugStatus)(options.status)) {
199
+ console.error(chalk_1.default.red(`错误: 无效的状态 "${options.status}"`));
200
+ process.exit(1);
201
+ }
202
+ const updates = {};
203
+ if (options.title)
204
+ updates.title = options.title;
205
+ if (options.status)
206
+ updates.status = options.status;
207
+ if (options.severity)
208
+ updates.severity = options.severity;
209
+ if (options.priority)
210
+ updates.priority = options.priority;
211
+ if (options.assignedTo)
212
+ updates.assignedTo = options.assignedTo;
213
+ if (options.deadline)
214
+ updates.deadline = options.deadline;
215
+ if (options.steps)
216
+ updates.steps = options.steps;
217
+ if (Object.keys(updates).length === 0) {
218
+ console.error(chalk_1.default.yellow('没有指定要更新的字段'));
219
+ process.exit(0);
220
+ }
221
+ try {
222
+ const bug = await bugService.update(bugId, updates);
223
+ if (bug) {
224
+ console.log(chalk_1.default.green(`✓ Bug #${bug.id} 更新成功`));
225
+ }
226
+ else {
227
+ console.log(chalk_1.default.green('✓ Bug 更新成功'));
228
+ }
229
+ }
230
+ catch (err) {
231
+ process.exit(1);
232
+ }
233
+ });
234
+ // bug resolve
235
+ bug.command('resolve <id>')
236
+ .description('解决 Bug(active → resolved)')
237
+ .option('-r, --resolution <type>', '解决方式: fixed|bydesign|duplicate|external|notrepro|postponed', 'fixed')
238
+ .option('--build <build>', '解决版本', 'trunk')
239
+ .option('-a, --assigned-to <user>', '指派给(默认回指给创建者)')
240
+ .option('-c, --comment <text>', '备注')
241
+ .option('-f, --force', '跳过确认')
242
+ .action(async (id, options) => {
243
+ const bugId = parseInt(id, 10);
244
+ if (!(0, validators_1.isValidId)(bugId)) {
245
+ console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
246
+ process.exit(1);
247
+ }
248
+ const validResolutions = ['fixed', 'bydesign', 'duplicate', 'external', 'notrepro', 'postponed'];
249
+ if (!validResolutions.includes(options.resolution)) {
250
+ console.error(chalk_1.default.red(`错误: 无效的解决方式 "${options.resolution}",有效值: ${validResolutions.join(', ')}`));
251
+ process.exit(1);
252
+ }
253
+ try {
254
+ if (!options.force) {
255
+ const confirmed = await (0, prompt_1.confirm)(`确定要解决 Bug #${bugId} 吗?`);
256
+ if (!confirmed) {
257
+ console.log(chalk_1.default.gray('已取消'));
258
+ return;
259
+ }
260
+ }
261
+ const bug = await bugService.resolve(bugId, {
262
+ resolution: options.resolution,
263
+ resolvedBuild: options.build,
264
+ assignedTo: options.assignedTo,
265
+ comment: options.comment,
266
+ });
267
+ if (bug) {
268
+ console.log(chalk_1.default.green(`✓ Bug #${bug.id} 已解决(${options.resolution})`));
269
+ }
270
+ else {
271
+ console.error(chalk_1.default.red('解决 Bug 失败'));
272
+ process.exit(1);
273
+ }
274
+ }
275
+ catch (err) {
276
+ process.exit(1);
277
+ }
278
+ });
279
+ // bug close
280
+ bug.command('close <id>')
281
+ .description('关闭 Bug(resolved → closed)')
282
+ .option('-c, --comment <text>', '备注')
283
+ .option('-f, --force', '跳过确认')
284
+ .action(async (id, options) => {
285
+ const bugId = parseInt(id, 10);
286
+ if (!(0, validators_1.isValidId)(bugId)) {
287
+ console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
288
+ process.exit(1);
289
+ }
290
+ try {
291
+ if (!options.force) {
292
+ const confirmed = await (0, prompt_1.confirm)(`确定要关闭 Bug #${bugId} 吗?`);
293
+ if (!confirmed) {
294
+ console.log(chalk_1.default.gray('已取消'));
295
+ return;
296
+ }
297
+ }
298
+ const bug = await bugService.close(bugId, options.comment);
299
+ if (bug) {
300
+ console.log(chalk_1.default.green(`✓ Bug #${bug.id} 已关闭`));
301
+ }
302
+ else {
303
+ console.error(chalk_1.default.red('关闭 Bug 失败'));
304
+ process.exit(1);
305
+ }
306
+ }
307
+ catch (err) {
308
+ process.exit(1);
309
+ }
310
+ });
311
+ // bug activate
312
+ bug.command('activate <id>')
313
+ .description('激活 Bug(resolved/closed → active)')
314
+ .option('-a, --assigned-to <user>', '指派给')
315
+ .option('-c, --comment <text>', '备注')
316
+ .option('-f, --force', '跳过确认')
317
+ .action(async (id, options) => {
318
+ const bugId = parseInt(id, 10);
319
+ if (!(0, validators_1.isValidId)(bugId)) {
320
+ console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
321
+ process.exit(1);
322
+ }
323
+ try {
324
+ if (!options.force) {
325
+ const confirmed = await (0, prompt_1.confirm)(`确定要激活 Bug #${bugId} 吗?`);
326
+ if (!confirmed) {
327
+ console.log(chalk_1.default.gray('已取消'));
328
+ return;
329
+ }
330
+ }
331
+ const bug = await bugService.activate(bugId, {
332
+ assignedTo: options.assignedTo,
333
+ comment: options.comment,
334
+ });
335
+ if (bug) {
336
+ console.log(chalk_1.default.green(`✓ Bug #${bug.id} 已激活(状态: ${bug.status})`));
337
+ }
338
+ else {
339
+ console.error(chalk_1.default.red('激活 Bug 失败'));
340
+ process.exit(1);
341
+ }
342
+ }
343
+ catch (err) {
344
+ process.exit(1);
345
+ }
346
+ });
347
+ // bug delete
348
+ bug.command('delete <id>')
349
+ .description('删除 Bug')
350
+ .option('-f, --force', '跳过确认')
351
+ .action(async (id, options) => {
352
+ const bugId = parseInt(id, 10);
353
+ if (!(0, validators_1.isValidId)(bugId)) {
354
+ console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
355
+ process.exit(1);
356
+ }
357
+ try {
358
+ if (!options.force) {
359
+ const confirmed = await (0, prompt_1.confirm)(`确定要删除 Bug #${bugId} 吗?`);
360
+ if (!confirmed) {
361
+ console.log(chalk_1.default.gray('已取消'));
362
+ return;
363
+ }
364
+ }
365
+ await bugService.delete(bugId);
366
+ console.log(chalk_1.default.green(`✓ Bug #${bugId} 已删除`));
367
+ }
368
+ catch (err) {
369
+ process.exit(1);
370
+ }
371
+ });
372
+ return bug;
373
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createConfigCommand(): Command;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ // 配置管理命令
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createConfigCommand = createConfigCommand;
8
+ const commander_1 = require("commander");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const config_1 = require("../config/config");
11
+ function createConfigCommand() {
12
+ const config = new commander_1.Command('config')
13
+ .description('配置管理');
14
+ config.command('show')
15
+ .description('显示当前配置')
16
+ .action(() => {
17
+ const cfg = (0, config_1.loadConfig)();
18
+ console.log(chalk_1.default.bold('\n当前配置:\n'));
19
+ console.log(` 服务器: ${cfg.server.url || chalk_1.default.gray('(未设置)')}`);
20
+ if (cfg.server.username) {
21
+ console.log(` 用户名: ${cfg.server.username}`);
22
+ console.log(` 密码: ${chalk_1.default.gray('******')}`);
23
+ }
24
+ console.log(` 认证模式: ${cfg.authMode === 'apikey' ? 'API Key' : 'Session (用户名/密码)'}`);
25
+ console.log('');
26
+ });
27
+ config.command('set <key> <value>')
28
+ .description('设置配置项(保存到用户目录)')
29
+ .action(async (key, value) => {
30
+ const validKeys = ['server.url', 'server.username', 'server.password', 'server.code', 'server.token'];
31
+ if (!validKeys.includes(key)) {
32
+ console.error(chalk_1.default.red(`错误: 无效的配置项 "${key}",有效值: ${validKeys.join(', ')}`));
33
+ process.exit(1);
34
+ }
35
+ (0, config_1.saveUserConfig)(key, value);
36
+ });
37
+ return config;
38
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createLoginCommand(): Command;
3
+ export declare function createLogoutCommand(): Command;