@leju-gym/gym-mcp 0.0.3 → 0.0.5
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/package.json +1 -1
- package/src/services/mcp/ToolService.js +679 -17
package/package.json
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* @ModuleName: ToolService
|
|
3
3
|
* @ModuleMethod: src/services/mcp
|
|
4
4
|
* @ModuleUsage: MCP 工具注册与调用服务
|
|
5
|
-
* @ModuleDesc: 通过 spawn gym CLI
|
|
6
|
-
* 逻辑零重复,认证完全复用 gym config login
|
|
5
|
+
* @ModuleDesc: 通过 spawn gym CLI 调用本地 gym 命令,
|
|
6
|
+
* 逻辑零重复,认证完全复用 gym config login。
|
|
7
|
+
* 所有查询命令标记 readOnlyHint,写操作不做标记以触发用户确认。
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
const appHelper = require('../../AppHelper');
|
|
@@ -17,32 +18,51 @@ function gymBin() {
|
|
|
17
18
|
return process.env.GYM_BIN || 'gym';
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
/**
|
|
22
|
+
* 执行 gym CLI 命令,返回清理 ANSI 后的纯文本输出
|
|
23
|
+
* @param {string[]} args - CLI 参数数组
|
|
24
|
+
* @param {number} [timeout=30000] - 超时毫秒,上传/下载需要更长时间
|
|
25
|
+
*/
|
|
26
|
+
async function runGym(args = [], timeout = 30_000) {
|
|
21
27
|
const bin = gymBin();
|
|
22
28
|
appHelper.info('runGym', bin, args.join(' '));
|
|
23
29
|
try {
|
|
24
30
|
const { stdout, stderr } = await execFileAsync(bin, args, {
|
|
25
|
-
timeout
|
|
31
|
+
timeout,
|
|
26
32
|
maxBuffer: 1024 * 1024,
|
|
27
33
|
});
|
|
28
34
|
if (stderr) appHelper.warn('runGym stderr', stderr);
|
|
29
|
-
return
|
|
35
|
+
return cleanAnsi(stdout || '');
|
|
30
36
|
} catch (e) {
|
|
31
37
|
if (e.code === 'ENOENT') {
|
|
32
|
-
return
|
|
38
|
+
return 'gym CLI 未安装或不在 PATH 中。请执行: npm i -g @leju-gym/gym-cli && gym config login';
|
|
33
39
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return { error: e.stderr || e.message };
|
|
40
|
+
const out = cleanAnsi(e.stdout || '') || e.stderr || e.message;
|
|
41
|
+
return out;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
/**
|
|
46
|
+
* 清理 ANSI 转义序列(颜色码、光标移动等),保留可读文本
|
|
47
|
+
*/
|
|
48
|
+
function cleanAnsi(str) {
|
|
49
|
+
return str
|
|
50
|
+
.replace(/\x1b\[[0-9;]*m/g, '') // 颜色/样式
|
|
51
|
+
.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '') // 光标移动等
|
|
52
|
+
.replace(/\x1b\]8;.*?\x1b\\/g, '') // 超链接
|
|
53
|
+
.trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
// ─── 工具定义 ──────────────────────────────────────────────────
|
|
41
57
|
|
|
42
58
|
const toolDefinitions = [
|
|
59
|
+
// ══════════════════════════════════════════════════════════════
|
|
60
|
+
// 连通性测试
|
|
61
|
+
// ══════════════════════════════════════════════════════════════
|
|
43
62
|
{
|
|
44
63
|
name: 'echo',
|
|
45
64
|
description: '回显输入内容,用于测试连通性',
|
|
65
|
+
annotations: { readOnlyHint: true },
|
|
46
66
|
inputSchema: {
|
|
47
67
|
type: 'object',
|
|
48
68
|
properties: {
|
|
@@ -51,10 +71,390 @@ const toolDefinitions = [
|
|
|
51
71
|
required: ['message'],
|
|
52
72
|
},
|
|
53
73
|
},
|
|
74
|
+
|
|
75
|
+
// ══════════════════════════════════════════════════════════════
|
|
76
|
+
// 项目管理
|
|
77
|
+
// ══════════════════════════════════════════════════════════════
|
|
54
78
|
{
|
|
55
79
|
name: 'gym_project_list',
|
|
56
80
|
description:
|
|
57
81
|
'查询组织及其下属项目列表(调用本地 gym CLI,复用已登录的认证状态)。返回每个组织下的所有项目 ID、名称、编码。',
|
|
82
|
+
annotations: { readOnlyHint: true },
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
page: { type: 'number', description: '页码,默认 1' },
|
|
87
|
+
limit: { type: 'number', description: '每页数量,默认 10' },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// ══════════════════════════════════════════════════════════════
|
|
93
|
+
// 任务管理
|
|
94
|
+
// ══════════════════════════════════════════════════════════════
|
|
95
|
+
{
|
|
96
|
+
name: 'gym_task_list',
|
|
97
|
+
description:
|
|
98
|
+
'查询任务列表。支持按状态、任务名称、任务编码、项目、场景、标签等筛选,支持分页。',
|
|
99
|
+
annotations: { readOnlyHint: true },
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
properties: {
|
|
103
|
+
page: { type: 'number', description: '页码,默认 1' },
|
|
104
|
+
limit: { type: 'number', description: '每页数量,默认 10' },
|
|
105
|
+
status: { type: 'string', description: '按状态筛选: 0=待处理 1=进行中 2=已完成;支持逗号分隔多个' },
|
|
106
|
+
taskId: { type: 'string', description: '按任务ID(精确匹配)筛选' },
|
|
107
|
+
taskName: { type: 'string', description: '按任务名称(包含匹配)筛选' },
|
|
108
|
+
taskCode: { type: 'string', description: '按任务编码(精确匹配)筛选' },
|
|
109
|
+
projectId: { type: 'string', description: '【必填】项目ID' },
|
|
110
|
+
scene1: { type: 'string', description: '按一级场景ID过滤' },
|
|
111
|
+
scene2: { type: 'string', description: '按二级场景ID过滤' },
|
|
112
|
+
scene3: { type: 'string', description: '按三级场景ID过滤' },
|
|
113
|
+
startDate: { type: 'string', description: '创建开始日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
114
|
+
endDate: { type: 'string', description: '创建结束日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
115
|
+
tag: { type: 'string', description: '按任务标签名过滤(支持多个,逗号分隔)' },
|
|
116
|
+
},
|
|
117
|
+
required: ['projectId'],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'gym_task_detail',
|
|
122
|
+
description: '查看单个任务详情,包括采集/标注/审核进度等信息',
|
|
123
|
+
annotations: { readOnlyHint: true },
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties: {
|
|
127
|
+
taskId: { type: 'string', description: '任务ID' },
|
|
128
|
+
},
|
|
129
|
+
required: ['taskId'],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'gym_task_list_bag',
|
|
134
|
+
description: '按任务ID列出对应的 BAG 数据信息,支持分页和状态筛选',
|
|
135
|
+
annotations: { readOnlyHint: true },
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
taskId: { type: 'string', description: '【必填】任务ID' },
|
|
140
|
+
page: { type: 'number', description: '页码,默认 1' },
|
|
141
|
+
limit: { type: 'number', description: '每页数量,默认 10' },
|
|
142
|
+
status: { type: 'number', description: '按状态筛选: 0=待标注 1=待审核 2=审核失败 3=审核成功 4=待同步 5=同步失败' },
|
|
143
|
+
},
|
|
144
|
+
required: ['taskId'],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'gym_task_tag_list',
|
|
149
|
+
description: '列出任务标签(标签与项目绑定)',
|
|
150
|
+
annotations: { readOnlyHint: true },
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
projectId: { type: 'string', description: '【必填】项目ID' },
|
|
155
|
+
},
|
|
156
|
+
required: ['projectId'],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'gym_task_tag_bind',
|
|
161
|
+
description:
|
|
162
|
+
'给一批任务打标签(支持按任务ID列表或按过滤条件批量打标)。此操作会修改任务标签,需要用户确认。',
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
projectId: { type: 'string', description: '【必填】项目ID' },
|
|
167
|
+
labels: { type: 'string', description: '【必填】标签名称列表(逗号分隔)' },
|
|
168
|
+
taskIds: { type: 'string', description: '任务ID列表(逗号分隔),指定后按 SELECTED 模式打标' },
|
|
169
|
+
status: { type: 'string', description: '按状态筛选: 0=待处理 1=进行中 2=已完成' },
|
|
170
|
+
taskId: { type: 'string', description: '按任务ID(精确匹配)筛选' },
|
|
171
|
+
taskName: { type: 'string', description: '按任务名称(包含匹配)筛选' },
|
|
172
|
+
taskCode: { type: 'string', description: '按任务编码(精确匹配)筛选' },
|
|
173
|
+
startDate: { type: 'string', description: '创建开始日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
174
|
+
endDate: { type: 'string', description: '创建结束日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
175
|
+
scene1: { type: 'string', description: '按一级场景过滤' },
|
|
176
|
+
scene2: { type: 'string', description: '按二级场景过滤' },
|
|
177
|
+
scene3: { type: 'string', description: '按三级场景过滤' },
|
|
178
|
+
tag: { type: 'string', description: '按任务标签名过滤(用于缩小打标范围)' },
|
|
179
|
+
},
|
|
180
|
+
required: ['projectId', 'labels'],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// ══════════════════════════════════════════════════════════════
|
|
185
|
+
// 数据管理
|
|
186
|
+
// ══════════════════════════════════════════════════════════════
|
|
187
|
+
{
|
|
188
|
+
name: 'gym_data_list',
|
|
189
|
+
description:
|
|
190
|
+
'查询数据管理列表。支持按状态、任务、项目、场景、标签、设备、采集员等筛选,支持分页。',
|
|
191
|
+
annotations: { readOnlyHint: true },
|
|
192
|
+
inputSchema: {
|
|
193
|
+
type: 'object',
|
|
194
|
+
properties: {
|
|
195
|
+
page: { type: 'number', description: '页码,默认 1' },
|
|
196
|
+
limit: { type: 'number', description: '每页数量,默认 10' },
|
|
197
|
+
status: { type: 'string', description: '状态过滤: 0=待标注 1=待审核 2=审核失败 3=审核成功 4=待同步 5=同步失败 6=已标注 7=待验收 8=验收失败 9=验收成功 10=标注中' },
|
|
198
|
+
taskId: { type: 'string', description: '按任务ID(精确匹配)筛选' },
|
|
199
|
+
dataName: { type: 'string', description: '按数据文件名(包含匹配)筛选' },
|
|
200
|
+
taskName: { type: 'string', description: '按任务名(包含匹配)筛选' },
|
|
201
|
+
taskCode: { type: 'string', description: '按任务编码(精确匹配)筛选' },
|
|
202
|
+
projectId: { type: 'string', description: '按项目过滤' },
|
|
203
|
+
scene1: { type: 'string', description: '按一级场景过滤' },
|
|
204
|
+
scene2: { type: 'string', description: '按二级场景过滤' },
|
|
205
|
+
scene3: { type: 'string', description: '按三级场景过滤' },
|
|
206
|
+
startDate: { type: 'string', description: '上传开始日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
207
|
+
endDate: { type: 'string', description: '上传结束日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
208
|
+
tag: { type: 'string', description: '按数据标签名过滤' },
|
|
209
|
+
deviceSn: { type: 'string', description: '按设备序列号(包含匹配)筛选' },
|
|
210
|
+
collector: { type: 'string', description: '按采集员(包含匹配)筛选' },
|
|
211
|
+
dataType: { type: 'string', description: '按数据类型筛选: success/failure' },
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'gym_data_detail',
|
|
217
|
+
description: '查看单条数据详情,包括设备、采集员、标注员、审核员等信息',
|
|
218
|
+
annotations: { readOnlyHint: true },
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: 'object',
|
|
221
|
+
properties: {
|
|
222
|
+
dataId: { type: 'string', description: '数据ID' },
|
|
223
|
+
},
|
|
224
|
+
required: ['dataId'],
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'gym_data_tag_list',
|
|
229
|
+
description: '列出数据标签(标签与项目绑定)',
|
|
230
|
+
annotations: { readOnlyHint: true },
|
|
231
|
+
inputSchema: {
|
|
232
|
+
type: 'object',
|
|
233
|
+
properties: {
|
|
234
|
+
projectId: { type: 'string', description: '【必填】项目ID' },
|
|
235
|
+
},
|
|
236
|
+
required: ['projectId'],
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: 'gym_data_tag_create',
|
|
241
|
+
description:
|
|
242
|
+
'创建数据标签。此操作会修改数据标签,需要用户确认。',
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: 'object',
|
|
245
|
+
properties: {
|
|
246
|
+
projectId: { type: 'string', description: '【必填】项目ID' },
|
|
247
|
+
name: { type: 'string', description: '【必填】标签名称' },
|
|
248
|
+
},
|
|
249
|
+
required: ['projectId', 'name'],
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: 'gym_data_tag_bind',
|
|
254
|
+
description:
|
|
255
|
+
'给一批数据打标签(支持按数据ID列表或按过滤条件批量打标)。此操作会修改数据标签,需要用户确认。',
|
|
256
|
+
inputSchema: {
|
|
257
|
+
type: 'object',
|
|
258
|
+
properties: {
|
|
259
|
+
projectId: { type: 'string', description: '【必填】项目ID' },
|
|
260
|
+
labels: { type: 'string', description: '【必填】标签名称列表(逗号分隔)' },
|
|
261
|
+
dataIds: { type: 'string', description: '数据ID列表(逗号分隔),指定后按 SELECTED 模式打标' },
|
|
262
|
+
status: { type: 'string', description: '状态过滤: 0=待标注 1=待审核 2=审核失败 3=审核成功 4=待同步 5=同步失败 6=已标注 7=待验收 8=验收失败 9=验收成功 10=标注中' },
|
|
263
|
+
taskId: { type: 'string', description: '按任务ID(精确匹配)筛选' },
|
|
264
|
+
dataName: { type: 'string', description: '按数据文件名(包含匹配)筛选' },
|
|
265
|
+
taskName: { type: 'string', description: '按任务名(包含匹配)筛选' },
|
|
266
|
+
taskCode: { type: 'string', description: '按任务编码(精确匹配)筛选' },
|
|
267
|
+
startDate: { type: 'string', description: '上传开始日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
268
|
+
endDate: { type: 'string', description: '上传结束日期 (YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD)' },
|
|
269
|
+
scene1: { type: 'string', description: '按一级场景过滤' },
|
|
270
|
+
scene2: { type: 'string', description: '按二级场景过滤' },
|
|
271
|
+
scene3: { type: 'string', description: '按三级场景过滤' },
|
|
272
|
+
tag: { type: 'string', description: '按数据标签名过滤(用于缩小打标范围)' },
|
|
273
|
+
deviceSn: { type: 'string', description: '按设备序列号(包含匹配)筛选' },
|
|
274
|
+
collector: { type: 'string', description: '按采集员(包含匹配)筛选' },
|
|
275
|
+
dataType: { type: 'string', description: '按数据类型筛选: success/failure' },
|
|
276
|
+
},
|
|
277
|
+
required: ['projectId', 'labels'],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
name: 'gym_data_download',
|
|
282
|
+
description:
|
|
283
|
+
'下载 bag 数据(支持单文件、批量、按任务/项目/标签过滤)。会写入本地磁盘,需要用户确认。',
|
|
284
|
+
inputSchema: {
|
|
285
|
+
type: 'object',
|
|
286
|
+
properties: {
|
|
287
|
+
dataId: { type: 'string', description: '单文件模式: 数据ID。不传则需使用 --batch 参数' },
|
|
288
|
+
batch: { type: 'boolean', description: '启用批量下载模式' },
|
|
289
|
+
output: { type: 'string', description: '输出目录,默认当前目录' },
|
|
290
|
+
taskId: { type: 'string', description: '批量模式: 按任务ID过滤(需配合 --batch)' },
|
|
291
|
+
projectId: { type: 'string', description: '批量模式: 按项目过滤' },
|
|
292
|
+
tag: { type: 'string', description: '批量模式: 按数据标签名过滤,支持多个逗号分隔' },
|
|
293
|
+
status: { type: 'string', description: '批量模式: 状态过滤。0=待标注 1=待审核 2=审核失败 3=审核成功 4=待同步 5=同步失败 6=已标注 7=待验收 8=验收失败 9=验收成功 10=标注中' },
|
|
294
|
+
dataName: { type: 'string', description: '批量模式: 文件名包含' },
|
|
295
|
+
startDate: { type: 'string', description: '批量模式: 开始日期' },
|
|
296
|
+
endDate: { type: 'string', description: '批量模式: 结束日期' },
|
|
297
|
+
scene1: { type: 'string', description: '批量模式: 按一级场景过滤' },
|
|
298
|
+
scene2: { type: 'string', description: '批量模式: 按二级场景过滤' },
|
|
299
|
+
scene3: { type: 'string', description: '批量模式: 按三级场景过滤' },
|
|
300
|
+
concurrency: { type: 'number', description: '批量模式: 并发数(1-10),默认 3' },
|
|
301
|
+
count: { type: 'number', description: '批量模式: 下载条数,默认 1000' },
|
|
302
|
+
page: { type: 'number', description: '批量模式: 起始页,默认 1' },
|
|
303
|
+
limit: { type: 'number', description: '批量模式: 每页拉取记录数,默认 50' },
|
|
304
|
+
force: { type: 'boolean', description: '强制覆盖下载(关闭断点续传)' },
|
|
305
|
+
yes: { type: 'boolean', description: '跳过确认直接开始下载' },
|
|
306
|
+
easy: { type: 'boolean', description: '简单模式: 仅下载 .bag 文件,不创建额外文件夹' },
|
|
307
|
+
metadata: { type: 'boolean', description: '同时下载 metadata.json' },
|
|
308
|
+
noOffline: { type: 'boolean', description: '禁用 NAS 离线下载' },
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'gym_data_upload',
|
|
314
|
+
description:
|
|
315
|
+
'上传 bag 包文件到云端存储。此操作会上传文件并注册数据记录,需要用户确认。',
|
|
316
|
+
inputSchema: {
|
|
317
|
+
type: 'object',
|
|
318
|
+
properties: {
|
|
319
|
+
taskId: { type: 'string', description: '【必填】任务 ID' },
|
|
320
|
+
bagPath: { type: 'string', description: '【必填】本地 bag 文件路径(支持单个文件或目录)' },
|
|
321
|
+
deviceSn: { type: 'string', description: '【必填】设备 SN 码(如 LB_11、11_200)' },
|
|
322
|
+
deviceType: { type: 'string', description: '【必填】设备类型: "Kuavo 4Pro" | "Kuavo LB" | "Kuavo 5" | "Kuavo 5W"' },
|
|
323
|
+
location: { type: 'string', description: '上传目标位置 (cloud/nas),默认 cloud' },
|
|
324
|
+
dataType: { type: 'string', description: '数据类型 (success/failure),默认 success' },
|
|
325
|
+
topicsVersion: { type: 'string', description: '机器人话题版本号,默认 unknown' },
|
|
326
|
+
lowerVersion: { type: 'string', description: '下位机版本,默认 unknown' },
|
|
327
|
+
lowerCommit: { type: 'string', description: '下位机提交记录' },
|
|
328
|
+
duration: { type: 'string', description: 'bag 包时长(秒),默认 0' },
|
|
329
|
+
remark: { type: 'string', description: '备注信息' },
|
|
330
|
+
},
|
|
331
|
+
required: ['taskId', 'bagPath', 'deviceSn', 'deviceType'],
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
// ══════════════════════════════════════════════════════════════
|
|
336
|
+
// 设备管理
|
|
337
|
+
// ══════════════════════════════════════════════════════════════
|
|
338
|
+
{
|
|
339
|
+
name: 'gym_device_list',
|
|
340
|
+
description: '分页查询设备列表',
|
|
341
|
+
annotations: { readOnlyHint: true },
|
|
342
|
+
inputSchema: {
|
|
343
|
+
type: 'object',
|
|
344
|
+
properties: {
|
|
345
|
+
page: { type: 'number', description: '页码,默认 1' },
|
|
346
|
+
limit: { type: 'number', description: '每页数量,默认 10' },
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
// ══════════════════════════════════════════════════════════════
|
|
352
|
+
// 场景管理
|
|
353
|
+
// ══════════════════════════════════════════════════════════════
|
|
354
|
+
{
|
|
355
|
+
name: 'gym_scene_list',
|
|
356
|
+
description: '查询场景列表。可选一级/二级场景ID来查询子场景。',
|
|
357
|
+
annotations: { readOnlyHint: true },
|
|
358
|
+
inputSchema: {
|
|
359
|
+
type: 'object',
|
|
360
|
+
properties: {
|
|
361
|
+
projectId: { type: 'string', description: '【必填】项目ID' },
|
|
362
|
+
scene1: { type: 'string', description: '一级场景ID(可选),传入后列出该一级场景下的二级场景' },
|
|
363
|
+
scene2: { type: 'string', description: '二级场景ID(可选),需配合 scene1 使用,列出该二级场景下的三级场景' },
|
|
364
|
+
},
|
|
365
|
+
required: ['projectId'],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
// ══════════════════════════════════════════════════════════════
|
|
370
|
+
// 交付任务管理
|
|
371
|
+
// ══════════════════════════════════════════════════════════════
|
|
372
|
+
{
|
|
373
|
+
name: 'gym_delivery_list',
|
|
374
|
+
description: '查询交付任务列表(支持分页)',
|
|
375
|
+
annotations: { readOnlyHint: true },
|
|
376
|
+
inputSchema: {
|
|
377
|
+
type: 'object',
|
|
378
|
+
properties: {
|
|
379
|
+
page: { type: 'number', description: '页码,默认 1' },
|
|
380
|
+
limit: { type: 'number', description: '每页数量,默认 10' },
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: 'gym_delivery_download',
|
|
386
|
+
description:
|
|
387
|
+
'下载交付任务数据(支持 Token 免登录模式或任务ID+业务名模式)。会写入本地磁盘,需要用户确认。',
|
|
388
|
+
inputSchema: {
|
|
389
|
+
type: 'object',
|
|
390
|
+
properties: {
|
|
391
|
+
taskId: { type: 'string', description: '交付任务ID(非 Token 模式必填)' },
|
|
392
|
+
businessName: { type: 'string', description: '业务名称(非 Token 模式必填)' },
|
|
393
|
+
downloadType: { type: 'string', description: '下载方式: cloud / nas / mix(非 Token 模式必填)' },
|
|
394
|
+
token: { type: 'string', description: '交付 Token(外部免登录模式,有此参数时无需 taskId/businessName/downloadType)' },
|
|
395
|
+
saveDir: { type: 'string', description: '保存目录,默认 ./download' },
|
|
396
|
+
nasSn: { type: 'string', description: 'NAS 序列号(download-type=nas 时必填)' },
|
|
397
|
+
concurrency: { type: 'number', description: '并发下载通道数(默认 3, 最大 10)' },
|
|
398
|
+
env: { type: 'string', description: 'Token 模式: 指定环境 dev/test/prod,默认 prod' },
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
// ══════════════════════════════════════════════════════════════
|
|
404
|
+
// 文件管理
|
|
405
|
+
// ══════════════════════════════════════════════════════════════
|
|
406
|
+
{
|
|
407
|
+
name: 'gym_file_list',
|
|
408
|
+
description: '列出记录下的文件',
|
|
409
|
+
annotations: { readOnlyHint: true },
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: 'object',
|
|
412
|
+
properties: {
|
|
413
|
+
recordId: { type: 'string', description: '【必填】记录ID' },
|
|
414
|
+
prefix: { type: 'string', description: '路径前缀' },
|
|
415
|
+
limit: { type: 'number', description: '最大返回数量,默认 100' },
|
|
416
|
+
},
|
|
417
|
+
required: ['recordId'],
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: 'gym_file_upload',
|
|
422
|
+
description:
|
|
423
|
+
'上传文件或文件夹到记录。此操作会写入 OSS 并注册文件记录,需要用户确认。',
|
|
424
|
+
inputSchema: {
|
|
425
|
+
type: 'object',
|
|
426
|
+
properties: {
|
|
427
|
+
recordId: { type: 'string', description: '【必填】记录ID' },
|
|
428
|
+
source: { type: 'string', description: '【必填】一个或多个文件/文件夹路径(空格分隔)' },
|
|
429
|
+
path: { type: 'string', description: '目标路径(相对于记录根目录)' },
|
|
430
|
+
stallTimeout: { type: 'number', description: '停滞超时(秒),默认 300' },
|
|
431
|
+
},
|
|
432
|
+
required: ['recordId', 'source'],
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'gym_file_download',
|
|
437
|
+
description:
|
|
438
|
+
'下载记录下的文件(支持单文件和递归下载目录)。会写入本地磁盘,需要用户确认。',
|
|
439
|
+
inputSchema: {
|
|
440
|
+
type: 'object',
|
|
441
|
+
properties: {
|
|
442
|
+
recordId: { type: 'string', description: '【必填】记录ID' },
|
|
443
|
+
filePath: { type: 'string', description: '【必填】文件路径' },
|
|
444
|
+
output: { type: 'string', description: '本地保存路径,默认当前目录' },
|
|
445
|
+
recursive: { type: 'boolean', description: '递归下载目录下所有文件' },
|
|
446
|
+
},
|
|
447
|
+
required: ['recordId', 'filePath'],
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
// ══════════════════════════════════════════════════════════════
|
|
452
|
+
// 文件记录管理
|
|
453
|
+
// ══════════════════════════════════════════════════════════════
|
|
454
|
+
{
|
|
455
|
+
name: 'gym_record_list',
|
|
456
|
+
description: '分页查询文件记录列表',
|
|
457
|
+
annotations: { readOnlyHint: true },
|
|
58
458
|
inputSchema: {
|
|
59
459
|
type: 'object',
|
|
60
460
|
properties: {
|
|
@@ -63,6 +463,22 @@ const toolDefinitions = [
|
|
|
63
463
|
},
|
|
64
464
|
},
|
|
65
465
|
},
|
|
466
|
+
{
|
|
467
|
+
name: 'gym_record_create',
|
|
468
|
+
description:
|
|
469
|
+
'创建新的文件记录。此操作会在服务端创建记录,需要用户确认。',
|
|
470
|
+
inputSchema: {
|
|
471
|
+
type: 'object',
|
|
472
|
+
properties: {
|
|
473
|
+
title: { type: 'string', description: '【必填】记录标题' },
|
|
474
|
+
projectId: { type: 'string', description: '【必填】所属项目 ID' },
|
|
475
|
+
deviceId: { type: 'string', description: '关联设备 ID(可选)' },
|
|
476
|
+
description: { type: 'string', description: '记录描述(可选)' },
|
|
477
|
+
visibility: { type: 'string', description: '可见性: public / private,默认 private' },
|
|
478
|
+
},
|
|
479
|
+
required: ['title', 'projectId'],
|
|
480
|
+
},
|
|
481
|
+
},
|
|
66
482
|
];
|
|
67
483
|
|
|
68
484
|
// ─── ToolService ────────────────────────────────────────────────
|
|
@@ -73,6 +489,7 @@ class ToolService {
|
|
|
73
489
|
name: t.name,
|
|
74
490
|
description: t.description,
|
|
75
491
|
inputSchema: t.inputSchema,
|
|
492
|
+
annotations: t.annotations,
|
|
76
493
|
}));
|
|
77
494
|
}
|
|
78
495
|
|
|
@@ -86,21 +503,266 @@ class ToolService {
|
|
|
86
503
|
appHelper.info('callTool', toolName, JSON.stringify(args));
|
|
87
504
|
|
|
88
505
|
switch (toolName) {
|
|
506
|
+
// ── 连通性测试 ──
|
|
89
507
|
case 'echo':
|
|
90
508
|
return { ok: true, data: { echo: args.message || '' } };
|
|
91
509
|
|
|
510
|
+
// ── 项目管理 ──
|
|
92
511
|
case 'gym_project_list': {
|
|
93
|
-
const page = args.page || 1;
|
|
94
|
-
const limit = args.limit || 10;
|
|
95
512
|
const result = await runGym([
|
|
96
513
|
'project', 'list',
|
|
97
|
-
'--page', String(page),
|
|
98
|
-
'--limit', String(limit),
|
|
99
|
-
'--json',
|
|
514
|
+
'--page', String(args.page || 1),
|
|
515
|
+
'--limit', String(args.limit || 10),
|
|
100
516
|
]);
|
|
101
|
-
return result
|
|
102
|
-
|
|
103
|
-
|
|
517
|
+
return { ok: true, data: result };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// ── 任务管理 ──
|
|
521
|
+
case 'gym_task_list': {
|
|
522
|
+
const cliArgs = ['task', 'list', '--project-id', String(args.projectId)];
|
|
523
|
+
if (args.page) cliArgs.push('--page', String(args.page));
|
|
524
|
+
if (args.limit) cliArgs.push('--limit', String(args.limit));
|
|
525
|
+
if (args.status) cliArgs.push('--status', String(args.status));
|
|
526
|
+
if (args.taskId) cliArgs.push('--task-id', String(args.taskId));
|
|
527
|
+
if (args.taskName) cliArgs.push('--task-name', String(args.taskName));
|
|
528
|
+
if (args.taskCode) cliArgs.push('--task-code', String(args.taskCode));
|
|
529
|
+
if (args.startDate) cliArgs.push('--start-date', String(args.startDate));
|
|
530
|
+
if (args.endDate) cliArgs.push('--end-date', String(args.endDate));
|
|
531
|
+
if (args.tag) cliArgs.push('--tag', String(args.tag));
|
|
532
|
+
if (args.scene1) cliArgs.push('--scene1', String(args.scene1));
|
|
533
|
+
if (args.scene2) cliArgs.push('--scene2', String(args.scene2));
|
|
534
|
+
if (args.scene3) cliArgs.push('--scene3', String(args.scene3));
|
|
535
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
case 'gym_task_detail':
|
|
539
|
+
return { ok: true, data: await runGym(['task', 'detail', String(args.taskId)]) };
|
|
540
|
+
|
|
541
|
+
case 'gym_task_list_bag': {
|
|
542
|
+
const cliArgs = ['task', 'list-bag', String(args.taskId)];
|
|
543
|
+
if (args.page) cliArgs.push('--page', String(args.page));
|
|
544
|
+
if (args.limit) cliArgs.push('--limit', String(args.limit));
|
|
545
|
+
if (args.status !== undefined) cliArgs.push('--status', String(args.status));
|
|
546
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
case 'gym_task_tag_list':
|
|
550
|
+
return { ok: true, data: await runGym(['task', 'tag', 'list', '--project-id', String(args.projectId)]) };
|
|
551
|
+
|
|
552
|
+
case 'gym_task_tag_bind': {
|
|
553
|
+
const cliArgs = ['task', 'tag', 'bind',
|
|
554
|
+
'--project-id', String(args.projectId),
|
|
555
|
+
'--labels', String(args.labels),
|
|
556
|
+
];
|
|
557
|
+
if (args.taskIds) cliArgs.push('--task-ids', String(args.taskIds));
|
|
558
|
+
if (args.status) cliArgs.push('--status', String(args.status));
|
|
559
|
+
if (args.taskId) cliArgs.push('--task-id', String(args.taskId));
|
|
560
|
+
if (args.taskName) cliArgs.push('--task-name', String(args.taskName));
|
|
561
|
+
if (args.taskCode) cliArgs.push('--task-code', String(args.taskCode));
|
|
562
|
+
if (args.startDate) cliArgs.push('--start-date', String(args.startDate));
|
|
563
|
+
if (args.endDate) cliArgs.push('--end-date', String(args.endDate));
|
|
564
|
+
if (args.scene1) cliArgs.push('--scene1', String(args.scene1));
|
|
565
|
+
if (args.scene2) cliArgs.push('--scene2', String(args.scene2));
|
|
566
|
+
if (args.scene3) cliArgs.push('--scene3', String(args.scene3));
|
|
567
|
+
if (args.tag) cliArgs.push('--tag', String(args.tag));
|
|
568
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// ── 数据管理 ──
|
|
572
|
+
case 'gym_data_list': {
|
|
573
|
+
const cliArgs = ['data', 'list'];
|
|
574
|
+
if (args.page) cliArgs.push('--page', String(args.page));
|
|
575
|
+
if (args.limit) cliArgs.push('--limit', String(args.limit));
|
|
576
|
+
if (args.status) cliArgs.push('--status', String(args.status));
|
|
577
|
+
if (args.taskId) cliArgs.push('--task-id', String(args.taskId));
|
|
578
|
+
if (args.dataName) cliArgs.push('--data-name', String(args.dataName));
|
|
579
|
+
if (args.taskName) cliArgs.push('--task-name', String(args.taskName));
|
|
580
|
+
if (args.taskCode) cliArgs.push('--task-code', String(args.taskCode));
|
|
581
|
+
if (args.startDate) cliArgs.push('--start-date', String(args.startDate));
|
|
582
|
+
if (args.endDate) cliArgs.push('--end-date', String(args.endDate));
|
|
583
|
+
if (args.tag) cliArgs.push('--tag', String(args.tag));
|
|
584
|
+
if (args.deviceSn) cliArgs.push('--device-sn', String(args.deviceSn));
|
|
585
|
+
if (args.collector) cliArgs.push('--collector', String(args.collector));
|
|
586
|
+
if (args.dataType) cliArgs.push('--data-type', String(args.dataType));
|
|
587
|
+
if (args.projectId) cliArgs.push('--project-id', String(args.projectId));
|
|
588
|
+
if (args.scene1) cliArgs.push('--scene1', String(args.scene1));
|
|
589
|
+
if (args.scene2) cliArgs.push('--scene2', String(args.scene2));
|
|
590
|
+
if (args.scene3) cliArgs.push('--scene3', String(args.scene3));
|
|
591
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
case 'gym_data_detail':
|
|
595
|
+
return { ok: true, data: await runGym(['data', 'detail', String(args.dataId)]) };
|
|
596
|
+
|
|
597
|
+
case 'gym_data_tag_list':
|
|
598
|
+
return { ok: true, data: await runGym(['data', 'tag', 'list', '--project-id', String(args.projectId)]) };
|
|
599
|
+
|
|
600
|
+
case 'gym_data_tag_create':
|
|
601
|
+
return { ok: true, data: await runGym(['data', 'tag', 'create',
|
|
602
|
+
'--project-id', String(args.projectId),
|
|
603
|
+
'--name', String(args.name),
|
|
604
|
+
]) };
|
|
605
|
+
|
|
606
|
+
case 'gym_data_tag_bind': {
|
|
607
|
+
const cliArgs = ['data', 'tag', 'bind',
|
|
608
|
+
'--project-id', String(args.projectId),
|
|
609
|
+
'--labels', String(args.labels),
|
|
610
|
+
];
|
|
611
|
+
if (args.dataIds) cliArgs.push('--data-ids', String(args.dataIds));
|
|
612
|
+
if (args.status) cliArgs.push('--status', String(args.status));
|
|
613
|
+
if (args.taskId) cliArgs.push('--task-id', String(args.taskId));
|
|
614
|
+
if (args.dataName) cliArgs.push('--data-name', String(args.dataName));
|
|
615
|
+
if (args.taskName) cliArgs.push('--task-name', String(args.taskName));
|
|
616
|
+
if (args.taskCode) cliArgs.push('--task-code', String(args.taskCode));
|
|
617
|
+
if (args.startDate) cliArgs.push('--start-date', String(args.startDate));
|
|
618
|
+
if (args.endDate) cliArgs.push('--end-date', String(args.endDate));
|
|
619
|
+
if (args.scene1) cliArgs.push('--scene1', String(args.scene1));
|
|
620
|
+
if (args.scene2) cliArgs.push('--scene2', String(args.scene2));
|
|
621
|
+
if (args.scene3) cliArgs.push('--scene3', String(args.scene3));
|
|
622
|
+
if (args.tag) cliArgs.push('--tag', String(args.tag));
|
|
623
|
+
if (args.deviceSn) cliArgs.push('--device-sn', String(args.deviceSn));
|
|
624
|
+
if (args.collector) cliArgs.push('--collector', String(args.collector));
|
|
625
|
+
if (args.dataType) cliArgs.push('--data-type', String(args.dataType));
|
|
626
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
case 'gym_data_download': {
|
|
630
|
+
const cliArgs = ['data', 'download'];
|
|
631
|
+
if (args.dataId) {
|
|
632
|
+
// 单文件模式:dataId 作为位置参数
|
|
633
|
+
cliArgs.splice(1, 0, String(args.dataId));
|
|
634
|
+
}
|
|
635
|
+
if (args.output) cliArgs.push('--output', String(args.output));
|
|
636
|
+
if (args.batch) cliArgs.push('--batch');
|
|
637
|
+
if (args.force) cliArgs.push('--force');
|
|
638
|
+
if (args.yes) cliArgs.push('--yes');
|
|
639
|
+
if (args.easy) cliArgs.push('--easy');
|
|
640
|
+
if (args.metadata) cliArgs.push('--metadata');
|
|
641
|
+
if (args.noOffline) cliArgs.push('--no-offline');
|
|
642
|
+
if (args.taskId) cliArgs.push('--task-id', String(args.taskId));
|
|
643
|
+
if (args.projectId) cliArgs.push('--project-id', String(args.projectId));
|
|
644
|
+
if (args.tag) cliArgs.push('--tag', String(args.tag));
|
|
645
|
+
if (args.status) cliArgs.push('--status', String(args.status));
|
|
646
|
+
if (args.dataName) cliArgs.push('--data-name', String(args.dataName));
|
|
647
|
+
if (args.startDate) cliArgs.push('--start', String(args.startDate));
|
|
648
|
+
if (args.endDate) cliArgs.push('--end', String(args.endDate));
|
|
649
|
+
if (args.scene1) cliArgs.push('--scene1', String(args.scene1));
|
|
650
|
+
if (args.scene2) cliArgs.push('--scene2', String(args.scene2));
|
|
651
|
+
if (args.scene3) cliArgs.push('--scene3', String(args.scene3));
|
|
652
|
+
if (args.count) cliArgs.push('--count', String(args.count));
|
|
653
|
+
if (args.concurrency) cliArgs.push('--concurrency', String(args.concurrency));
|
|
654
|
+
if (args.page) cliArgs.push('--page', String(args.page));
|
|
655
|
+
if (args.limit) cliArgs.push('--limit', String(args.limit));
|
|
656
|
+
// 下载超时 10 分钟
|
|
657
|
+
return { ok: true, data: await runGym(cliArgs, 600_000) };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
case 'gym_data_upload': {
|
|
661
|
+
const cliArgs = ['data', 'upload',
|
|
662
|
+
'--task-id', String(args.taskId),
|
|
663
|
+
'--bag-path', String(args.bagPath),
|
|
664
|
+
'--device-sn', String(args.deviceSn),
|
|
665
|
+
'--device-type', String(args.deviceType),
|
|
666
|
+
];
|
|
667
|
+
if (args.location) cliArgs.push('--location', String(args.location));
|
|
668
|
+
if (args.dataType) cliArgs.push('--data-type', String(args.dataType));
|
|
669
|
+
if (args.topicsVersion) cliArgs.push('--topics-version', String(args.topicsVersion));
|
|
670
|
+
if (args.lowerVersion) cliArgs.push('--lower-version', String(args.lowerVersion));
|
|
671
|
+
if (args.lowerCommit) cliArgs.push('--lower-commit', String(args.lowerCommit));
|
|
672
|
+
if (args.duration) cliArgs.push('--duration', String(args.duration));
|
|
673
|
+
if (args.remark) cliArgs.push('--remark', String(args.remark));
|
|
674
|
+
// 上传超时 30 分钟
|
|
675
|
+
return { ok: true, data: await runGym(cliArgs, 1_800_000) };
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// ── 设备管理 ──
|
|
679
|
+
case 'gym_device_list':
|
|
680
|
+
return { ok: true, data: await runGym([
|
|
681
|
+
'device', 'list',
|
|
682
|
+
'--page', String(args.page || 1),
|
|
683
|
+
'--limit', String(args.limit || 10),
|
|
684
|
+
]) };
|
|
685
|
+
|
|
686
|
+
// ── 场景管理 ──
|
|
687
|
+
case 'gym_scene_list': {
|
|
688
|
+
const cliArgs = ['scene', 'list', '--project', String(args.projectId)];
|
|
689
|
+
if (args.scene1) cliArgs.push('--scene1', String(args.scene1));
|
|
690
|
+
if (args.scene2) cliArgs.push('--scene2', String(args.scene2));
|
|
691
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// ── 交付任务管理 ──
|
|
695
|
+
case 'gym_delivery_list':
|
|
696
|
+
return { ok: true, data: await runGym([
|
|
697
|
+
'delivery', 'list',
|
|
698
|
+
'--page', String(args.page || 1),
|
|
699
|
+
'--limit', String(args.limit || 10),
|
|
700
|
+
]) };
|
|
701
|
+
|
|
702
|
+
case 'gym_delivery_download': {
|
|
703
|
+
const cliArgs = ['delivery', 'download'];
|
|
704
|
+
if (args.token) {
|
|
705
|
+
cliArgs.push('--token', String(args.token));
|
|
706
|
+
if (args.env) cliArgs.push('--env', String(args.env));
|
|
707
|
+
} else {
|
|
708
|
+
cliArgs.push('--task-id', String(args.taskId));
|
|
709
|
+
cliArgs.push('--business-name', String(args.businessName));
|
|
710
|
+
cliArgs.push('--download-type', String(args.downloadType));
|
|
711
|
+
}
|
|
712
|
+
if (args.saveDir) cliArgs.push('--save-dir', String(args.saveDir));
|
|
713
|
+
if (args.nasSn) cliArgs.push('--nas-sn', String(args.nasSn));
|
|
714
|
+
if (args.concurrency) cliArgs.push('--concurrency', String(args.concurrency));
|
|
715
|
+
// 下载超时 30 分钟
|
|
716
|
+
return { ok: true, data: await runGym(cliArgs, 1_800_000) };
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// ── 文件管理 ──
|
|
720
|
+
case 'gym_file_list': {
|
|
721
|
+
const cliArgs = ['file', 'list', '--record-id', String(args.recordId)];
|
|
722
|
+
if (args.prefix) cliArgs.push('--prefix', String(args.prefix));
|
|
723
|
+
if (args.limit) cliArgs.push('--limit', String(args.limit));
|
|
724
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
case 'gym_file_upload': {
|
|
728
|
+
const cliArgs = ['file', 'upload',
|
|
729
|
+
'--record-id', String(args.recordId),
|
|
730
|
+
'--source', String(args.source),
|
|
731
|
+
];
|
|
732
|
+
if (args.path) cliArgs.push('--path', String(args.path));
|
|
733
|
+
if (args.stallTimeout) cliArgs.push('--stall-timeout', String(args.stallTimeout));
|
|
734
|
+
// 上传超时 30 分钟
|
|
735
|
+
return { ok: true, data: await runGym(cliArgs, 1_800_000) };
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
case 'gym_file_download': {
|
|
739
|
+
const cliArgs = ['file', 'download',
|
|
740
|
+
'--record-id', String(args.recordId),
|
|
741
|
+
'--file-path', String(args.filePath),
|
|
742
|
+
];
|
|
743
|
+
if (args.output) cliArgs.push('--output', String(args.output));
|
|
744
|
+
if (args.recursive) cliArgs.push('--recursive');
|
|
745
|
+
// 下载超时 10 分钟
|
|
746
|
+
return { ok: true, data: await runGym(cliArgs, 600_000) };
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// ── 文件记录管理 ──
|
|
750
|
+
case 'gym_record_list':
|
|
751
|
+
return { ok: true, data: await runGym([
|
|
752
|
+
'record', 'list',
|
|
753
|
+
'--page', String(args.page || 1),
|
|
754
|
+
'--limit', String(args.limit || 10),
|
|
755
|
+
]) };
|
|
756
|
+
|
|
757
|
+
case 'gym_record_create': {
|
|
758
|
+
const cliArgs = ['record', 'create',
|
|
759
|
+
'--title', String(args.title),
|
|
760
|
+
'--project-id', String(args.projectId),
|
|
761
|
+
];
|
|
762
|
+
if (args.deviceId) cliArgs.push('--device-id', String(args.deviceId));
|
|
763
|
+
if (args.description) cliArgs.push('--description', String(args.description));
|
|
764
|
+
if (args.visibility) cliArgs.push('--visibility', String(args.visibility));
|
|
765
|
+
return { ok: true, data: await runGym(cliArgs) };
|
|
104
766
|
}
|
|
105
767
|
|
|
106
768
|
default:
|