@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,120 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass
4
-
5
- from ..backend_client import BackendClient
6
- from ..builder_facade.service import AiBuilderFacade
7
- from ..session_store import SessionStore
8
- from ..tools.ai_builder_tools import AiBuilderTools
9
- from ..tools.app_tools import AppTools
10
- from ..tools.auth_tools import AuthTools
11
- from ..tools.code_block_tools import CodeBlockTools
12
- from ..tools.directory_tools import DirectoryTools
13
- from ..tools.feedback_tools import FeedbackTools
14
- from ..tools.file_tools import FileTools
15
- from ..tools.import_tools import ImportTools
16
- from ..tools.package_tools import PackageTools
17
- from ..tools.portal_tools import PortalTools
18
- from ..tools.qingbi_report_tools import QingbiReportTools
19
- from ..tools.record_tools import RecordTools
20
- from ..tools.role_tools import RoleTools
21
- from ..tools.solution_tools import SolutionTools
22
- from ..tools.task_context_tools import TaskContextTools
23
- from ..tools.view_tools import ViewTools
24
- from ..tools.workflow_tools import WorkflowTools
25
- from ..tools.workspace_tools import WorkspaceTools
26
- from .apps import AppOperations
27
- from .auth import AuthOperations
28
- from .builder import BuilderOperations
29
- from .directory import DirectoryOperations
30
- from .feedback import FeedbackOperations
31
- from .files import FileOperations
32
- from .imports import ImportOperations
33
- from .records import RecordOperations
34
- from .tasks import TaskOperations
35
- from .workspace import WorkspaceOperations
36
-
37
-
38
- @dataclass(slots=True)
39
- class Toolset:
40
- auth: AuthTools
41
- workspace: WorkspaceTools
42
- app: AppTools
43
- file: FileTools
44
- feedback: FeedbackTools
45
- directory: DirectoryTools
46
- record: RecordTools
47
- code_block: CodeBlockTools
48
- imports: ImportTools
49
- task: TaskContextTools
50
- builder: AiBuilderTools
51
- facade: AiBuilderFacade
52
-
53
-
54
- @dataclass(slots=True)
55
- class OperationsRuntime:
56
- sessions: SessionStore
57
- backend: BackendClient
58
- tools: Toolset
59
- auth: AuthOperations
60
- workspace: WorkspaceOperations
61
- apps: AppOperations
62
- files: FileOperations
63
- feedback: FeedbackOperations
64
- directory: DirectoryOperations
65
- records: RecordOperations
66
- imports: ImportOperations
67
- tasks: TaskOperations
68
- builder: BuilderOperations
69
-
70
- def close(self) -> None:
71
- self.backend.close()
72
-
73
- def build_operations_runtime(
74
- *,
75
- sessions: SessionStore | None = None,
76
- backend: BackendClient | None = None,
77
- feedback_mcp_side: str = "CLI",
78
- ) -> OperationsRuntime:
79
- sessions = sessions or SessionStore()
80
- backend = backend or BackendClient()
81
- facade = AiBuilderFacade(
82
- apps=AppTools(sessions, backend),
83
- packages=PackageTools(sessions, backend),
84
- views=ViewTools(sessions, backend),
85
- workflows=WorkflowTools(sessions, backend),
86
- portals=PortalTools(sessions, backend),
87
- charts=QingbiReportTools(sessions, backend),
88
- roles=RoleTools(sessions, backend),
89
- directory=DirectoryTools(sessions, backend),
90
- solutions=SolutionTools(sessions, backend),
91
- )
92
- tools = Toolset(
93
- auth=AuthTools(sessions, backend),
94
- workspace=WorkspaceTools(sessions, backend),
95
- app=AppTools(sessions, backend),
96
- file=FileTools(sessions, backend),
97
- feedback=FeedbackTools(backend, mcp_side=feedback_mcp_side),
98
- directory=DirectoryTools(sessions, backend),
99
- record=RecordTools(sessions, backend),
100
- code_block=CodeBlockTools(sessions, backend),
101
- imports=ImportTools(sessions, backend),
102
- task=TaskContextTools(sessions, backend),
103
- builder=AiBuilderTools(sessions, backend),
104
- facade=facade,
105
- )
106
- return OperationsRuntime(
107
- sessions=sessions,
108
- backend=backend,
109
- tools=tools,
110
- auth=AuthOperations(tools),
111
- workspace=WorkspaceOperations(tools),
112
- apps=AppOperations(tools),
113
- files=FileOperations(tools),
114
- feedback=FeedbackOperations(tools),
115
- directory=DirectoryOperations(tools),
116
- records=RecordOperations(tools),
117
- imports=ImportOperations(tools),
118
- tasks=TaskOperations(tools),
119
- builder=BuilderOperations(tools),
120
- )
@@ -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 DirectoryOperations:
9
- def __init__(self, tools: Any) -> None:
10
- self._tools = tools
11
-
12
- def search(
13
- self,
14
- *,
15
- profile: str,
16
- query: str,
17
- scopes: list[str] | None,
18
- page_num: int,
19
- page_size: int,
20
- ) -> dict:
21
- return self._call(
22
- "DIRECTORY_SEARCH_READY",
23
- "已完成目录搜索",
24
- profile=profile,
25
- raw_call=lambda: self._tools.directory.directory_search(
26
- profile=profile,
27
- query=query,
28
- scopes=scopes,
29
- page_num=page_num,
30
- page_size=page_size,
31
- ),
32
- )
33
-
34
- def list_internal_users(
35
- self,
36
- *,
37
- profile: str,
38
- keyword: str | None,
39
- department_id: int | None,
40
- role_id: int | None,
41
- page_num: int,
42
- page_size: int,
43
- include_disabled: bool,
44
- ) -> dict:
45
- return self._call(
46
- "DIRECTORY_INTERNAL_USERS_READY",
47
- "已读取内部成员",
48
- profile=profile,
49
- raw_call=lambda: self._tools.directory.directory_list_internal_users(
50
- profile=profile,
51
- keyword=keyword,
52
- dept_id=department_id,
53
- role_id=role_id,
54
- page_num=page_num,
55
- page_size=page_size,
56
- contain_disable=include_disabled,
57
- ),
58
- )
59
-
60
- def list_all_internal_users(
61
- self,
62
- *,
63
- profile: str,
64
- keyword: str | None,
65
- department_id: int | None,
66
- role_id: int | None,
67
- page_size: int,
68
- include_disabled: bool,
69
- max_pages: int,
70
- ) -> dict:
71
- return self._call(
72
- "DIRECTORY_ALL_INTERNAL_USERS_READY",
73
- "已读取全部内部成员",
74
- profile=profile,
75
- raw_call=lambda: self._tools.directory.directory_list_all_internal_users(
76
- profile=profile,
77
- keyword=keyword,
78
- dept_id=department_id,
79
- role_id=role_id,
80
- page_size=page_size,
81
- contain_disable=include_disabled,
82
- max_pages=max_pages,
83
- ),
84
- )
85
-
86
- def list_internal_departments(
87
- self,
88
- *,
89
- profile: str,
90
- keyword: str,
91
- page_num: int,
92
- page_size: int,
93
- ) -> dict:
94
- return self._call(
95
- "DIRECTORY_INTERNAL_DEPARTMENTS_READY",
96
- "已读取内部部门",
97
- profile=profile,
98
- raw_call=lambda: self._tools.directory.directory_list_internal_departments(
99
- profile=profile,
100
- keyword=keyword,
101
- page_num=page_num,
102
- page_size=page_size,
103
- ),
104
- )
105
-
106
- def list_all_departments(
107
- self,
108
- *,
109
- profile: str,
110
- parent_department_id: int | None,
111
- max_depth: int,
112
- max_items: int,
113
- ) -> dict:
114
- return self._call(
115
- "DIRECTORY_ALL_DEPARTMENTS_READY",
116
- "已读取全部部门",
117
- profile=profile,
118
- raw_call=lambda: self._tools.directory.directory_list_all_departments(
119
- profile=profile,
120
- parent_dept_id=parent_department_id,
121
- max_depth=max_depth,
122
- max_items=max_items,
123
- ),
124
- )
125
-
126
- def list_sub_departments(self, *, profile: str, parent_department_id: int | None) -> dict:
127
- return self._call(
128
- "DIRECTORY_SUB_DEPARTMENTS_READY",
129
- "已读取子部门",
130
- profile=profile,
131
- raw_call=lambda: self._tools.directory.directory_list_sub_departments(
132
- profile=profile,
133
- parent_dept_id=parent_department_id,
134
- ),
135
- )
136
-
137
- def list_external_members(
138
- self,
139
- *,
140
- profile: str,
141
- keyword: str | None,
142
- page_num: int,
143
- page_size: int,
144
- simple: bool,
145
- ) -> dict:
146
- return self._call(
147
- "DIRECTORY_EXTERNAL_MEMBERS_READY",
148
- "已读取外部联系人",
149
- profile=profile,
150
- raw_call=lambda: self._tools.directory.directory_list_external_members(
151
- profile=profile,
152
- keyword=keyword,
153
- page_num=page_num,
154
- page_size=page_size,
155
- simple=simple,
156
- ),
157
- )
158
-
159
- def _call(self, code: str, message: str, *, profile: str, raw_call: Any) -> dict:
160
- try:
161
- raw = raw_call()
162
- except Exception as error: # noqa: BLE001
163
- return normalize_exception(error)
164
- return success_result(
165
- code,
166
- message,
167
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
168
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
169
- meta={"profile": profile, "request_route": raw.get("request_route") if isinstance(raw, dict) else None},
170
- legacy=raw,
171
- )
@@ -1,49 +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 FeedbackOperations:
9
- def __init__(self, tools: Any) -> None:
10
- self._tools = tools
11
-
12
- def submit(
13
- self,
14
- *,
15
- category: str,
16
- title: str,
17
- description: str,
18
- expected_behavior: str | None,
19
- actual_behavior: str | None,
20
- impact_scope: str | None,
21
- tool_name: str | None,
22
- app_key: str | None,
23
- record_id: str | int | None,
24
- workflow_node_id: str | int | None,
25
- note: str | None,
26
- ) -> dict:
27
- try:
28
- raw = self._tools.feedback.feedback_submit(
29
- category=category,
30
- title=title,
31
- description=description,
32
- expected_behavior=expected_behavior,
33
- actual_behavior=actual_behavior,
34
- impact_scope=impact_scope,
35
- tool_name=tool_name,
36
- app_key=app_key,
37
- record_id=record_id,
38
- workflow_node_id=workflow_node_id,
39
- note=note,
40
- )
41
- except Exception as error: # noqa: BLE001
42
- return normalize_exception(error)
43
- return success_result(
44
- "FEEDBACK_SUBMITTED",
45
- "已提交需求反馈",
46
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
47
- meta={"request_route": raw.get("request_route") if isinstance(raw, dict) else None},
48
- legacy=raw,
49
- )
@@ -1,78 +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 FileOperations:
9
- def __init__(self, tools: Any) -> None:
10
- self._tools = tools
11
-
12
- def get_upload_info(
13
- self,
14
- *,
15
- profile: str,
16
- upload_kind: str,
17
- file_name: str,
18
- file_size: int,
19
- upload_mark: str | None,
20
- content_type: str | None,
21
- bucket_type: str | None,
22
- path_id: int | None,
23
- file_related_url: str | None,
24
- ) -> dict:
25
- try:
26
- raw = self._tools.file.file_get_upload_info(
27
- profile=profile,
28
- upload_kind=upload_kind,
29
- file_name=file_name,
30
- file_size=file_size,
31
- upload_mark=upload_mark,
32
- content_type=content_type,
33
- bucket_type=bucket_type,
34
- path_id=path_id,
35
- file_related_url=file_related_url,
36
- )
37
- except Exception as error: # noqa: BLE001
38
- return normalize_exception(error)
39
- return success_result(
40
- "UPLOAD_INFO_READY",
41
- "已获取上传信息",
42
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
43
- meta={"profile": profile, "request_route": raw.get("request_route") if isinstance(raw, dict) else None},
44
- legacy=raw,
45
- )
46
-
47
- def upload_local(
48
- self,
49
- *,
50
- profile: str,
51
- upload_kind: str,
52
- file_path: str,
53
- upload_mark: str | None,
54
- content_type: str | None,
55
- bucket_type: str | None,
56
- path_id: int | None,
57
- file_related_url: str | None,
58
- ) -> dict:
59
- try:
60
- raw = self._tools.file.file_upload_local(
61
- profile=profile,
62
- upload_kind=upload_kind,
63
- file_path=file_path,
64
- upload_mark=upload_mark,
65
- content_type=content_type,
66
- bucket_type=bucket_type,
67
- path_id=path_id,
68
- file_related_url=file_related_url,
69
- )
70
- except Exception as error: # noqa: BLE001
71
- return normalize_exception(error)
72
- return success_result(
73
- "FILE_UPLOADED",
74
- "已完成本地文件上传",
75
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
76
- meta={"profile": profile, "request_route": raw.get("request_route") if isinstance(raw, dict) else None},
77
- legacy=raw,
78
- )
@@ -1,140 +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 ImportOperations:
9
- def __init__(self, tools: Any) -> None:
10
- self._tools = tools
11
-
12
- def schema(self, *, profile: str, app_key: str, output_profile: str = "normal") -> dict:
13
- try:
14
- raw = self._tools.imports.record_import_schema_get(
15
- profile=profile,
16
- app_key=app_key,
17
- output_profile=output_profile,
18
- )
19
- except Exception as error: # noqa: BLE001
20
- return normalize_exception(error)
21
- return success_result(
22
- "IMPORT_SCHEMA_READY",
23
- "已读取导入字段结构",
24
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
25
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
26
- meta={"profile": profile},
27
- legacy=raw,
28
- )
29
-
30
- def template(self, *, profile: str, app_key: str, download_to_path: str | None) -> dict:
31
- try:
32
- raw = self._tools.imports.record_import_template_get(
33
- profile=profile,
34
- app_key=app_key,
35
- download_to_path=download_to_path,
36
- )
37
- except Exception as error: # noqa: BLE001
38
- return normalize_exception(error)
39
- return success_result(
40
- "IMPORT_TEMPLATE_READY",
41
- "已获取导入模板",
42
- data=tool_payload(raw),
43
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
44
- meta={"profile": profile},
45
- legacy=raw,
46
- )
47
-
48
- def verify(self, *, profile: str, app_key: str, file_path: str) -> dict:
49
- try:
50
- raw = self._tools.imports.record_import_verify(profile=profile, app_key=app_key, file_path=file_path)
51
- except Exception as error: # noqa: BLE001
52
- return normalize_exception(error)
53
- return success_result(
54
- "IMPORT_VERIFIED",
55
- "已完成导入预检",
56
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
57
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
58
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
59
- legacy=raw,
60
- )
61
-
62
- def repair(
63
- self,
64
- *,
65
- profile: str,
66
- verification_id: str,
67
- authorized_file_modification: bool,
68
- output_path: str | None,
69
- selected_repairs: list[str],
70
- ) -> dict:
71
- try:
72
- raw = self._tools.imports.record_import_repair_local(
73
- profile=profile,
74
- verification_id=verification_id,
75
- authorized_file_modification=authorized_file_modification,
76
- output_path=output_path,
77
- selected_repairs=selected_repairs,
78
- )
79
- except Exception as error: # noqa: BLE001
80
- return normalize_exception(error)
81
- return success_result(
82
- "IMPORT_FILE_REPAIRED",
83
- "已完成导入文件修复",
84
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
85
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
86
- meta={"profile": profile},
87
- legacy=raw,
88
- )
89
-
90
- def start(
91
- self,
92
- *,
93
- profile: str,
94
- app_key: str,
95
- verification_id: str,
96
- being_enter_auditing: bool | None,
97
- view_key: str | None,
98
- ) -> dict:
99
- try:
100
- raw = self._tools.imports.record_import_start(
101
- profile=profile,
102
- app_key=app_key,
103
- verification_id=verification_id,
104
- being_enter_auditing=being_enter_auditing,
105
- view_key=view_key,
106
- )
107
- except Exception as error: # noqa: BLE001
108
- return normalize_exception(error)
109
- return success_result(
110
- "IMPORT_STARTED",
111
- "已启动导入任务",
112
- data=tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw,
113
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
114
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
115
- legacy=raw,
116
- )
117
-
118
- def status(self, *, profile: str, app_key: str, import_id: str | None, process_id_str: str | None) -> dict:
119
- try:
120
- raw = self._tools.imports.record_import_status_get(
121
- profile=profile,
122
- app_key=app_key,
123
- import_id=import_id,
124
- process_id_str=process_id_str,
125
- )
126
- except Exception as error: # noqa: BLE001
127
- return normalize_exception(error)
128
- data = tool_payload(raw) if isinstance(tool_payload(raw), dict) else raw
129
- if isinstance(data, dict) and raw.get("status") is not None:
130
- data = dict(data)
131
- data["status"] = raw.get("status")
132
- data["raw_status"] = raw.get("status")
133
- return success_result(
134
- "IMPORT_STATUS_READY",
135
- "已读取导入状态",
136
- data=data,
137
- warnings=raw.get("warnings") if isinstance(raw, dict) and isinstance(raw.get("warnings"), list) else [],
138
- meta={"profile": profile, "verification": raw.get("verification") if isinstance(raw, dict) else None},
139
- legacy=raw,
140
- )