@meet-im/lxcli 0.0.5 → 0.0.6
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 +34 -24
- package/dist/commands/services/kb/descriptions.js +1 -0
- package/dist/commands/services/kb/params.js +7 -0
- package/dist/commands/services/kb/rpc.js +1 -1
- package/dist/commands/services/kb/skill-metadata.d.ts +1 -0
- package/dist/commands/services/kb/skill-metadata.js +13 -0
- package/dist/core/http.d.ts +17 -27
- package/dist/core/http.js +15 -25
- package/package.json +1 -1
|
@@ -4,7 +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
|
+
import { METHOD_SHORTCUT_PARAMS, METHOD_SKILL_METADATA } from './services/kb/skill-metadata.js';
|
|
8
8
|
const API_ROOT = path.resolve('api');
|
|
9
9
|
const DEFAULT_SKILLS_DIR = path.resolve('api/skills');
|
|
10
10
|
const DEFAULT_OUTPUT = 'api/docs/skills.md';
|
|
@@ -155,41 +155,51 @@ async function readPackageVersion() {
|
|
|
155
155
|
const packageJson = JSON.parse(await fs.readFile(PACKAGE_JSON_PATH, 'utf-8'));
|
|
156
156
|
return packageJson.version || '1.0.0';
|
|
157
157
|
}
|
|
158
|
+
function buildParamsExample(params) {
|
|
159
|
+
const paramsExample = {};
|
|
160
|
+
for (const p of params) {
|
|
161
|
+
if (p.type === 'array<integer>') {
|
|
162
|
+
paramsExample[p.name] = [1, 2];
|
|
163
|
+
}
|
|
164
|
+
else if (p.type === 'array<string>') {
|
|
165
|
+
paramsExample[p.name] = ['id1', 'id2'];
|
|
166
|
+
}
|
|
167
|
+
else if (p.type === 'integer') {
|
|
168
|
+
paramsExample[p.name] = 123;
|
|
169
|
+
}
|
|
170
|
+
else if (p.type === 'object') {
|
|
171
|
+
paramsExample[p.name] = {};
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
paramsExample[p.name] = 'value';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return paramsExample;
|
|
178
|
+
}
|
|
158
179
|
function renderSkillMd(method, version) {
|
|
159
180
|
const description = METHOD_DESCRIPTIONS[method];
|
|
160
181
|
const params = PARAM_DEFS[method] || [];
|
|
161
182
|
const metadata = METHOD_SKILL_METADATA[method];
|
|
162
183
|
const skillName = `lxcli-kb-${method}`;
|
|
163
184
|
const hasParams = params.length > 0;
|
|
164
|
-
//
|
|
165
|
-
const
|
|
185
|
+
// 快捷参数列表(显式配置,不从 required 自动推断)
|
|
186
|
+
const shortcutParams = METHOD_SHORTCUT_PARAMS[method] || [];
|
|
187
|
+
const shortcuts = shortcutParams.map((name) => {
|
|
188
|
+
const param = params.find((p) => p.name === name);
|
|
189
|
+
if (!param) {
|
|
190
|
+
return `--${name} <VALUE>`;
|
|
191
|
+
}
|
|
192
|
+
return `--${param.name} <${param.type === 'integer' ? 'ID' : 'VALUE'}>`;
|
|
193
|
+
});
|
|
166
194
|
const shortcutUsage = shortcuts.length > 0 ? `lxcli kb ${method} ${shortcuts.join(' ')}` : '';
|
|
195
|
+
const paramsUsage = hasParams ? `lxcli kb ${method} --params '${JSON.stringify(buildParamsExample(params))}'` : '';
|
|
167
196
|
// Usage 部分
|
|
168
197
|
const usageLines = ['## Usage', '', '```bash'];
|
|
169
198
|
if (hasParams) {
|
|
170
199
|
if (shortcutUsage) {
|
|
171
200
|
usageLines.push(shortcutUsage);
|
|
172
201
|
}
|
|
173
|
-
|
|
174
|
-
const paramsExample = {};
|
|
175
|
-
for (const p of params) {
|
|
176
|
-
if (p.type === 'array<integer>') {
|
|
177
|
-
paramsExample[p.name] = [1, 2];
|
|
178
|
-
}
|
|
179
|
-
else if (p.type === 'array<string>') {
|
|
180
|
-
paramsExample[p.name] = ['id1', 'id2'];
|
|
181
|
-
}
|
|
182
|
-
else if (p.type === 'integer') {
|
|
183
|
-
paramsExample[p.name] = 123;
|
|
184
|
-
}
|
|
185
|
-
else if (p.type === 'object') {
|
|
186
|
-
paramsExample[p.name] = {};
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
paramsExample[p.name] = 'value';
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
usageLines.push(`lxcli kb ${method} --params '${JSON.stringify(paramsExample)}'`);
|
|
202
|
+
usageLines.push(paramsUsage);
|
|
193
203
|
}
|
|
194
204
|
else {
|
|
195
205
|
usageLines.push(`lxcli kb ${method}`);
|
|
@@ -224,7 +234,7 @@ metadata:
|
|
|
224
234
|
if (hasParams) {
|
|
225
235
|
lines.push(renderParamsTable(params));
|
|
226
236
|
}
|
|
227
|
-
lines.push('## Examples', '', '```bash', shortcutUsage || `lxcli kb ${method}`, '```', '');
|
|
237
|
+
lines.push('## Examples', '', '```bash', shortcutUsage || paramsUsage || `lxcli kb ${method}`, '```', '');
|
|
228
238
|
if (metadata?.notes?.length) {
|
|
229
239
|
lines.push('## Notes', '', ...metadata.notes.map((note) => `- ${note}`), '');
|
|
230
240
|
}
|
|
@@ -90,4 +90,11 @@ export const PARAM_DEFS = {
|
|
|
90
90
|
downloadTaskFile: [
|
|
91
91
|
{ name: 'file_id', type: 'integer', required: true, description: '需求附件 ID' },
|
|
92
92
|
],
|
|
93
|
+
moveTaskPosition: [
|
|
94
|
+
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID。必须是任务所属项目。' },
|
|
95
|
+
{ name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
96
|
+
{ name: 'column_id', type: 'integer', required: false, description: '目标状态列 ID。未传时保留当前状态列' },
|
|
97
|
+
{ name: 'position', type: 'integer', required: false, description: '目标列内排序位置,必须 >= 1。未传时保留当前排序值' },
|
|
98
|
+
{ name: 'swimlane_id', type: 'integer', required: false, description: '目标泳道 ID。未传或传 0 保留当前泳道' },
|
|
99
|
+
],
|
|
93
100
|
};
|
|
@@ -20,7 +20,7 @@ export async function jsonRpcCall(gateway, method, params, pat) {
|
|
|
20
20
|
throwOnError: false,
|
|
21
21
|
});
|
|
22
22
|
// HTTP 层错误
|
|
23
|
-
if (result.
|
|
23
|
+
if (!result.ok) {
|
|
24
24
|
outputCliError('NETWORK_ERROR', result.error.message, {
|
|
25
25
|
code: result.error.code,
|
|
26
26
|
details: result.error.details,
|
|
@@ -2,4 +2,5 @@ import type { Method } from './descriptions.js';
|
|
|
2
2
|
export interface MethodSkillMetadata {
|
|
3
3
|
notes?: string[];
|
|
4
4
|
}
|
|
5
|
+
export declare const METHOD_SHORTCUT_PARAMS: Partial<Record<Method, string[]>>;
|
|
5
6
|
export declare const METHOD_SKILL_METADATA: Partial<Record<Method, MethodSkillMetadata>>;
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
export const METHOD_SHORTCUT_PARAMS = {
|
|
2
|
+
getTask: ['task_id'],
|
|
3
|
+
createTask: ['title', 'project_id', 'owner_id', 'category_id'],
|
|
4
|
+
searchTasks: ['project_id', 'query'],
|
|
5
|
+
getAllActiveIterations: ['project_id'],
|
|
6
|
+
getAllCategories: ['project_id'],
|
|
7
|
+
getAllComments: ['task_id'],
|
|
8
|
+
createComment: ['task_id', 'user_id', 'content'],
|
|
9
|
+
createProjectFile: ['file', 'project_id'],
|
|
10
|
+
downloadProjectFile: ['project_id', 'file_id'],
|
|
11
|
+
createTaskFile: ['file', 'task_id', 'project_id'],
|
|
12
|
+
downloadTaskFile: ['file_id'],
|
|
13
|
+
};
|
|
1
14
|
export const METHOD_SKILL_METADATA = {
|
|
2
15
|
downloadProjectFile: {
|
|
3
16
|
notes: [
|
package/dist/core/http.d.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
export interface
|
|
2
|
-
|
|
1
|
+
export interface RequestSuccessResult<T> {
|
|
2
|
+
ok: true;
|
|
3
3
|
status: number;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
data: T;
|
|
5
|
+
}
|
|
6
|
+
export interface RequestErrorResult {
|
|
7
|
+
ok: false;
|
|
8
|
+
status: number;
|
|
9
|
+
error: {
|
|
8
10
|
code: number;
|
|
9
11
|
message: string;
|
|
10
12
|
details?: unknown;
|
|
11
13
|
};
|
|
12
14
|
}
|
|
13
|
-
export
|
|
15
|
+
export type RequestResult<T> = RequestSuccessResult<T> | RequestErrorResult;
|
|
16
|
+
interface RequestOptionsBase {
|
|
14
17
|
/** gateway 地址(必填) */
|
|
15
18
|
gateway: string;
|
|
16
19
|
/** 请求路径(不含域名) */
|
|
@@ -27,29 +30,15 @@ export interface RequestOptionsWithThrow {
|
|
|
27
30
|
userAgent?: string;
|
|
28
31
|
/** 最大重试次数,默认 3 */
|
|
29
32
|
maxRetries?: number;
|
|
30
|
-
/** 错误时抛出异常而非直接退出,默认 true */
|
|
31
|
-
throwOnError?: true;
|
|
32
33
|
}
|
|
33
|
-
export
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/** HTTP 方法,默认 GET */
|
|
39
|
-
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
40
|
-
/** URL query 参数 */
|
|
41
|
-
query?: Record<string, string | number | boolean>;
|
|
42
|
-
/** 请求体 */
|
|
43
|
-
body?: Record<string, unknown>;
|
|
44
|
-
/** 自定义 headers(会覆盖默认 headers) */
|
|
45
|
-
headers?: Record<string, string>;
|
|
46
|
-
/** User-Agent,默认 lxcli/版本号 */
|
|
47
|
-
userAgent?: string;
|
|
48
|
-
/** 最大重试次数,默认 3 */
|
|
49
|
-
maxRetries?: number;
|
|
34
|
+
export type RequestOptionsWithThrow = RequestOptionsBase & {
|
|
35
|
+
/** 错误时调用 outputApiError 退出进程,默认 true */
|
|
36
|
+
throwOnError?: true;
|
|
37
|
+
};
|
|
38
|
+
export type RequestOptionsWithoutThrow = RequestOptionsBase & {
|
|
50
39
|
/** 错误时返回 RequestResult 而非退出 */
|
|
51
40
|
throwOnError: false;
|
|
52
|
-
}
|
|
41
|
+
};
|
|
53
42
|
export type RequestOptions = RequestOptionsWithThrow | RequestOptionsWithoutThrow;
|
|
54
43
|
/**
|
|
55
44
|
* 通用请求函数
|
|
@@ -64,3 +53,4 @@ export type RequestOptions = RequestOptionsWithThrow | RequestOptionsWithoutThro
|
|
|
64
53
|
*/
|
|
65
54
|
export declare function request<T>(options: RequestOptionsWithThrow): Promise<T>;
|
|
66
55
|
export declare function request<T>(options: RequestOptionsWithoutThrow): Promise<RequestResult<T>>;
|
|
56
|
+
export {};
|
package/dist/core/http.js
CHANGED
|
@@ -56,12 +56,16 @@ export async function request(options) {
|
|
|
56
56
|
};
|
|
57
57
|
const requestBody = body ? JSON.stringify(body) : undefined;
|
|
58
58
|
let lastError = null;
|
|
59
|
-
//
|
|
60
|
-
const
|
|
59
|
+
// 辅助函数:构造错误结果
|
|
60
|
+
const errorResult = (status, code, message, details) => {
|
|
61
|
+
return { ok: false, status, error: { code, message, details } };
|
|
62
|
+
};
|
|
63
|
+
// 辅助函数:处理错误(throwOnError 时退出,否则返回错误结果)
|
|
64
|
+
const handleError = (result) => {
|
|
61
65
|
if (throwOnError) {
|
|
62
|
-
outputApiError(code, message, details, status);
|
|
66
|
+
outputApiError(result.error.code, result.error.message, result.error.details, result.status);
|
|
63
67
|
}
|
|
64
|
-
return
|
|
68
|
+
return result;
|
|
65
69
|
};
|
|
66
70
|
// 发送请求(带重试)
|
|
67
71
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
@@ -92,32 +96,22 @@ export async function request(options) {
|
|
|
92
96
|
await sleep(delay);
|
|
93
97
|
continue;
|
|
94
98
|
}
|
|
95
|
-
|
|
96
|
-
if (!throwOnError)
|
|
97
|
-
return result;
|
|
98
|
-
// throwOnError 为 true 时 makeErrorResult 已经调用 outputApiError 退出了
|
|
99
|
+
return handleError(errorResult(response.status, errorResponse?.error?.code || response.status, errorResponse?.error?.message || 'Request failed', errorResponse?.error?.details));
|
|
99
100
|
}
|
|
100
101
|
let responseData;
|
|
101
102
|
try {
|
|
102
103
|
responseData = parseJsonResponse(responseText);
|
|
103
104
|
}
|
|
104
105
|
catch {
|
|
105
|
-
|
|
106
|
-
if (!throwOnError)
|
|
107
|
-
return result;
|
|
108
|
-
// throwOnError 为 true 时 makeErrorResult 已经调用 outputApiError 退出了
|
|
106
|
+
return handleError(errorResult(response.status, response.status, `Invalid JSON response: ${responseText}`));
|
|
109
107
|
}
|
|
110
108
|
logger.debug('Response', { status: response.status, data: responseData, attempt });
|
|
111
109
|
if (!response.ok) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
if (!throwOnError)
|
|
115
|
-
return result;
|
|
116
|
-
// throwOnError 为 true 时 makeErrorResult 已经调用 outputApiError 退出了
|
|
110
|
+
const errResp = responseData;
|
|
111
|
+
return handleError(errorResult(response.status, errResp?.error?.code || response.status, errResp?.error?.message || 'Request failed', errResp?.error?.details));
|
|
117
112
|
}
|
|
118
|
-
// throwOnError 为 false 时返回完整结果
|
|
119
113
|
if (!throwOnError) {
|
|
120
|
-
return { status: response.status, data: responseData };
|
|
114
|
+
return { ok: true, status: response.status, data: responseData };
|
|
121
115
|
}
|
|
122
116
|
return responseData;
|
|
123
117
|
}
|
|
@@ -136,9 +130,7 @@ export async function request(options) {
|
|
|
136
130
|
}
|
|
137
131
|
if (isNetworkError) {
|
|
138
132
|
logger.error('Request failed', error);
|
|
139
|
-
|
|
140
|
-
if (!throwOnError)
|
|
141
|
-
return result;
|
|
133
|
+
return handleError(errorResult(500, 500, error.message));
|
|
142
134
|
}
|
|
143
135
|
throw error;
|
|
144
136
|
}
|
|
@@ -146,9 +138,7 @@ export async function request(options) {
|
|
|
146
138
|
// 所有重试都失败了
|
|
147
139
|
if (lastError) {
|
|
148
140
|
logger.error('All retries failed', lastError);
|
|
149
|
-
|
|
150
|
-
if (!throwOnError)
|
|
151
|
-
return result;
|
|
141
|
+
return handleError(errorResult(500, 500, lastError.message));
|
|
152
142
|
}
|
|
153
143
|
// 不应该到达这里,但 TypeScript 需要返回值
|
|
154
144
|
throw new Error('Request failed after all retries');
|