@meet-im/lxcli 0.0.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/LICENSE +15 -0
- package/README.md +3 -0
- package/bin/lxcli.js +2 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +45 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +28 -0
- package/dist/commands/generate-skills.d.ts +2 -0
- package/dist/commands/generate-skills.js +283 -0
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.js +43 -0
- package/dist/commands/schema.d.ts +2 -0
- package/dist/commands/schema.js +37 -0
- package/dist/commands/services/kb/config.d.ts +2 -0
- package/dist/commands/services/kb/config.js +32 -0
- package/dist/commands/services/kb/descriptions.d.ts +4 -0
- package/dist/commands/services/kb/descriptions.js +24 -0
- package/dist/commands/services/kb/handler.d.ts +12 -0
- package/dist/commands/services/kb/handler.js +179 -0
- package/dist/commands/services/kb/index.d.ts +3 -0
- package/dist/commands/services/kb/index.js +119 -0
- package/dist/commands/services/kb/params.d.ts +8 -0
- package/dist/commands/services/kb/params.js +73 -0
- package/dist/commands/services/kb/rpc.d.ts +1 -0
- package/dist/commands/services/kb/rpc.js +35 -0
- package/dist/commands/services/kb/types.d.ts +97 -0
- package/dist/commands/services/kb/types.js +2 -0
- package/dist/commands/services/kb/utils.d.ts +1 -0
- package/dist/commands/services/kb/utils.js +4 -0
- package/dist/commands/services/meet/api.d.ts +13 -0
- package/dist/commands/services/meet/api.js +38 -0
- package/dist/commands/services/meet/config.d.ts +4 -0
- package/dist/commands/services/meet/config.js +16 -0
- package/dist/commands/services/meet/descriptions.d.ts +3 -0
- package/dist/commands/services/meet/descriptions.js +7 -0
- package/dist/commands/services/meet/handler.d.ts +25 -0
- package/dist/commands/services/meet/handler.js +50 -0
- package/dist/commands/services/meet/index.d.ts +3 -0
- package/dist/commands/services/meet/index.js +43 -0
- package/dist/commands/services/meet/types.d.ts +65 -0
- package/dist/commands/services/meet/types.js +2 -0
- package/dist/commands/services/meetbot/api.d.ts +37 -0
- package/dist/commands/services/meetbot/api.js +130 -0
- package/dist/commands/services/meetbot/config.d.ts +4 -0
- package/dist/commands/services/meetbot/config.js +21 -0
- package/dist/commands/services/meetbot/descriptions.d.ts +3 -0
- package/dist/commands/services/meetbot/descriptions.js +10 -0
- package/dist/commands/services/meetbot/handler.d.ts +42 -0
- package/dist/commands/services/meetbot/handler.js +83 -0
- package/dist/commands/services/meetbot/index.d.ts +3 -0
- package/dist/commands/services/meetbot/index.js +74 -0
- package/dist/commands/services/meetbot/types.d.ts +101 -0
- package/dist/commands/services/meetbot/types.js +2 -0
- package/dist/commands/services/meetbot/utils.d.ts +28 -0
- package/dist/commands/services/meetbot/utils.js +78 -0
- package/dist/commands/services.d.ts +2 -0
- package/dist/commands/services.js +50 -0
- package/dist/commands/tenants.d.ts +2 -0
- package/dist/commands/tenants.js +46 -0
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +38 -0
- package/dist/commands/users.d.ts +2 -0
- package/dist/commands/users.js +182 -0
- package/dist/core/auth.d.ts +26 -0
- package/dist/core/auth.js +75 -0
- package/dist/core/config.d.ts +76 -0
- package/dist/core/config.js +255 -0
- package/dist/core/error.d.ts +128 -0
- package/dist/core/error.js +85 -0
- package/dist/core/help.d.ts +6 -0
- package/dist/core/help.js +15 -0
- package/dist/core/http.d.ts +28 -0
- package/dist/core/http.js +140 -0
- package/dist/core/logger.d.ts +24 -0
- package/dist/core/logger.js +53 -0
- package/dist/core/reserved.d.ts +13 -0
- package/dist/core/reserved.js +28 -0
- package/dist/core/tenants.d.ts +36 -0
- package/dist/core/tenants.js +50 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +152 -0
- package/dist/types/index.d.ts +59 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/index.d.ts +20 -0
- package/dist/utils/index.js +133 -0
- package/dist/utils/loose-params.d.ts +9 -0
- package/dist/utils/loose-params.js +274 -0
- package/dist/utils/table.d.ts +4 -0
- package/dist/utils/table.js +25 -0
- package/package.json +46 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { basename } from 'path';
|
|
3
|
+
import { getCurrentUser, getGatewayForUser } from '../../../core/auth.js';
|
|
4
|
+
import { outputCliError } from '../../../core/error.js';
|
|
5
|
+
import { outputJson, parseCliParams } from '../../../utils/index.js';
|
|
6
|
+
import { jsonRpcCall } from './rpc.js';
|
|
7
|
+
function parseTaskId(value) {
|
|
8
|
+
if (!/^\d+$/.test(value)) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const taskId = Number(value);
|
|
12
|
+
return Number.isSafeInteger(taskId) && taskId > 0 ? taskId : null;
|
|
13
|
+
}
|
|
14
|
+
export function createKbAction(method) {
|
|
15
|
+
return async (options) => {
|
|
16
|
+
const user = getCurrentUser(options.user);
|
|
17
|
+
if (!user) {
|
|
18
|
+
outputCliError('USER_NOT_FOUND', 'No user configured. Run "lxcli auth" first.');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const gateway = options.gateway || getGatewayForUser(user, 'kb');
|
|
22
|
+
let params;
|
|
23
|
+
// --params 优先级高于快捷参数
|
|
24
|
+
if (options.params) {
|
|
25
|
+
try {
|
|
26
|
+
params = parseCliParams(options.params);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
outputCliError('PARAM_INVALID', error instanceof Error ? error.message : `Invalid JSON in --params: ${options.params}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (method === 'queryUsers') {
|
|
33
|
+
if (!params || typeof params !== 'object' || Array.isArray(params)) {
|
|
34
|
+
outputCliError('PARAM_INVALID', 'queryUsers --params must be an object');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const p = params;
|
|
38
|
+
const hasUserIds = Array.isArray(p.user_ids) && p.user_ids.length > 0;
|
|
39
|
+
const hasOauth2Uid = Array.isArray(p.oauth2_uid) && p.oauth2_uid.length > 0;
|
|
40
|
+
if (!hasUserIds && !hasOauth2Uid) {
|
|
41
|
+
outputCliError('PARAM_INVALID', 'queryUsers requires at least one of user_ids or oauth2_uid');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// createTaskFile 特殊处理:--params 里的 file 字段自动转 blob
|
|
46
|
+
if (method === 'createTaskFile' && params && typeof params === 'object' && !Array.isArray(params)) {
|
|
47
|
+
const p = params;
|
|
48
|
+
if (typeof p.file === 'string') {
|
|
49
|
+
try {
|
|
50
|
+
const fileBuffer = await readFile(p.file);
|
|
51
|
+
p.blob = fileBuffer.toString('base64');
|
|
52
|
+
if (!p.filename) {
|
|
53
|
+
p.filename = basename(p.file);
|
|
54
|
+
}
|
|
55
|
+
delete p.file;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
outputCliError('PARAM_INVALID', `Failed to read file: ${p.file}`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// createProjectFile 特殊处理:--params 里的 file 字段自动转 blob
|
|
64
|
+
if (method === 'createProjectFile' && params && typeof params === 'object' && !Array.isArray(params)) {
|
|
65
|
+
const p = params;
|
|
66
|
+
if (typeof p.file === 'string') {
|
|
67
|
+
try {
|
|
68
|
+
const fileBuffer = await readFile(p.file);
|
|
69
|
+
p.blob = fileBuffer.toString('base64');
|
|
70
|
+
if (!p.filename) {
|
|
71
|
+
p.filename = basename(p.file);
|
|
72
|
+
}
|
|
73
|
+
delete p.file;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
outputCliError('PARAM_INVALID', `Failed to read file: ${p.file}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (method === 'getTask' && options.task_id) {
|
|
83
|
+
const taskId = parseTaskId(options.task_id);
|
|
84
|
+
if (taskId === null) {
|
|
85
|
+
outputCliError('PARAM_INVALID', '--task_id must be a positive integer');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
params = { task_id: taskId };
|
|
89
|
+
}
|
|
90
|
+
else if (method === 'createTask' && options.title && options.project_id) {
|
|
91
|
+
const projectId = parseTaskId(options.project_id);
|
|
92
|
+
if (projectId === null) {
|
|
93
|
+
outputCliError('PARAM_INVALID', '--project_id must be a positive integer');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
params = { title: options.title, project_id: projectId };
|
|
97
|
+
}
|
|
98
|
+
else if (method === 'searchTasks' && options.project_id && options.query) {
|
|
99
|
+
const projectId = parseTaskId(options.project_id);
|
|
100
|
+
if (projectId === null) {
|
|
101
|
+
outputCliError('PARAM_INVALID', '--project_id must be a positive integer');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
params = { project_id: projectId, query: options.query };
|
|
105
|
+
}
|
|
106
|
+
else if (method === 'getAllActiveIterations' && options.project_id) {
|
|
107
|
+
const projectId = parseTaskId(options.project_id);
|
|
108
|
+
if (projectId === null) {
|
|
109
|
+
outputCliError('PARAM_INVALID', '--project_id must be a positive integer');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
params = { project_id: projectId };
|
|
113
|
+
}
|
|
114
|
+
else if (method === 'getAllComments' && options.task_id) {
|
|
115
|
+
const taskId = parseTaskId(options.task_id);
|
|
116
|
+
if (taskId === null) {
|
|
117
|
+
outputCliError('PARAM_INVALID', '--task_id must be a positive integer');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
params = { task_id: taskId };
|
|
121
|
+
}
|
|
122
|
+
else if (method === 'createProjectFile' && options.file && options.project_id) {
|
|
123
|
+
const projectId = parseTaskId(options.project_id);
|
|
124
|
+
if (projectId === null) {
|
|
125
|
+
outputCliError('PARAM_INVALID', '--project_id must be a positive integer');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const fileBuffer = await readFile(options.file);
|
|
130
|
+
const blob = fileBuffer.toString('base64');
|
|
131
|
+
const filename = basename(options.file);
|
|
132
|
+
params = { project_id: projectId, filename, blob };
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
outputCliError('PARAM_INVALID', `Failed to read file: ${options.file}`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (method === 'createTaskFile' && options.file && options.task_id && options.project_id) {
|
|
140
|
+
const taskId = parseTaskId(options.task_id);
|
|
141
|
+
const projectId = parseTaskId(options.project_id);
|
|
142
|
+
if (taskId === null || projectId === null) {
|
|
143
|
+
outputCliError('PARAM_INVALID', '--task_id and --project_id must be positive integers');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const fileBuffer = await readFile(options.file);
|
|
148
|
+
const blob = fileBuffer.toString('base64');
|
|
149
|
+
const filename = basename(options.file);
|
|
150
|
+
params = { project_id: projectId, task_id: taskId, filename, blob };
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
outputCliError('PARAM_INVALID', `Failed to read file: ${options.file}`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (method === 'getMe' || method === 'getMyProjects') {
|
|
158
|
+
// 无参数方法
|
|
159
|
+
params = {};
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const shortcuts = {
|
|
163
|
+
getTask: '--task_id',
|
|
164
|
+
createTask: '--title and --project_id',
|
|
165
|
+
searchTasks: '--project_id and --query',
|
|
166
|
+
getAllActiveIterations: '--project_id',
|
|
167
|
+
getAllComments: '--task_id',
|
|
168
|
+
createProjectFile: '--file and --project_id',
|
|
169
|
+
createTaskFile: '--file, --task_id and --project_id',
|
|
170
|
+
};
|
|
171
|
+
outputCliError('PARAM_INVALID', shortcuts[method]
|
|
172
|
+
? `Missing required options: ${shortcuts[method]} or --params.`
|
|
173
|
+
: 'Missing required option: --params.');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const result = await jsonRpcCall(gateway, method, params, user.token);
|
|
177
|
+
outputJson(result);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// src/commands/services/kb/index.ts
|
|
2
|
+
import { registerService } from '../../../core/tenants.js';
|
|
3
|
+
import { kbConfig } from './config.js';
|
|
4
|
+
import { METHODS, METHOD_DESCRIPTIONS } from './descriptions.js';
|
|
5
|
+
import { createKbAction } from './handler.js';
|
|
6
|
+
const kbService = {
|
|
7
|
+
name: 'kb',
|
|
8
|
+
description: 'Kanboard JSON-RPC API',
|
|
9
|
+
enabled: true,
|
|
10
|
+
register(cmd) {
|
|
11
|
+
registerService(kbConfig);
|
|
12
|
+
cmd.description(this.description).allowUnknownOption(false);
|
|
13
|
+
// 无子命令时显示帮助
|
|
14
|
+
cmd.action(() => {
|
|
15
|
+
console.log(cmd.helpInformation());
|
|
16
|
+
process.exitCode = 0;
|
|
17
|
+
});
|
|
18
|
+
// getMe 无参数
|
|
19
|
+
cmd
|
|
20
|
+
.command('getMe')
|
|
21
|
+
.description(METHOD_DESCRIPTIONS.getMe)
|
|
22
|
+
.option('--user <user>', 'Specify user')
|
|
23
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
24
|
+
.action(createKbAction('getMe'));
|
|
25
|
+
// getMyProjects 无参数
|
|
26
|
+
cmd
|
|
27
|
+
.command('getMyProjects')
|
|
28
|
+
.description(METHOD_DESCRIPTIONS.getMyProjects)
|
|
29
|
+
.option('--user <user>', 'Specify user')
|
|
30
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
31
|
+
.action(createKbAction('getMyProjects'));
|
|
32
|
+
// getTask 支持快捷参数 --task_id,且 --params 仍然可用
|
|
33
|
+
cmd
|
|
34
|
+
.command('getTask')
|
|
35
|
+
.description(METHOD_DESCRIPTIONS.getTask)
|
|
36
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
37
|
+
.option('--task_id <id>', 'Task ID (shortcut for --params \'{"task_id": <id>}\')')
|
|
38
|
+
.option('--user <user>', 'Specify user')
|
|
39
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
40
|
+
.action(createKbAction('getTask'));
|
|
41
|
+
// createTask 支持快捷参数 --title / --project_id,且 --params 仍然可用
|
|
42
|
+
cmd
|
|
43
|
+
.command('createTask')
|
|
44
|
+
.description(METHOD_DESCRIPTIONS.createTask)
|
|
45
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
46
|
+
.option('--title <title>', 'Task title (shortcut)')
|
|
47
|
+
.option('--project_id <id>', 'Project ID (shortcut)')
|
|
48
|
+
.option('--user <user>', 'Specify user')
|
|
49
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
50
|
+
.action(createKbAction('createTask'));
|
|
51
|
+
// searchTasks 支持快捷参数 --project_id / --query
|
|
52
|
+
cmd
|
|
53
|
+
.command('searchTasks')
|
|
54
|
+
.description(METHOD_DESCRIPTIONS.searchTasks)
|
|
55
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
56
|
+
.option('--project_id <id>', 'Project ID (shortcut)')
|
|
57
|
+
.option('--query <query>', 'Search query (shortcut)')
|
|
58
|
+
.option('--user <user>', 'Specify user')
|
|
59
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
60
|
+
.action(createKbAction('searchTasks'));
|
|
61
|
+
// getAllActiveIterations 支持快捷参数 --project_id
|
|
62
|
+
cmd
|
|
63
|
+
.command('getAllActiveIterations')
|
|
64
|
+
.description(METHOD_DESCRIPTIONS.getAllActiveIterations)
|
|
65
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
66
|
+
.option('--project_id <id>', 'Project ID (shortcut)')
|
|
67
|
+
.option('--user <user>', 'Specify user')
|
|
68
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
69
|
+
.action(createKbAction('getAllActiveIterations'));
|
|
70
|
+
// getAllComments 支持快捷参数 --task_id
|
|
71
|
+
cmd
|
|
72
|
+
.command('getAllComments')
|
|
73
|
+
.description(METHOD_DESCRIPTIONS.getAllComments)
|
|
74
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
75
|
+
.option('--task_id <id>', 'Task ID (shortcut)')
|
|
76
|
+
.option('--user <user>', 'Specify user')
|
|
77
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
78
|
+
.action(createKbAction('getAllComments'));
|
|
79
|
+
for (const method of METHODS.filter((item) => item !== 'getMe' &&
|
|
80
|
+
item !== 'getMyProjects' &&
|
|
81
|
+
item !== 'getTask' &&
|
|
82
|
+
item !== 'createTask' &&
|
|
83
|
+
item !== 'searchTasks' &&
|
|
84
|
+
item !== 'getAllActiveIterations' &&
|
|
85
|
+
item !== 'getAllComments' &&
|
|
86
|
+
item !== 'createTaskFile' &&
|
|
87
|
+
item !== 'createProjectFile')) {
|
|
88
|
+
cmd
|
|
89
|
+
.command(method)
|
|
90
|
+
.description(METHOD_DESCRIPTIONS[method])
|
|
91
|
+
.requiredOption('--params <json>', 'JSON/JSON5 params only; CLI fills jsonrpc/method/id')
|
|
92
|
+
.option('--user <user>', 'Specify user')
|
|
93
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
94
|
+
.action(createKbAction(method));
|
|
95
|
+
}
|
|
96
|
+
// createProjectFile 支持快捷参数 --file / --project_id,自动转 base64
|
|
97
|
+
cmd
|
|
98
|
+
.command('createProjectFile')
|
|
99
|
+
.description(METHOD_DESCRIPTIONS.createProjectFile)
|
|
100
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
101
|
+
.option('--file <path>', 'File path to upload (auto base64)')
|
|
102
|
+
.option('--project_id <id>', 'Project ID (shortcut)')
|
|
103
|
+
.option('--user <user>', 'Specify user')
|
|
104
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
105
|
+
.action(createKbAction('createProjectFile'));
|
|
106
|
+
// createTaskFile 支持快捷参数 --file / --task_id / --project_id,自动转 base64
|
|
107
|
+
cmd
|
|
108
|
+
.command('createTaskFile')
|
|
109
|
+
.description(METHOD_DESCRIPTIONS.createTaskFile)
|
|
110
|
+
.option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
|
|
111
|
+
.option('--file <path>', 'File path to upload (auto base64)')
|
|
112
|
+
.option('--task_id <id>', 'Task ID (shortcut)')
|
|
113
|
+
.option('--project_id <id>', 'Project ID (shortcut)')
|
|
114
|
+
.option('--user <user>', 'Specify user')
|
|
115
|
+
.option('--gateway <url>', 'Override kanboard gateway')
|
|
116
|
+
.action(createKbAction('createTaskFile'));
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
export default kbService;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ParamType = 'integer' | 'string' | 'array<integer>' | 'array<string>' | 'object' | 'file';
|
|
2
|
+
export interface ParamDef {
|
|
3
|
+
name: string;
|
|
4
|
+
type: ParamType;
|
|
5
|
+
required: boolean;
|
|
6
|
+
description: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const PARAM_DEFS: Record<string, ParamDef[]>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/commands/services/kb/params.ts
|
|
2
|
+
//
|
|
3
|
+
// 参数定义:从 Kanboard API 文档 (docs/kanboard-api.html) 提取
|
|
4
|
+
// 用于 CLI help 和 SKILL.md 自动生成
|
|
5
|
+
// 方法的参数列表
|
|
6
|
+
export const PARAM_DEFS = {
|
|
7
|
+
// 无参方法
|
|
8
|
+
getMe: [],
|
|
9
|
+
getMyProjects: [],
|
|
10
|
+
// 查询方法
|
|
11
|
+
getTask: [
|
|
12
|
+
{ name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
13
|
+
],
|
|
14
|
+
queryUsers: [
|
|
15
|
+
{ name: 'user_ids', type: 'array<integer>', required: false, description: 'Kanboard 用户 ID 列表,例如 [18, 23]' },
|
|
16
|
+
{ name: 'oauth2_uid', type: 'array<string>', required: false, description: 'Meet 用户 ID 列表,例如 ["100861", "100862"]' },
|
|
17
|
+
],
|
|
18
|
+
searchTasks: [
|
|
19
|
+
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID。接口按项目检查查看权限。' },
|
|
20
|
+
{ name: 'query', type: 'string', required: true, description: 'Kanboard 搜索表达式,例如 iteration:456 status:1' },
|
|
21
|
+
],
|
|
22
|
+
getAllActiveIterations: [
|
|
23
|
+
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
|
|
24
|
+
],
|
|
25
|
+
getAllComments: [
|
|
26
|
+
{ name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
27
|
+
],
|
|
28
|
+
// 写入方法
|
|
29
|
+
createTask: [
|
|
30
|
+
{ name: 'title', type: 'string', required: true, description: '需求标题' },
|
|
31
|
+
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
|
|
32
|
+
{ name: 'owner_id', type: 'integer', required: false, description: '处理人 Kanboard 用户 ID' },
|
|
33
|
+
{ name: 'is_bug', type: 'integer', required: false, description: '是否为 bug,1 表示 bug,0 表示需求' },
|
|
34
|
+
{ name: 'description', type: 'string', required: false, description: '需求描述(支持 Markdown)' },
|
|
35
|
+
{ name: 'category_id', type: 'integer', required: false, description: '分类 ID' },
|
|
36
|
+
{ name: 'iteration_id', type: 'integer', required: false, description: '迭代 ID' },
|
|
37
|
+
{ name: 'column_id', type: 'integer', required: false, description: '状态列 ID' },
|
|
38
|
+
],
|
|
39
|
+
createSubtask: [
|
|
40
|
+
{ name: 'task_id', type: 'integer', required: true, description: '父需求/bug ID' },
|
|
41
|
+
{ name: 'title', type: 'string', required: true, description: '子任务标题' },
|
|
42
|
+
{ name: 'user_id', type: 'integer', required: false, description: '子任务处理人 Kanboard 用户 ID' },
|
|
43
|
+
{ name: 'time_estimated', type: 'integer', required: false, description: '预估时间(小时)' },
|
|
44
|
+
{ name: 'time_spent', type: 'integer', required: false, description: '已花费时间(小时)' },
|
|
45
|
+
{ name: 'status', type: 'integer', required: false, description: '状态:0 未开始,1 进行中,2 已完成' },
|
|
46
|
+
],
|
|
47
|
+
updateTask: [
|
|
48
|
+
{ name: 'id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
49
|
+
{ name: 'title', type: 'string', required: false, description: '需求标题' },
|
|
50
|
+
{ name: 'owner_id', type: 'integer', required: false, description: '处理人 Kanboard 用户 ID' },
|
|
51
|
+
{ name: 'description', type: 'string', required: false, description: '需求描述(支持 Markdown)' },
|
|
52
|
+
{ name: 'category_id', type: 'integer', required: false, description: '分类 ID' },
|
|
53
|
+
{ name: 'iteration_id', type: 'integer', required: false, description: '迭代 ID' },
|
|
54
|
+
{ name: 'column_id', type: 'integer', required: false, description: '状态列 ID' },
|
|
55
|
+
],
|
|
56
|
+
createComment: [
|
|
57
|
+
{ name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
58
|
+
{ name: 'user_id', type: 'integer', required: true, description: '评论作者 Kanboard 用户 ID' },
|
|
59
|
+
{ name: 'content', type: 'string', required: true, description: '评论内容(支持 Markdown)' },
|
|
60
|
+
{ name: 'reference', type: 'string', required: false, description: '外部引用,如 Meet 消息 ID' },
|
|
61
|
+
],
|
|
62
|
+
createProjectFile: [
|
|
63
|
+
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
|
|
64
|
+
{ name: 'filename', type: 'string', required: true, description: '文件名' },
|
|
65
|
+
{ name: 'blob', type: 'string', required: true, description: '文件内容 Base64 编码' },
|
|
66
|
+
],
|
|
67
|
+
createTaskFile: [
|
|
68
|
+
{ name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
|
|
69
|
+
{ name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
|
|
70
|
+
{ name: 'filename', type: 'string', required: true, description: '文件名' },
|
|
71
|
+
{ name: 'blob', type: 'string', required: true, description: '文件内容 Base64 编码' },
|
|
72
|
+
],
|
|
73
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function jsonRpcCall<T>(gateway: string, method: string, params: Record<string, unknown> | unknown[], pat: string): Promise<T>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { outputCliError } from '../../../core/error.js';
|
|
2
|
+
import { request } from '../../../core/http.js';
|
|
3
|
+
import { getNextRequestId } from './utils.js';
|
|
4
|
+
export async function jsonRpcCall(gateway, method, params, pat) {
|
|
5
|
+
const requestBody = {
|
|
6
|
+
jsonrpc: '2.0',
|
|
7
|
+
method,
|
|
8
|
+
id: getNextRequestId(),
|
|
9
|
+
params,
|
|
10
|
+
};
|
|
11
|
+
const response = await request({
|
|
12
|
+
gateway,
|
|
13
|
+
path: '/jsonrpc.php',
|
|
14
|
+
method: 'POST',
|
|
15
|
+
body: requestBody,
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `meet ${pat}`,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
if (response.error) {
|
|
21
|
+
outputCliError('NETWORK_ERROR', response.error.message, {
|
|
22
|
+
code: response.error.code,
|
|
23
|
+
data: response.error.data,
|
|
24
|
+
});
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
if (response.result === undefined) {
|
|
28
|
+
outputCliError('NETWORK_ERROR', 'No result returned from Kanboard API', {
|
|
29
|
+
method,
|
|
30
|
+
params,
|
|
31
|
+
});
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return response.result;
|
|
35
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanboard JSON-RPC API 类型定义
|
|
3
|
+
*/
|
|
4
|
+
export interface JsonRpcRequest {
|
|
5
|
+
jsonrpc: '2.0';
|
|
6
|
+
method: string;
|
|
7
|
+
id: number | string;
|
|
8
|
+
params: Record<string, unknown> | unknown[];
|
|
9
|
+
}
|
|
10
|
+
export interface JsonRpcResponse<T> {
|
|
11
|
+
jsonrpc: '2.0';
|
|
12
|
+
id: number | string;
|
|
13
|
+
result?: T;
|
|
14
|
+
error?: {
|
|
15
|
+
code: number;
|
|
16
|
+
message: string;
|
|
17
|
+
data?: unknown;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface KanboardTask {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
project_id: string;
|
|
25
|
+
column_id: string;
|
|
26
|
+
swimlane_id?: string;
|
|
27
|
+
owner_id: string;
|
|
28
|
+
creator_id: string;
|
|
29
|
+
category_id?: string;
|
|
30
|
+
iteration_id?: string;
|
|
31
|
+
is_bug: string;
|
|
32
|
+
date_creation: string;
|
|
33
|
+
date_due?: string;
|
|
34
|
+
date_started?: string;
|
|
35
|
+
priority: string;
|
|
36
|
+
score: string;
|
|
37
|
+
reference: string;
|
|
38
|
+
url: string;
|
|
39
|
+
color?: {
|
|
40
|
+
name: string;
|
|
41
|
+
background: string;
|
|
42
|
+
border: string;
|
|
43
|
+
};
|
|
44
|
+
tags?: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface GetTaskParams {
|
|
47
|
+
task_id: number;
|
|
48
|
+
}
|
|
49
|
+
export interface GetUsersByIdsParams {
|
|
50
|
+
user_ids: number[];
|
|
51
|
+
}
|
|
52
|
+
export interface KanboardUser {
|
|
53
|
+
id: string;
|
|
54
|
+
username: string;
|
|
55
|
+
name: string;
|
|
56
|
+
email: string;
|
|
57
|
+
oauth2_uid?: string;
|
|
58
|
+
role: string;
|
|
59
|
+
is_active: string;
|
|
60
|
+
avatar_path?: string | null;
|
|
61
|
+
theme?: string;
|
|
62
|
+
language?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface UpdateTaskParams {
|
|
65
|
+
id: number;
|
|
66
|
+
title?: string;
|
|
67
|
+
description?: string;
|
|
68
|
+
owner_id?: number;
|
|
69
|
+
category_id?: number;
|
|
70
|
+
date_due?: string;
|
|
71
|
+
date_started?: string;
|
|
72
|
+
priority?: number;
|
|
73
|
+
score?: number;
|
|
74
|
+
color_id?: string;
|
|
75
|
+
reference?: string;
|
|
76
|
+
tags?: string[];
|
|
77
|
+
time_spent?: number;
|
|
78
|
+
time_estimated?: number;
|
|
79
|
+
}
|
|
80
|
+
export interface CreateCommentParams {
|
|
81
|
+
task_id: number;
|
|
82
|
+
user_id: number;
|
|
83
|
+
content: string;
|
|
84
|
+
reference?: string;
|
|
85
|
+
}
|
|
86
|
+
export interface CreateCommentResult {
|
|
87
|
+
comment_id: number;
|
|
88
|
+
}
|
|
89
|
+
export interface CreateTaskFileParams {
|
|
90
|
+
project_id: number;
|
|
91
|
+
task_id: number;
|
|
92
|
+
filename: string;
|
|
93
|
+
blob: string;
|
|
94
|
+
}
|
|
95
|
+
export interface CreateTaskFileResult {
|
|
96
|
+
file_id: number;
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getNextRequestId(): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SyncMsgParams, SyncMsgResult, GetConvInfo2Params, GetConvInfo2Result, GetMyInfoResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 同步消息
|
|
4
|
+
*/
|
|
5
|
+
export declare function syncMsg(gateway: string, params: SyncMsgParams, token: string): Promise<SyncMsgResult>;
|
|
6
|
+
/**
|
|
7
|
+
* 获取会话信息(含成员列表)
|
|
8
|
+
*/
|
|
9
|
+
export declare function getConvInfo2(gateway: string, params: GetConvInfo2Params, token: string): Promise<GetConvInfo2Result>;
|
|
10
|
+
/**
|
|
11
|
+
* 获取当前用户信息
|
|
12
|
+
*/
|
|
13
|
+
export declare function getMyInfo(gateway: string, token: string): Promise<GetMyInfoResult>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/commands/services/meet/api.ts
|
|
2
|
+
import { request } from '../../../core/http.js';
|
|
3
|
+
/**
|
|
4
|
+
* 同步消息
|
|
5
|
+
*/
|
|
6
|
+
export async function syncMsg(gateway, params, token) {
|
|
7
|
+
return request({
|
|
8
|
+
gateway,
|
|
9
|
+
path: '/im/SyncMsg',
|
|
10
|
+
method: 'POST',
|
|
11
|
+
body: params,
|
|
12
|
+
headers: { Token: token },
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 获取会话信息(含成员列表)
|
|
17
|
+
*/
|
|
18
|
+
export async function getConvInfo2(gateway, params, token) {
|
|
19
|
+
return request({
|
|
20
|
+
gateway,
|
|
21
|
+
path: '/general/GetConvInfo2',
|
|
22
|
+
method: 'POST',
|
|
23
|
+
body: params,
|
|
24
|
+
headers: { Token: token },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 获取当前用户信息
|
|
29
|
+
*/
|
|
30
|
+
export async function getMyInfo(gateway, token) {
|
|
31
|
+
return request({
|
|
32
|
+
gateway,
|
|
33
|
+
path: '/general/GetMyInfo',
|
|
34
|
+
method: 'POST',
|
|
35
|
+
body: {},
|
|
36
|
+
headers: { Token: token },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/commands/services/meet/config.ts
|
|
2
|
+
export const meetConfig = {
|
|
3
|
+
name: 'meet',
|
|
4
|
+
gateways: {
|
|
5
|
+
meet: {
|
|
6
|
+
test: 'https://staging-meet-api.miyachat.com',
|
|
7
|
+
pre: 'https://pre-meet-api.miyachat.com',
|
|
8
|
+
staging: 'https://staging-meet-api.miyachat.com',
|
|
9
|
+
beta: 'https://staging-meet-api.miyachat.com',
|
|
10
|
+
prod: 'https://meet-api.miyachat.com',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
export function getMeetGateway(tenant, env) {
|
|
15
|
+
return meetConfig.gateways[tenant]?.[env];
|
|
16
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// src/commands/services/meet/descriptions.ts
|
|
2
|
+
export const COMMANDS = ['syncMsg', 'getConvInfo2', 'getMyInfo'];
|
|
3
|
+
export const COMMAND_DESCRIPTIONS = {
|
|
4
|
+
syncMsg: 'Sync messages from conversation',
|
|
5
|
+
getConvInfo2: 'Get conversation info with members',
|
|
6
|
+
getMyInfo: 'Get current user info',
|
|
7
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getCurrentUser } from '../../../core/auth.js';
|
|
2
|
+
/**
|
|
3
|
+
* 获取 meet 认证信息
|
|
4
|
+
*/
|
|
5
|
+
export declare function getMeetAuth(userName?: string): {
|
|
6
|
+
user: NonNullable<ReturnType<typeof getCurrentUser>>;
|
|
7
|
+
token: string;
|
|
8
|
+
};
|
|
9
|
+
export interface SyncMsgOptions {
|
|
10
|
+
user?: string;
|
|
11
|
+
gateway?: string;
|
|
12
|
+
params?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function handleSyncMsg(options: SyncMsgOptions): Promise<void>;
|
|
15
|
+
export interface GetConvInfo2Options {
|
|
16
|
+
user?: string;
|
|
17
|
+
gateway?: string;
|
|
18
|
+
params?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function handleGetConvInfo2(options: GetConvInfo2Options): Promise<void>;
|
|
21
|
+
export interface GetMyInfoOptions {
|
|
22
|
+
user?: string;
|
|
23
|
+
gateway?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function handleGetMyInfo(options: GetMyInfoOptions): Promise<void>;
|