@meet-im/lxcli 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.
- package/dist/commands/generate-skills.js +5 -0
- package/dist/commands/services/kb/config.js +12 -12
- package/dist/commands/services/kb/descriptions.js +3 -0
- package/dist/commands/services/kb/handler.d.ts +6 -0
- package/dist/commands/services/kb/handler.js +52 -6
- package/dist/commands/services/kb/index.js +38 -1
- package/dist/commands/services/kb/params.d.ts +1 -1
- package/dist/commands/services/kb/params.js +19 -2
- package/dist/commands/services/kb/skill-metadata.d.ts +5 -0
- package/dist/commands/services/kb/skill-metadata.js +15 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import * as path from 'path';
|
|
|
4
4
|
import { outputCliError } from '../core/error.js';
|
|
5
5
|
import { logger } from '../core/logger.js';
|
|
6
6
|
import { PARAM_DEFS, METHOD_DESCRIPTIONS } from './services/kb/descriptions.js';
|
|
7
|
+
import { METHOD_SKILL_METADATA } from './services/kb/skill-metadata.js';
|
|
7
8
|
const API_ROOT = path.resolve('api');
|
|
8
9
|
const DEFAULT_SKILLS_DIR = path.resolve('api/skills');
|
|
9
10
|
const DEFAULT_OUTPUT = 'api/docs/skills.md';
|
|
@@ -157,6 +158,7 @@ async function readPackageVersion() {
|
|
|
157
158
|
function renderSkillMd(method, version) {
|
|
158
159
|
const description = METHOD_DESCRIPTIONS[method];
|
|
159
160
|
const params = PARAM_DEFS[method] || [];
|
|
161
|
+
const metadata = METHOD_SKILL_METADATA[method];
|
|
160
162
|
const skillName = `lxcli-kb-${method}`;
|
|
161
163
|
const hasParams = params.length > 0;
|
|
162
164
|
// 快捷参数列表
|
|
@@ -223,6 +225,9 @@ metadata:
|
|
|
223
225
|
lines.push(renderParamsTable(params));
|
|
224
226
|
}
|
|
225
227
|
lines.push('## Examples', '', '```bash', shortcutUsage || `lxcli kb ${method}`, '```', '');
|
|
228
|
+
if (metadata?.notes?.length) {
|
|
229
|
+
lines.push('## Notes', '', ...metadata.notes.map((note) => `- ${note}`), '');
|
|
230
|
+
}
|
|
226
231
|
return lines.join('\n');
|
|
227
232
|
}
|
|
228
233
|
async function generateServiceSkills(skillsDir, serviceName) {
|
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
// kanboard gateway 配置(按企业、按环境)
|
|
3
3
|
// 实际地址需要根据部署环境配置
|
|
4
4
|
const meetGateways = {
|
|
5
|
-
test: 'https://kanboard-
|
|
5
|
+
test: 'https://kanboard-staging.miyachat.com',
|
|
6
6
|
pre: 'https://kanboard-pre.miyachat.com',
|
|
7
7
|
staging: 'https://kanboard-staging.miyachat.com',
|
|
8
|
-
beta: 'https://kanboard-
|
|
8
|
+
beta: 'https://kanboard-staging.miyachat.com',
|
|
9
9
|
prod: 'https://kanboard.miyachat.com',
|
|
10
10
|
};
|
|
11
11
|
const coGateways = {
|
|
12
|
-
test: 'https://kanboard
|
|
13
|
-
pre: 'https://kanboard
|
|
14
|
-
staging: 'https://kanboard
|
|
15
|
-
beta: 'https://kanboard
|
|
16
|
-
prod: 'https://kanboard.
|
|
12
|
+
test: 'https://kanboard.mecord668.com',
|
|
13
|
+
pre: 'https://kanboard.mecord668.com',
|
|
14
|
+
staging: 'https://kanboard.mecord668.com',
|
|
15
|
+
beta: 'https://kanboard.mecord668.com',
|
|
16
|
+
prod: 'https://kanboard.mecord668.com',
|
|
17
17
|
};
|
|
18
18
|
const skyGateways = {
|
|
19
|
-
test: 'https://kanboard
|
|
20
|
-
pre: 'https://kanboard
|
|
21
|
-
staging: 'https://kanboard
|
|
22
|
-
beta: 'https://kanboard
|
|
23
|
-
prod: 'https://kanboard.
|
|
19
|
+
test: 'https://kanboard.2tianxin.com',
|
|
20
|
+
pre: 'https://kanboard.2tianxin.com',
|
|
21
|
+
staging: 'https://kanboard.2tianxin.com',
|
|
22
|
+
beta: 'https://kanboard.2tianxin.com',
|
|
23
|
+
prod: 'https://kanboard.2tianxin.com',
|
|
24
24
|
};
|
|
25
25
|
export const kbConfig = {
|
|
26
26
|
name: 'kb',
|
|
@@ -16,10 +16,13 @@ export const METHOD_DESCRIPTIONS = {
|
|
|
16
16
|
createTask: '创建需求/bug',
|
|
17
17
|
createSubtask: '创建子任务',
|
|
18
18
|
updateTask: '修改需求',
|
|
19
|
+
updateSubtask: '更新子任务',
|
|
19
20
|
getAllComments: '获取评论列表',
|
|
20
21
|
createComment: '对需求增加评论',
|
|
21
22
|
createProjectFile: '上传附件到项目',
|
|
23
|
+
downloadProjectFile: '下载项目附件',
|
|
22
24
|
createTaskFile: '上传附件到需求',
|
|
25
|
+
downloadTaskFile: '下载需求附件',
|
|
23
26
|
};
|
|
24
27
|
// 重新导出 params.ts 的类型和定义,方便其他模块使用
|
|
25
28
|
export { PARAM_DEFS } from './params.js';
|
|
@@ -4,9 +4,15 @@ export interface KbCommandOptions {
|
|
|
4
4
|
task_id?: string;
|
|
5
5
|
title?: string;
|
|
6
6
|
project_id?: string;
|
|
7
|
+
file_id?: string;
|
|
8
|
+
owner_id?: string;
|
|
9
|
+
category_id?: string;
|
|
7
10
|
query?: string;
|
|
11
|
+
user_id?: string;
|
|
12
|
+
content?: string;
|
|
8
13
|
user?: string;
|
|
9
14
|
gateway?: string;
|
|
10
15
|
file?: string;
|
|
16
|
+
out?: string;
|
|
11
17
|
}
|
|
12
18
|
export declare function createKbAction(method: Method): (options: KbCommandOptions) => Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile } from 'fs/promises';
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
2
|
import { basename } from 'path';
|
|
3
3
|
import { getCurrentUser, getGatewayForUser } from '../../../core/auth.js';
|
|
4
4
|
import { outputCliError } from '../../../core/error.js';
|
|
@@ -87,13 +87,15 @@ export function createKbAction(method) {
|
|
|
87
87
|
}
|
|
88
88
|
params = { task_id: taskId };
|
|
89
89
|
}
|
|
90
|
-
else if (method === 'createTask' && options.title && options.project_id) {
|
|
90
|
+
else if (method === 'createTask' && options.title && options.project_id && options.owner_id && options.category_id) {
|
|
91
91
|
const projectId = parseTaskId(options.project_id);
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const ownerId = parseTaskId(options.owner_id);
|
|
93
|
+
const categoryId = parseTaskId(options.category_id);
|
|
94
|
+
if (projectId === null || ownerId === null || categoryId === null) {
|
|
95
|
+
outputCliError('PARAM_INVALID', '--project_id, --owner_id and --category_id must be positive integers');
|
|
94
96
|
return;
|
|
95
97
|
}
|
|
96
|
-
params = { title: options.title, project_id: projectId };
|
|
98
|
+
params = { title: options.title, project_id: projectId, owner_id: ownerId, category_id: categoryId };
|
|
97
99
|
}
|
|
98
100
|
else if (method === 'searchTasks' && options.project_id && options.query) {
|
|
99
101
|
const projectId = parseTaskId(options.project_id);
|
|
@@ -127,6 +129,24 @@ export function createKbAction(method) {
|
|
|
127
129
|
}
|
|
128
130
|
params = { task_id: taskId };
|
|
129
131
|
}
|
|
132
|
+
else if (method === 'createComment' && options.task_id && options.user_id && options.content) {
|
|
133
|
+
const taskId = parseTaskId(options.task_id);
|
|
134
|
+
const userId = options.user_id === '0' ? 0 : parseTaskId(options.user_id);
|
|
135
|
+
if (taskId === null || userId === null) {
|
|
136
|
+
outputCliError('PARAM_INVALID', '--task_id and --user_id must be non-negative integers');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
params = { task_id: taskId, user_id: userId, content: options.content };
|
|
140
|
+
}
|
|
141
|
+
else if (method === 'downloadProjectFile' && options.project_id && options.file_id) {
|
|
142
|
+
const projectId = parseTaskId(options.project_id);
|
|
143
|
+
const fileId = parseTaskId(options.file_id);
|
|
144
|
+
if (projectId === null || fileId === null) {
|
|
145
|
+
outputCliError('PARAM_INVALID', '--project_id and --file_id must be positive integers');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
params = { project_id: projectId, file_id: fileId };
|
|
149
|
+
}
|
|
130
150
|
else if (method === 'createProjectFile' && options.file && options.project_id) {
|
|
131
151
|
const projectId = parseTaskId(options.project_id);
|
|
132
152
|
if (projectId === null) {
|
|
@@ -144,6 +164,14 @@ export function createKbAction(method) {
|
|
|
144
164
|
return;
|
|
145
165
|
}
|
|
146
166
|
}
|
|
167
|
+
else if (method === 'downloadTaskFile' && options.file_id) {
|
|
168
|
+
const fileId = parseTaskId(options.file_id);
|
|
169
|
+
if (fileId === null) {
|
|
170
|
+
outputCliError('PARAM_INVALID', '--file_id must be a positive integer');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
params = { file_id: fileId };
|
|
174
|
+
}
|
|
147
175
|
else if (method === 'createTaskFile' && options.file && options.task_id && options.project_id) {
|
|
148
176
|
const taskId = parseTaskId(options.task_id);
|
|
149
177
|
const projectId = parseTaskId(options.project_id);
|
|
@@ -169,12 +197,15 @@ export function createKbAction(method) {
|
|
|
169
197
|
else {
|
|
170
198
|
const shortcuts = {
|
|
171
199
|
getTask: '--task_id',
|
|
172
|
-
createTask: '--title and --
|
|
200
|
+
createTask: '--title, --project_id, --owner_id and --category_id',
|
|
173
201
|
searchTasks: '--project_id and --query',
|
|
174
202
|
getAllActiveIterations: '--project_id',
|
|
175
203
|
getAllCategories: '--project_id',
|
|
176
204
|
getAllComments: '--task_id',
|
|
205
|
+
createComment: '--task_id, --user_id and --content',
|
|
206
|
+
downloadProjectFile: '--project_id and --file_id',
|
|
177
207
|
createProjectFile: '--file and --project_id',
|
|
208
|
+
downloadTaskFile: '--file_id',
|
|
178
209
|
createTaskFile: '--file, --task_id and --project_id',
|
|
179
210
|
};
|
|
180
211
|
outputCliError('PARAM_INVALID', shortcuts[method]
|
|
@@ -183,6 +214,21 @@ export function createKbAction(method) {
|
|
|
183
214
|
return;
|
|
184
215
|
}
|
|
185
216
|
const result = await jsonRpcCall(gateway, method, params, user.token);
|
|
217
|
+
if ((method === 'downloadProjectFile' || method === 'downloadTaskFile') && options.out) {
|
|
218
|
+
if (typeof result !== 'string') {
|
|
219
|
+
outputCliError('PARAM_INVALID', `${method} result must be a Base64 string`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
const buffer = Buffer.from(result, 'base64');
|
|
224
|
+
await writeFile(options.out, buffer);
|
|
225
|
+
outputJson({ success: true, path: options.out, bytes: buffer.length });
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
outputCliError('PARAM_INVALID', `Failed to write file: ${options.out}`);
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
186
232
|
outputJson(result);
|
|
187
233
|
};
|
|
188
234
|
}
|
|
@@ -38,13 +38,15 @@ const kbService = {
|
|
|
38
38
|
.option('--user <user>', 'Specify user')
|
|
39
39
|
.option('--gateway <url>', 'Override kanboard gateway')
|
|
40
40
|
.action(createKbAction('getTask'));
|
|
41
|
-
// createTask 支持快捷参数 --title / --project_id,且 --params 仍然可用
|
|
41
|
+
// createTask 支持快捷参数 --title / --project_id / --owner_id / --category_id,且 --params 仍然可用
|
|
42
42
|
cmd
|
|
43
43
|
.command('createTask')
|
|
44
44
|
.description(METHOD_DESCRIPTIONS.createTask)
|
|
45
45
|
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
46
46
|
.option('--title <title>', 'Task title (shortcut)')
|
|
47
47
|
.option('--project_id <id>', 'Project ID (shortcut)')
|
|
48
|
+
.option('--owner_id <id>', 'Owner user ID (shortcut)')
|
|
49
|
+
.option('--category_id <id>', 'Category ID (shortcut)')
|
|
48
50
|
.option('--user <user>', 'Specify user')
|
|
49
51
|
.option('--gateway <url>', 'Override kanboard gateway')
|
|
50
52
|
.action(createKbAction('createTask'));
|
|
@@ -85,6 +87,17 @@ const kbService = {
|
|
|
85
87
|
.option('--user <user>', 'Specify user')
|
|
86
88
|
.option('--gateway <url>', 'Override kanboard gateway')
|
|
87
89
|
.action(createKbAction('getAllComments'));
|
|
90
|
+
// createComment 支持快捷参数 --task_id / --user_id / --content
|
|
91
|
+
cmd
|
|
92
|
+
.command('createComment')
|
|
93
|
+
.description(METHOD_DESCRIPTIONS.createComment)
|
|
94
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
95
|
+
.option('--task_id <id>', 'Task ID (shortcut)')
|
|
96
|
+
.option('--user_id <id>', 'Comment author user ID (shortcut)')
|
|
97
|
+
.option('--content <content>', 'Comment content (shortcut)')
|
|
98
|
+
.option('--user <user>', 'Specify user')
|
|
99
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
100
|
+
.action(createKbAction('createComment'));
|
|
88
101
|
for (const method of METHODS.filter((item) => item !== 'getMe' &&
|
|
89
102
|
item !== 'getMyProjects' &&
|
|
90
103
|
item !== 'getTask' &&
|
|
@@ -93,7 +106,10 @@ const kbService = {
|
|
|
93
106
|
item !== 'getAllActiveIterations' &&
|
|
94
107
|
item !== 'getAllCategories' &&
|
|
95
108
|
item !== 'getAllComments' &&
|
|
109
|
+
item !== 'createComment' &&
|
|
110
|
+
item !== 'downloadProjectFile' &&
|
|
96
111
|
item !== 'createTaskFile' &&
|
|
112
|
+
item !== 'downloadTaskFile' &&
|
|
97
113
|
item !== 'createProjectFile')) {
|
|
98
114
|
cmd
|
|
99
115
|
.command(method)
|
|
@@ -113,6 +129,17 @@ const kbService = {
|
|
|
113
129
|
.option('--user <user>', 'Specify user')
|
|
114
130
|
.option('--gateway <url>', 'Override kanboard gateway')
|
|
115
131
|
.action(createKbAction('createProjectFile'));
|
|
132
|
+
// downloadProjectFile 支持快捷参数 --project_id / --file_id,可选 --out 落盘
|
|
133
|
+
cmd
|
|
134
|
+
.command('downloadProjectFile')
|
|
135
|
+
.description(METHOD_DESCRIPTIONS.downloadProjectFile)
|
|
136
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
137
|
+
.option('--project_id <id>', 'Project ID (shortcut)')
|
|
138
|
+
.option('--file_id <id>', 'Project file ID (shortcut)')
|
|
139
|
+
.option('--out <path>', 'Write decoded file to path')
|
|
140
|
+
.option('--user <user>', 'Specify user')
|
|
141
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
142
|
+
.action(createKbAction('downloadProjectFile'));
|
|
116
143
|
// createTaskFile 支持快捷参数 --file / --task_id / --project_id,自动转 base64
|
|
117
144
|
cmd
|
|
118
145
|
.command('createTaskFile')
|
|
@@ -124,6 +151,16 @@ const kbService = {
|
|
|
124
151
|
.option('--user <user>', 'Specify user')
|
|
125
152
|
.option('--gateway <url>', 'Override kanboard gateway')
|
|
126
153
|
.action(createKbAction('createTaskFile'));
|
|
154
|
+
// downloadTaskFile 支持快捷参数 --file_id,可选 --out 落盘
|
|
155
|
+
cmd
|
|
156
|
+
.command('downloadTaskFile')
|
|
157
|
+
.description(METHOD_DESCRIPTIONS.downloadTaskFile)
|
|
158
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
159
|
+
.option('--file_id <id>', 'Task file ID (shortcut)')
|
|
160
|
+
.option('--out <path>', 'Write decoded file to path')
|
|
161
|
+
.option('--user <user>', 'Specify user')
|
|
162
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
163
|
+
.action(createKbAction('downloadTaskFile'));
|
|
127
164
|
},
|
|
128
165
|
};
|
|
129
166
|
export default kbService;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ParamType = 'integer' | 'string' | 'array<integer>' | 'array<string>' | 'object' | 'file';
|
|
1
|
+
export type ParamType = 'integer' | 'number' | 'string' | 'array<integer>' | 'array<string>' | 'object' | 'file';
|
|
2
2
|
export interface ParamDef {
|
|
3
3
|
name: string;
|
|
4
4
|
type: ParamType;
|
|
@@ -43,8 +43,8 @@ export const PARAM_DEFS = {
|
|
|
43
43
|
{ name: 'task_id', type: 'integer', required: true, description: '父需求/bug ID' },
|
|
44
44
|
{ name: 'title', type: 'string', required: true, description: '子任务标题' },
|
|
45
45
|
{ name: 'user_id', type: 'integer', required: false, description: '子任务处理人 Kanboard 用户 ID' },
|
|
46
|
-
{ name: 'time_estimated', type: '
|
|
47
|
-
{ name: 'time_spent', type: '
|
|
46
|
+
{ name: 'time_estimated', type: 'number', required: false, description: '预估时间(小时)' },
|
|
47
|
+
{ name: 'time_spent', type: 'number', required: false, description: '已花费时间(小时)' },
|
|
48
48
|
{ name: 'status', type: 'integer', required: false, description: '状态:0 未开始,1 进行中,2 已完成' },
|
|
49
49
|
],
|
|
50
50
|
updateTask: [
|
|
@@ -56,6 +56,16 @@ export const PARAM_DEFS = {
|
|
|
56
56
|
{ name: 'iteration_id', type: 'integer', required: false, description: '迭代 ID' },
|
|
57
57
|
{ name: 'column_id', type: 'integer', required: false, description: '状态列 ID' },
|
|
58
58
|
],
|
|
59
|
+
updateSubtask: [
|
|
60
|
+
{ name: 'id', type: 'integer', required: true, description: '子任务 ID' },
|
|
61
|
+
{ name: 'task_id', type: 'integer', required: true, description: '父需求/bug ID' },
|
|
62
|
+
{ name: 'title', type: 'string', required: false, description: '子任务标题' },
|
|
63
|
+
{ name: 'user_id', type: 'integer', required: false, description: '子任务处理人,传 0 表示未分配' },
|
|
64
|
+
{ name: 'time_estimated', type: 'number', required: false, description: '预估时间(小时)' },
|
|
65
|
+
{ name: 'time_spent', type: 'number', required: false, description: '已花费时间(小时)' },
|
|
66
|
+
{ name: 'status', type: 'integer', required: false, description: '状态:0 未开始,1 进行中,2 已完成' },
|
|
67
|
+
{ name: 'position', type: 'integer', required: false, description: '子任务排序位置' },
|
|
68
|
+
],
|
|
59
69
|
createComment: [
|
|
60
70
|
{ name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
61
71
|
{ name: 'user_id', type: 'integer', required: true, description: '评论作者 Kanboard 用户 ID' },
|
|
@@ -67,10 +77,17 @@ export const PARAM_DEFS = {
|
|
|
67
77
|
{ name: 'filename', type: 'string', required: true, description: '文件名' },
|
|
68
78
|
{ name: 'blob', type: 'string', required: true, description: '文件内容 Base64 编码' },
|
|
69
79
|
],
|
|
80
|
+
downloadProjectFile: [
|
|
81
|
+
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
|
|
82
|
+
{ name: 'file_id', type: 'integer', required: true, description: '项目附件 ID' },
|
|
83
|
+
],
|
|
70
84
|
createTaskFile: [
|
|
71
85
|
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
|
|
72
86
|
{ name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
73
87
|
{ name: 'filename', type: 'string', required: true, description: '文件名' },
|
|
74
88
|
{ name: 'blob', type: 'string', required: true, description: '文件内容 Base64 编码' },
|
|
75
89
|
],
|
|
90
|
+
downloadTaskFile: [
|
|
91
|
+
{ name: 'file_id', type: 'integer', required: true, description: '需求附件 ID' },
|
|
92
|
+
],
|
|
76
93
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const METHOD_SKILL_METADATA = {
|
|
2
|
+
downloadProjectFile: {
|
|
3
|
+
notes: [
|
|
4
|
+
'当附件链接包含 `project_id` 时,使用 `downloadProjectFile`。',
|
|
5
|
+
'例如:`/FileViewer/browser?project_id=4&file_id=168`。',
|
|
6
|
+
'例如:`/?controller=FileViewerController&action=browser&project_id=4&file_id=168`。',
|
|
7
|
+
],
|
|
8
|
+
},
|
|
9
|
+
downloadTaskFile: {
|
|
10
|
+
notes: [
|
|
11
|
+
'当附件链接包含 `task_id` 时,使用 `downloadTaskFile`。',
|
|
12
|
+
'例如:`?controller=FileViewerController&action=browser&task_id=2000757&file_id=3870`。',
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
};
|