@josephyan/qingflow-cli 0.2.0-beta.58 → 0.2.0-beta.60

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.
Files changed (35) hide show
  1. package/README.md +3 -2
  2. package/docs/local-agent-install.md +9 -0
  3. package/npm/bin/qingflow.mjs +1 -1
  4. package/npm/lib/runtime.mjs +156 -21
  5. package/package.json +1 -1
  6. package/pyproject.toml +1 -1
  7. package/src/qingflow_mcp/builder_facade/service.py +670 -191
  8. package/src/qingflow_mcp/cli/commands/app.py +16 -16
  9. package/src/qingflow_mcp/cli/commands/auth.py +19 -16
  10. package/src/qingflow_mcp/cli/commands/builder.py +124 -162
  11. package/src/qingflow_mcp/cli/commands/common.py +21 -95
  12. package/src/qingflow_mcp/cli/commands/imports.py +42 -34
  13. package/src/qingflow_mcp/cli/commands/record.py +131 -133
  14. package/src/qingflow_mcp/cli/commands/task.py +43 -44
  15. package/src/qingflow_mcp/cli/commands/workspace.py +10 -10
  16. package/src/qingflow_mcp/cli/context.py +35 -32
  17. package/src/qingflow_mcp/cli/formatters.py +124 -121
  18. package/src/qingflow_mcp/cli/main.py +52 -17
  19. package/src/qingflow_mcp/server_app_builder.py +122 -190
  20. package/src/qingflow_mcp/server_app_user.py +63 -662
  21. package/src/qingflow_mcp/solution/executor.py +63 -4
  22. package/src/qingflow_mcp/tools/solution_tools.py +115 -3
  23. package/src/qingflow_mcp/ops/__init__.py +0 -3
  24. package/src/qingflow_mcp/ops/apps.py +0 -64
  25. package/src/qingflow_mcp/ops/auth.py +0 -121
  26. package/src/qingflow_mcp/ops/base.py +0 -290
  27. package/src/qingflow_mcp/ops/builder.py +0 -357
  28. package/src/qingflow_mcp/ops/context.py +0 -120
  29. package/src/qingflow_mcp/ops/directory.py +0 -171
  30. package/src/qingflow_mcp/ops/feedback.py +0 -49
  31. package/src/qingflow_mcp/ops/files.py +0 -78
  32. package/src/qingflow_mcp/ops/imports.py +0 -140
  33. package/src/qingflow_mcp/ops/records.py +0 -415
  34. package/src/qingflow_mcp/ops/tasks.py +0 -171
  35. package/src/qingflow_mcp/ops/workspace.py +0 -76
@@ -1,415 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- from .base import normalize_exception, success_result, tool_payload
6
-
7
-
8
- class RecordOperations:
9
- def __init__(self, tools: Any) -> None:
10
- self._tools = tools
11
-
12
- def schema(
13
- self,
14
- *,
15
- profile: str,
16
- app_key: str,
17
- mode: str,
18
- view_id: str | None,
19
- record_id: int | None,
20
- output_profile: str = "normal",
21
- ) -> dict:
22
- try:
23
- if mode == "applicant":
24
- raw = self._tools.record.record_schema_get(
25
- profile=profile,
26
- app_key=app_key,
27
- schema_mode="applicant",
28
- output_profile=output_profile,
29
- )
30
- elif mode == "browse":
31
- raw = self._tools.record.record_browse_schema_get_public(
32
- profile=profile,
33
- app_key=app_key,
34
- view_id=view_id,
35
- output_profile=output_profile,
36
- )
37
- elif mode == "insert":
38
- raw = self._tools.record.record_insert_schema_get_public(
39
- profile=profile,
40
- app_key=app_key,
41
- output_profile=output_profile,
42
- )
43
- elif mode == "update":
44
- raw = self._tools.record.record_update_schema_get_public(
45
- profile=profile,
46
- app_key=app_key,
47
- record_id=record_id,
48
- output_profile=output_profile,
49
- )
50
- elif mode == "import":
51
- raw = self._tools.imports.record_import_schema_get(
52
- profile=profile,
53
- app_key=app_key,
54
- output_profile=output_profile,
55
- )
56
- else:
57
- raw = self._tools.code_block.record_code_block_schema_get_public(
58
- profile=profile,
59
- app_key=app_key,
60
- output_profile=output_profile,
61
- )
62
- except Exception as error: # noqa: BLE001
63
- return normalize_exception(error)
64
- return success_result(
65
- "RECORD_SCHEMA_READY",
66
- "已读取表结构",
67
- data={"mode": mode, "schema": tool_payload(raw)},
68
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
69
- meta={"profile": profile},
70
- legacy=raw,
71
- )
72
-
73
- def list(
74
- self,
75
- *,
76
- profile: str,
77
- app_key: str,
78
- columns: list[Any],
79
- where: list[Any],
80
- order_by: list[Any],
81
- limit: int,
82
- page: int,
83
- view_id: str | None,
84
- list_type: int | None,
85
- view_key: str | None,
86
- view_name: str | None,
87
- output_profile: str = "normal",
88
- ) -> dict:
89
- try:
90
- raw = self._tools.record.record_list(
91
- profile=profile,
92
- app_key=app_key,
93
- columns=columns,
94
- where=where,
95
- order_by=order_by,
96
- limit=limit,
97
- page=page,
98
- view_id=view_id,
99
- list_type=list_type,
100
- view_key=view_key,
101
- view_name=view_name,
102
- output_profile=output_profile,
103
- )
104
- except Exception as error: # noqa: BLE001
105
- return normalize_exception(error)
106
- data = tool_payload(raw)
107
- return success_result(
108
- "RECORDS_LISTED",
109
- "已读取记录列表",
110
- data=data if isinstance(data, dict) else {"items": data},
111
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
112
- meta={"profile": profile},
113
- legacy=raw,
114
- )
115
-
116
- def get(
117
- self,
118
- *,
119
- profile: str,
120
- app_key: str,
121
- record_id: int,
122
- columns: list[Any],
123
- view_id: str | None,
124
- workflow_node_id: int | None,
125
- output_profile: str = "normal",
126
- ) -> dict:
127
- try:
128
- raw = self._tools.record.record_get_public(
129
- profile=profile,
130
- app_key=app_key,
131
- record_id=record_id,
132
- columns=columns,
133
- view_id=view_id,
134
- workflow_node_id=workflow_node_id,
135
- output_profile=output_profile,
136
- )
137
- except Exception as error: # noqa: BLE001
138
- return normalize_exception(error)
139
- return success_result(
140
- "RECORD_SHOWN",
141
- "已读取单条记录",
142
- data={"record": tool_payload(raw), "record_id": record_id},
143
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
144
- meta={"profile": profile},
145
- legacy=raw,
146
- )
147
-
148
- def create(
149
- self,
150
- *,
151
- profile: str,
152
- app_key: str,
153
- fields: dict[str, Any],
154
- verify_write: bool,
155
- output_profile: str = "normal",
156
- ) -> dict:
157
- try:
158
- raw = self._tools.record.record_insert_public(
159
- profile=profile,
160
- app_key=app_key,
161
- fields=fields,
162
- verify_write=verify_write,
163
- output_profile=output_profile,
164
- )
165
- except Exception as error: # noqa: BLE001
166
- return normalize_exception(error)
167
- data = tool_payload(raw)
168
- return success_result(
169
- "RECORD_CREATED",
170
- "已新增记录",
171
- data={
172
- "record_id": _extract_record_id(data),
173
- "record_ids": _extract_record_ids(data),
174
- "result": data,
175
- },
176
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
177
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
178
- legacy=raw,
179
- )
180
-
181
- def update(
182
- self,
183
- *,
184
- profile: str,
185
- app_key: str,
186
- record_id: int | None,
187
- fields: dict[str, Any],
188
- verify_write: bool,
189
- output_profile: str = "normal",
190
- ) -> dict:
191
- try:
192
- raw = self._tools.record.record_update_public(
193
- profile=profile,
194
- app_key=app_key,
195
- record_id=record_id,
196
- fields=fields,
197
- verify_write=verify_write,
198
- output_profile=output_profile,
199
- )
200
- except Exception as error: # noqa: BLE001
201
- return normalize_exception(error)
202
- data = tool_payload(raw)
203
- return success_result(
204
- "RECORD_UPDATED",
205
- "已更新记录",
206
- data={
207
- "record_id": _extract_record_id(data) or record_id,
208
- "record_ids": _extract_record_ids(data) or [record_id],
209
- "result": data,
210
- },
211
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
212
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
213
- legacy=raw,
214
- )
215
-
216
- def delete(
217
- self,
218
- *,
219
- profile: str,
220
- app_key: str,
221
- record_id: int | None,
222
- record_ids: list[int],
223
- output_profile: str = "normal",
224
- ) -> dict:
225
- try:
226
- raw = self._tools.record.record_delete_public(
227
- profile=profile,
228
- app_key=app_key,
229
- record_id=record_id,
230
- record_ids=record_ids,
231
- output_profile=output_profile,
232
- )
233
- except Exception as error: # noqa: BLE001
234
- return normalize_exception(error)
235
- data = tool_payload(raw)
236
- removed = _extract_record_ids(data) or ([record_id] if record_id else record_ids)
237
- return success_result(
238
- "RECORDS_DELETED",
239
- "已删除记录",
240
- data={"record_ids": removed, "result": data},
241
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
242
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
243
- legacy=raw,
244
- )
245
-
246
- def analyze(
247
- self,
248
- *,
249
- profile: str,
250
- app_key: str,
251
- dimensions: list[Any],
252
- metrics: list[Any],
253
- filters: list[Any],
254
- sort: list[Any],
255
- limit: int,
256
- strict_full: bool,
257
- view_id: str | None,
258
- list_type: int | None,
259
- view_key: str | None,
260
- view_name: str | None,
261
- output_profile: str = "normal",
262
- ) -> dict:
263
- try:
264
- raw = self._tools.record.record_analyze(
265
- profile=profile,
266
- app_key=app_key,
267
- dimensions=dimensions,
268
- metrics=metrics,
269
- filters=filters,
270
- sort=sort,
271
- limit=limit,
272
- strict_full=strict_full,
273
- view_id=view_id,
274
- list_type=list_type,
275
- view_key=view_key,
276
- view_name=view_name,
277
- output_profile=output_profile,
278
- )
279
- except Exception as error: # noqa: BLE001
280
- return normalize_exception(error)
281
- return success_result(
282
- "RECORDS_ANALYZED",
283
- "已完成数据分析",
284
- data=tool_payload(raw),
285
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
286
- meta={"profile": profile},
287
- legacy=raw,
288
- )
289
-
290
- def run_code(
291
- self,
292
- *,
293
- profile: str,
294
- app_key: str,
295
- record_id: int,
296
- code_block_field: str,
297
- role: int,
298
- workflow_node_id: int | None,
299
- answers: list[Any],
300
- fields: dict[str, Any],
301
- manual: bool,
302
- verify_writeback: bool,
303
- force_refresh_form: bool,
304
- output_profile: str = "normal",
305
- ) -> dict:
306
- try:
307
- raw = self._tools.code_block.record_code_block_run(
308
- profile=profile,
309
- app_key=app_key,
310
- record_id=record_id,
311
- code_block_field=code_block_field,
312
- role=role,
313
- workflow_node_id=workflow_node_id,
314
- answers=answers,
315
- fields=fields,
316
- manual=manual,
317
- verify_writeback=verify_writeback,
318
- force_refresh_form=force_refresh_form,
319
- output_profile=output_profile,
320
- )
321
- except Exception as error: # noqa: BLE001
322
- return normalize_exception(error)
323
- return success_result(
324
- "CODE_BLOCK_EXECUTED",
325
- "已执行代码块字段",
326
- data=tool_payload(raw),
327
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
328
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
329
- legacy=raw,
330
- )
331
-
332
- def member_candidates(
333
- self,
334
- *,
335
- profile: str,
336
- app_key: str,
337
- field_id: int,
338
- keyword: str,
339
- page_num: int,
340
- page_size: int,
341
- ) -> dict:
342
- try:
343
- raw = self._tools.record.record_member_candidates(
344
- profile=profile,
345
- app_key=app_key,
346
- field_id=field_id,
347
- keyword=keyword,
348
- page_num=page_num,
349
- page_size=page_size,
350
- )
351
- except Exception as error: # noqa: BLE001
352
- return normalize_exception(error)
353
- return success_result(
354
- "RECORD_MEMBER_CANDIDATES_READY",
355
- "已读取成员候选范围",
356
- data=tool_payload(raw),
357
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
358
- meta={"profile": profile},
359
- legacy=raw,
360
- )
361
-
362
- def department_candidates(
363
- self,
364
- *,
365
- profile: str,
366
- app_key: str,
367
- field_id: int,
368
- keyword: str,
369
- page_num: int,
370
- page_size: int,
371
- ) -> dict:
372
- try:
373
- raw = self._tools.record.record_department_candidates(
374
- profile=profile,
375
- app_key=app_key,
376
- field_id=field_id,
377
- keyword=keyword,
378
- page_num=page_num,
379
- page_size=page_size,
380
- )
381
- except Exception as error: # noqa: BLE001
382
- return normalize_exception(error)
383
- return success_result(
384
- "RECORD_DEPARTMENT_CANDIDATES_READY",
385
- "已读取部门候选范围",
386
- data=tool_payload(raw),
387
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
388
- meta={"profile": profile},
389
- legacy=raw,
390
- )
391
-
392
-
393
- def _extract_record_id(data: Any) -> int | None:
394
- if not isinstance(data, dict):
395
- return None
396
- value = data.get("record_id")
397
- if isinstance(value, int):
398
- return value
399
- resource = data.get("resource")
400
- if isinstance(resource, dict) and isinstance(resource.get("record_id"), int):
401
- return resource.get("record_id")
402
- return None
403
-
404
-
405
- def _extract_record_ids(data: Any) -> list[int]:
406
- if not isinstance(data, dict):
407
- return []
408
- value = data.get("record_ids")
409
- if isinstance(value, list):
410
- return [item for item in value if isinstance(item, int)]
411
- resource = data.get("resource")
412
- if isinstance(resource, dict) and isinstance(resource.get("record_ids"), list):
413
- return [item for item in resource["record_ids"] if isinstance(item, int)]
414
- record_id = _extract_record_id(data)
415
- return [record_id] if record_id is not None else []
@@ -1,171 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- from .base import normalize_exception, success_result, tool_payload
6
-
7
-
8
- class TaskOperations:
9
- def __init__(self, tools: Any) -> None:
10
- self._tools = tools
11
-
12
- def list(
13
- self,
14
- *,
15
- profile: str,
16
- task_box: str,
17
- flow_status: str,
18
- app_key: str | None,
19
- workflow_node_id: int | None,
20
- query: str | None,
21
- page: int,
22
- page_size: int,
23
- ) -> dict:
24
- try:
25
- raw = self._tools.task.task_list(
26
- profile=profile,
27
- task_box=task_box,
28
- flow_status=flow_status,
29
- app_key=app_key,
30
- workflow_node_id=workflow_node_id,
31
- query=query,
32
- page=page,
33
- page_size=page_size,
34
- )
35
- except Exception as error: # noqa: BLE001
36
- return normalize_exception(error)
37
- data = tool_payload(raw)
38
- if isinstance(data, dict) and isinstance(data.get("items"), list):
39
- items = []
40
- for item in data["items"]:
41
- if not isinstance(item, dict):
42
- continue
43
- title = item.get("title") or item.get("task_name") or item.get("node_name")
44
- if not title:
45
- app_name = item.get("app_name") or item.get("app_key") or "任务"
46
- record_id = item.get("record_id")
47
- title = f"{app_name}#{record_id}" if record_id is not None else str(app_name)
48
- normalized = dict(item)
49
- normalized["title"] = title
50
- items.append(normalized)
51
- data = dict(data)
52
- data["items"] = items
53
- return success_result(
54
- "TASKS_LISTED",
55
- "已读取待办列表",
56
- data=data if isinstance(data, dict) else {"items": data},
57
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
58
- meta={"profile": profile},
59
- legacy=raw,
60
- )
61
-
62
- def show(
63
- self,
64
- *,
65
- profile: str,
66
- app_key: str,
67
- record_id: int,
68
- workflow_node_id: int,
69
- include_candidates: bool,
70
- include_associated_reports: bool,
71
- ) -> dict:
72
- try:
73
- raw = self._tools.task.task_get(
74
- profile=profile,
75
- app_key=app_key,
76
- record_id=record_id,
77
- workflow_node_id=workflow_node_id,
78
- include_candidates=include_candidates,
79
- include_associated_reports=include_associated_reports,
80
- )
81
- except Exception as error: # noqa: BLE001
82
- return normalize_exception(error)
83
- return success_result(
84
- "TASK_READY",
85
- "已读取待办详情",
86
- data=tool_payload(raw),
87
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
88
- meta={"profile": profile},
89
- legacy=raw,
90
- )
91
-
92
- def act(
93
- self,
94
- *,
95
- profile: str,
96
- app_key: str,
97
- record_id: int,
98
- workflow_node_id: int,
99
- action: str,
100
- payload: dict,
101
- ) -> dict:
102
- try:
103
- raw = self._tools.task.task_action_execute(
104
- profile=profile,
105
- app_key=app_key,
106
- record_id=record_id,
107
- workflow_node_id=workflow_node_id,
108
- action=action,
109
- payload=payload,
110
- )
111
- except Exception as error: # noqa: BLE001
112
- return normalize_exception(error)
113
- return success_result(
114
- "TASK_ACTION_EXECUTED",
115
- "已执行待办动作",
116
- data=tool_payload(raw),
117
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
118
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
119
- legacy=raw,
120
- )
121
-
122
- def log(self, *, profile: str, app_key: str, record_id: int, workflow_node_id: int) -> dict:
123
- try:
124
- raw = self._tools.task.task_workflow_log_get(
125
- profile=profile,
126
- app_key=app_key,
127
- record_id=record_id,
128
- workflow_node_id=workflow_node_id,
129
- )
130
- except Exception as error: # noqa: BLE001
131
- return normalize_exception(error)
132
- return success_result(
133
- "TASK_LOG_READY",
134
- "已读取流程日志",
135
- data=tool_payload(raw),
136
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
137
- meta={"profile": profile},
138
- legacy=raw,
139
- )
140
-
141
- def associated_report_detail(
142
- self,
143
- *,
144
- profile: str,
145
- app_key: str,
146
- record_id: int,
147
- workflow_node_id: int,
148
- report_id: int,
149
- page: int,
150
- page_size: int,
151
- ) -> dict:
152
- try:
153
- raw = self._tools.task.task_associated_report_detail_get(
154
- profile=profile,
155
- app_key=app_key,
156
- record_id=record_id,
157
- workflow_node_id=workflow_node_id,
158
- report_id=report_id,
159
- page=page,
160
- page_size=page_size,
161
- )
162
- except Exception as error: # noqa: BLE001
163
- return normalize_exception(error)
164
- return success_result(
165
- "TASK_ASSOCIATED_REPORT_READY",
166
- "已读取关联报表详情",
167
- data=tool_payload(raw),
168
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
169
- meta={"profile": profile},
170
- legacy=raw,
171
- )
@@ -1,76 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- from .base import normalize_exception, success_result
6
-
7
-
8
- class WorkspaceOperations:
9
- def __init__(self, tools: Any) -> None:
10
- self._tools = tools
11
-
12
- def list(
13
- self,
14
- *,
15
- profile: str,
16
- page_num: int,
17
- page_size: int,
18
- include_external: bool,
19
- ) -> dict:
20
- try:
21
- raw = self._tools.workspace.workspace_list(
22
- profile=profile,
23
- page_num=page_num,
24
- page_size=page_size,
25
- include_external=include_external,
26
- )
27
- except Exception as error: # noqa: BLE001
28
- return normalize_exception(error)
29
- page = raw.get("page") if isinstance(raw.get("page"), dict) else {}
30
- items = page.get("list") if isinstance(page.get("list"), list) else []
31
- normalized = [
32
- {
33
- "ws_id": item.get("wsId"),
34
- "name": item.get("workspaceName") or item.get("wsName"),
35
- "remark": item.get("remark"),
36
- "system_version": item.get("systemVersion"),
37
- }
38
- for item in items
39
- if isinstance(item, dict)
40
- ]
41
- return success_result(
42
- "WORKSPACES_LISTED",
43
- "已读取工作区列表",
44
- data={
45
- "items": normalized,
46
- "page_num": page_num,
47
- "page_size": page_size,
48
- "include_external": include_external,
49
- "total": page.get("total"),
50
- },
51
- meta={"profile": profile},
52
- legacy=raw,
53
- )
54
-
55
- def use(self, *, profile: str, ws_id: int) -> dict:
56
- try:
57
- raw = self._tools.workspace.workspace_select(profile=profile, ws_id=ws_id)
58
- except Exception as error: # noqa: BLE001
59
- return normalize_exception(error)
60
- workspace = raw.get("workspace") if isinstance(raw.get("workspace"), dict) else {}
61
- return success_result(
62
- "WORKSPACE_SELECTED",
63
- "已切换工作区",
64
- data={
65
- "workspace": {
66
- "ws_id": raw.get("selected_ws_id", ws_id),
67
- "name": raw.get("selected_ws_name"),
68
- "remark": workspace.get("remark"),
69
- "system_version": workspace.get("systemVersion"),
70
- },
71
- "qf_version": raw.get("qf_version"),
72
- "qf_version_source": raw.get("qf_version_source"),
73
- },
74
- meta={"profile": profile, "request_route": raw.get("request_route")},
75
- legacy=raw,
76
- )