@meet-im/lxcli 0.0.3 → 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.
@@ -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-test.miyachat.com',
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-beta.miyachat.com',
8
+ beta: 'https://kanboard-staging.miyachat.com',
9
9
  prod: 'https://kanboard.miyachat.com',
10
10
  };
11
11
  const coGateways = {
12
- test: 'https://kanboard-test.co.example.com',
13
- pre: 'https://kanboard-pre.co.example.com',
14
- staging: 'https://kanboard-staging.co.example.com',
15
- beta: 'https://kanboard-beta.co.example.com',
16
- prod: 'https://kanboard.co.example.com',
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-test.sky.example.com',
20
- pre: 'https://kanboard-pre.sky.example.com',
21
- staging: 'https://kanboard-staging.sky.example.com',
22
- beta: 'https://kanboard-beta.sky.example.com',
23
- prod: 'https://kanboard.sky.example.com',
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',
@@ -20,7 +20,9 @@ export const METHOD_DESCRIPTIONS = {
20
20
  getAllComments: '获取评论列表',
21
21
  createComment: '对需求增加评论',
22
22
  createProjectFile: '上传附件到项目',
23
+ downloadProjectFile: '下载项目附件',
23
24
  createTaskFile: '上传附件到需求',
25
+ downloadTaskFile: '下载需求附件',
24
26
  };
25
27
  // 重新导出 params.ts 的类型和定义,方便其他模块使用
26
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
- if (projectId === null) {
93
- outputCliError('PARAM_INVALID', '--project_id must be a positive integer');
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 --project_id',
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;
@@ -77,10 +77,17 @@ export const PARAM_DEFS = {
77
77
  { name: 'filename', type: 'string', required: true, description: '文件名' },
78
78
  { name: 'blob', type: 'string', required: true, description: '文件内容 Base64 编码' },
79
79
  ],
80
+ downloadProjectFile: [
81
+ { name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
82
+ { name: 'file_id', type: 'integer', required: true, description: '项目附件 ID' },
83
+ ],
80
84
  createTaskFile: [
81
85
  { name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
82
86
  { name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
83
87
  { name: 'filename', type: 'string', required: true, description: '文件名' },
84
88
  { name: 'blob', type: 'string', required: true, description: '文件内容 Base64 编码' },
85
89
  ],
90
+ downloadTaskFile: [
91
+ { name: 'file_id', type: 'integer', required: true, description: '需求附件 ID' },
92
+ ],
86
93
  };
@@ -0,0 +1,5 @@
1
+ import type { Method } from './descriptions.js';
2
+ export interface MethodSkillMetadata {
3
+ notes?: string[];
4
+ }
5
+ export declare const METHOD_SKILL_METADATA: Partial<Record<Method, MethodSkillMetadata>>;
@@ -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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meet-im/lxcli",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "连续科技命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",