@josephyan/qingflow-cli 0.2.0-beta.55
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/README.md +30 -0
- package/docs/local-agent-install.md +235 -0
- package/entry_point.py +13 -0
- package/npm/bin/qingflow.mjs +5 -0
- package/npm/lib/runtime.mjs +204 -0
- package/npm/scripts/postinstall.mjs +16 -0
- package/package.json +34 -0
- package/pyproject.toml +67 -0
- package/qingflow +15 -0
- package/src/qingflow_mcp/__init__.py +5 -0
- package/src/qingflow_mcp/__main__.py +5 -0
- package/src/qingflow_mcp/backend_client.py +547 -0
- package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
- package/src/qingflow_mcp/builder_facade/models.py +985 -0
- package/src/qingflow_mcp/builder_facade/service.py +8243 -0
- package/src/qingflow_mcp/cli/__init__.py +1 -0
- package/src/qingflow_mcp/cli/commands/__init__.py +15 -0
- package/src/qingflow_mcp/cli/commands/app.py +40 -0
- package/src/qingflow_mcp/cli/commands/auth.py +78 -0
- package/src/qingflow_mcp/cli/commands/builder.py +184 -0
- package/src/qingflow_mcp/cli/commands/common.py +47 -0
- package/src/qingflow_mcp/cli/commands/imports.py +86 -0
- package/src/qingflow_mcp/cli/commands/record.py +202 -0
- package/src/qingflow_mcp/cli/commands/task.py +87 -0
- package/src/qingflow_mcp/cli/commands/workspace.py +33 -0
- package/src/qingflow_mcp/cli/context.py +48 -0
- package/src/qingflow_mcp/cli/formatters.py +269 -0
- package/src/qingflow_mcp/cli/json_io.py +50 -0
- package/src/qingflow_mcp/cli/main.py +147 -0
- package/src/qingflow_mcp/config.py +221 -0
- package/src/qingflow_mcp/errors.py +66 -0
- package/src/qingflow_mcp/import_store.py +121 -0
- package/src/qingflow_mcp/json_types.py +18 -0
- package/src/qingflow_mcp/list_type_labels.py +76 -0
- package/src/qingflow_mcp/server.py +211 -0
- package/src/qingflow_mcp/server_app_builder.py +387 -0
- package/src/qingflow_mcp/server_app_user.py +317 -0
- package/src/qingflow_mcp/session_store.py +289 -0
- package/src/qingflow_mcp/solution/__init__.py +6 -0
- package/src/qingflow_mcp/solution/build_assembly_store.py +181 -0
- package/src/qingflow_mcp/solution/compiler/__init__.py +282 -0
- package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
- package/src/qingflow_mcp/solution/compiler/form_compiler.py +466 -0
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +113 -0
- package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
- package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
- package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
- package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
- package/src/qingflow_mcp/solution/design_session.py +222 -0
- package/src/qingflow_mcp/solution/design_store.py +100 -0
- package/src/qingflow_mcp/solution/executor.py +2339 -0
- package/src/qingflow_mcp/solution/normalizer.py +23 -0
- package/src/qingflow_mcp/solution/requirements_builder.py +536 -0
- package/src/qingflow_mcp/solution/run_store.py +244 -0
- package/src/qingflow_mcp/solution/spec_models.py +853 -0
- package/src/qingflow_mcp/tools/__init__.py +1 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2063 -0
- package/src/qingflow_mcp/tools/app_tools.py +850 -0
- package/src/qingflow_mcp/tools/approval_tools.py +833 -0
- package/src/qingflow_mcp/tools/auth_tools.py +697 -0
- package/src/qingflow_mcp/tools/base.py +81 -0
- package/src/qingflow_mcp/tools/code_block_tools.py +679 -0
- package/src/qingflow_mcp/tools/directory_tools.py +648 -0
- package/src/qingflow_mcp/tools/feedback_tools.py +230 -0
- package/src/qingflow_mcp/tools/file_tools.py +385 -0
- package/src/qingflow_mcp/tools/import_tools.py +1971 -0
- package/src/qingflow_mcp/tools/navigation_tools.py +177 -0
- package/src/qingflow_mcp/tools/package_tools.py +240 -0
- package/src/qingflow_mcp/tools/portal_tools.py +131 -0
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +269 -0
- package/src/qingflow_mcp/tools/record_tools.py +12739 -0
- package/src/qingflow_mcp/tools/role_tools.py +94 -0
- package/src/qingflow_mcp/tools/solution_tools.py +3887 -0
- package/src/qingflow_mcp/tools/task_context_tools.py +1423 -0
- package/src/qingflow_mcp/tools/task_tools.py +843 -0
- package/src/qingflow_mcp/tools/view_tools.py +280 -0
- package/src/qingflow_mcp/tools/workflow_tools.py +312 -0
- package/src/qingflow_mcp/tools/workspace_tools.py +219 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from ..backend_client import BackendRequestContext
|
|
8
|
+
from ..config import DEFAULT_PROFILE, normalize_base_url
|
|
9
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
10
|
+
from ..json_types import JSONObject, JSONValue
|
|
11
|
+
from .base import ToolBase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _qingbi_base_url(base_url: str) -> str:
|
|
15
|
+
normalized = normalize_base_url(base_url)
|
|
16
|
+
if not normalized:
|
|
17
|
+
raise QingflowApiError.config_error("base_url is required")
|
|
18
|
+
return normalized[:-4] if normalized.endswith("/api") else normalized
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QingbiReportTools(ToolBase):
|
|
22
|
+
def register(self, mcp: FastMCP) -> None:
|
|
23
|
+
@mcp.tool()
|
|
24
|
+
def qingbi_report_list(
|
|
25
|
+
profile: str = DEFAULT_PROFILE,
|
|
26
|
+
app_key: str = "",
|
|
27
|
+
) -> JSONObject:
|
|
28
|
+
return self.qingbi_report_list(profile=profile, app_key=app_key)
|
|
29
|
+
|
|
30
|
+
@mcp.tool()
|
|
31
|
+
def qingbi_report_get_base(
|
|
32
|
+
profile: str = DEFAULT_PROFILE,
|
|
33
|
+
chart_id: str = "",
|
|
34
|
+
) -> JSONObject:
|
|
35
|
+
return self.qingbi_report_get_base(profile=profile, chart_id=chart_id)
|
|
36
|
+
|
|
37
|
+
@mcp.tool()
|
|
38
|
+
def qingbi_report_get_config(
|
|
39
|
+
profile: str = DEFAULT_PROFILE,
|
|
40
|
+
chart_id: str = "",
|
|
41
|
+
) -> JSONObject:
|
|
42
|
+
return self.qingbi_report_get_config(profile=profile, chart_id=chart_id)
|
|
43
|
+
|
|
44
|
+
@mcp.tool()
|
|
45
|
+
def qingbi_report_get_data(
|
|
46
|
+
profile: str = DEFAULT_PROFILE,
|
|
47
|
+
chart_id: str = "",
|
|
48
|
+
payload: JSONObject | None = None,
|
|
49
|
+
page_num: int | None = None,
|
|
50
|
+
page_size: int | None = None,
|
|
51
|
+
page_num_y: int | None = None,
|
|
52
|
+
page_size_y: int | None = None,
|
|
53
|
+
) -> JSONObject:
|
|
54
|
+
return self.qingbi_report_get_data(
|
|
55
|
+
profile=profile,
|
|
56
|
+
chart_id=chart_id,
|
|
57
|
+
payload=payload or {},
|
|
58
|
+
page_num=page_num,
|
|
59
|
+
page_size=page_size,
|
|
60
|
+
page_num_y=page_num_y,
|
|
61
|
+
page_size_y=page_size_y,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="report chart"))
|
|
65
|
+
def qingbi_report_delete(
|
|
66
|
+
profile: str = DEFAULT_PROFILE,
|
|
67
|
+
chart_id: str = "",
|
|
68
|
+
) -> JSONObject:
|
|
69
|
+
return self.qingbi_report_delete(profile=profile, chart_id=chart_id)
|
|
70
|
+
|
|
71
|
+
@mcp.tool()
|
|
72
|
+
def qingbi_report_reorder(
|
|
73
|
+
profile: str = DEFAULT_PROFILE,
|
|
74
|
+
app_key: str = "",
|
|
75
|
+
chart_ids: list[str] | None = None,
|
|
76
|
+
) -> JSONObject:
|
|
77
|
+
return self.qingbi_report_reorder(profile=profile, app_key=app_key, chart_ids=chart_ids or [])
|
|
78
|
+
|
|
79
|
+
@mcp.tool()
|
|
80
|
+
def qingbi_report_list_fields(
|
|
81
|
+
profile: str = DEFAULT_PROFILE,
|
|
82
|
+
app_key: str = "",
|
|
83
|
+
) -> JSONObject:
|
|
84
|
+
return self.qingbi_report_list_fields(profile=profile, app_key=app_key)
|
|
85
|
+
|
|
86
|
+
@mcp.tool()
|
|
87
|
+
def qingbi_report_list_field_options(
|
|
88
|
+
profile: str = DEFAULT_PROFILE,
|
|
89
|
+
field_id: str = "",
|
|
90
|
+
chart_id: str = "",
|
|
91
|
+
) -> JSONObject:
|
|
92
|
+
return self.qingbi_report_list_field_options(profile=profile, field_id=field_id, chart_id=chart_id)
|
|
93
|
+
|
|
94
|
+
def qingbi_report_list(self, *, profile: str, app_key: str) -> JSONObject:
|
|
95
|
+
self._require_app_key(app_key)
|
|
96
|
+
return self._request(profile, "GET", f"/qingbi/charts/data/bichart/{app_key}", app_key=app_key, items_key="items")
|
|
97
|
+
|
|
98
|
+
def qingbi_report_list_sorted(
|
|
99
|
+
self,
|
|
100
|
+
*,
|
|
101
|
+
profile: str,
|
|
102
|
+
app_key: str,
|
|
103
|
+
page_num: int = 1,
|
|
104
|
+
page_size: int = 500,
|
|
105
|
+
search_key: str | None = None,
|
|
106
|
+
) -> JSONObject:
|
|
107
|
+
self._require_app_key(app_key)
|
|
108
|
+
body: JSONObject = {"appKey": app_key, "pageNum": page_num, "pageSize": page_size}
|
|
109
|
+
if search_key:
|
|
110
|
+
body["searchKey"] = search_key
|
|
111
|
+
return self._request(
|
|
112
|
+
profile,
|
|
113
|
+
"POST",
|
|
114
|
+
"/qingbi/charts/chart/list",
|
|
115
|
+
app_key=app_key,
|
|
116
|
+
page_num=page_num,
|
|
117
|
+
page_size=page_size,
|
|
118
|
+
search_key=search_key,
|
|
119
|
+
items_key="items",
|
|
120
|
+
result_transform=_extract_sorted_chart_items,
|
|
121
|
+
json_body=body,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def qingbi_report_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
125
|
+
body = self._require_dict(payload)
|
|
126
|
+
return self._request(profile, "POST", "/qingbi/charts", json_body=body)
|
|
127
|
+
|
|
128
|
+
def qingbi_report_get_base(self, *, profile: str, chart_id: str) -> JSONObject:
|
|
129
|
+
self._require_chart_id(chart_id)
|
|
130
|
+
return self._request(profile, "GET", f"/qingbi/charts/baseinfo/{chart_id}", chart_id=chart_id)
|
|
131
|
+
|
|
132
|
+
def qingbi_report_update_base(self, *, profile: str, chart_id: str, payload: JSONObject) -> JSONObject:
|
|
133
|
+
self._require_chart_id(chart_id)
|
|
134
|
+
body = self._require_dict(payload)
|
|
135
|
+
return self._request(profile, "PUT", f"/qingbi/charts/baseinfo/{chart_id}", chart_id=chart_id, json_body=body, risk_operation="update", risk_target="report base settings")
|
|
136
|
+
|
|
137
|
+
def qingbi_report_get_config(self, *, profile: str, chart_id: str) -> JSONObject:
|
|
138
|
+
self._require_chart_id(chart_id)
|
|
139
|
+
return self._request(profile, "GET", f"/qingbi/charts/{chart_id}/configs", chart_id=chart_id)
|
|
140
|
+
|
|
141
|
+
def qingbi_report_update_config(self, *, profile: str, chart_id: str, payload: JSONObject) -> JSONObject:
|
|
142
|
+
self._require_chart_id(chart_id)
|
|
143
|
+
body = self._require_dict(payload)
|
|
144
|
+
return self._request(profile, "PUT", f"/qingbi/charts/{chart_id}/configs", chart_id=chart_id, json_body=body, risk_operation="update", risk_target="report chart config")
|
|
145
|
+
|
|
146
|
+
def qingbi_report_get_data(
|
|
147
|
+
self,
|
|
148
|
+
*,
|
|
149
|
+
profile: str,
|
|
150
|
+
chart_id: str,
|
|
151
|
+
payload: JSONObject,
|
|
152
|
+
page_num: int | None = None,
|
|
153
|
+
page_size: int | None = None,
|
|
154
|
+
page_num_y: int | None = None,
|
|
155
|
+
page_size_y: int | None = None,
|
|
156
|
+
) -> JSONObject:
|
|
157
|
+
self._require_chart_id(chart_id)
|
|
158
|
+
params = {"qfUUID": uuid4().hex}
|
|
159
|
+
if page_num is not None:
|
|
160
|
+
params["pageNum"] = page_num
|
|
161
|
+
if page_size is not None:
|
|
162
|
+
params["pageSize"] = page_size
|
|
163
|
+
if page_num_y is not None:
|
|
164
|
+
params["pageNumY"] = page_num_y
|
|
165
|
+
if page_size_y is not None:
|
|
166
|
+
params["pageSizeY"] = page_size_y
|
|
167
|
+
return self._request(profile, "POST", f"/qingbi/charts/data/{chart_id}", chart_id=chart_id, params=params, json_body=payload)
|
|
168
|
+
|
|
169
|
+
def qingbi_report_delete(self, *, profile: str, chart_id: str) -> JSONObject:
|
|
170
|
+
self._require_chart_id(chart_id)
|
|
171
|
+
return self._request(profile, "DELETE", f"/qingbi/charts/{chart_id}", chart_id=chart_id, risk_operation="delete", risk_target="report chart")
|
|
172
|
+
|
|
173
|
+
def qingbi_report_reorder(self, *, profile: str, app_key: str, chart_ids: list[str]) -> JSONObject:
|
|
174
|
+
self._require_app_key(app_key)
|
|
175
|
+
if not isinstance(chart_ids, list) or not chart_ids:
|
|
176
|
+
raise_tool_error(QingflowApiError.config_error("chart_ids must be a non-empty array"))
|
|
177
|
+
if any(not chart_id for chart_id in chart_ids):
|
|
178
|
+
raise_tool_error(QingflowApiError.config_error("chart_ids cannot contain empty values"))
|
|
179
|
+
return self._request(
|
|
180
|
+
profile,
|
|
181
|
+
"POST",
|
|
182
|
+
"/qingbi/charts/sort/chart",
|
|
183
|
+
app_key=app_key,
|
|
184
|
+
chart_ids=chart_ids,
|
|
185
|
+
json_body={"appKey": app_key, "sortChartIdList": chart_ids},
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def qingbi_report_list_fields(self, *, profile: str, app_key: str) -> JSONObject:
|
|
189
|
+
self._require_app_key(app_key)
|
|
190
|
+
return self._request(
|
|
191
|
+
profile,
|
|
192
|
+
"GET",
|
|
193
|
+
"/qingbi/datasets/datasource/fields",
|
|
194
|
+
app_key=app_key,
|
|
195
|
+
params={"dataSourceId": app_key, "dataSourceType": "qingflow"},
|
|
196
|
+
items_key="items",
|
|
197
|
+
result_transform=_extract_dataset_fields,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def qingbi_report_list_field_options(self, *, profile: str, field_id: str, chart_id: str) -> JSONObject:
|
|
201
|
+
if not field_id:
|
|
202
|
+
raise_tool_error(QingflowApiError.config_error("field_id is required"))
|
|
203
|
+
path = f"/qingbi/charts/datasource/{chart_id}/question/option" if chart_id else "/qingbi/charts/datasource/question/option"
|
|
204
|
+
params = {"fieldId": field_id}
|
|
205
|
+
if chart_id:
|
|
206
|
+
self._require_chart_id(chart_id)
|
|
207
|
+
return self._request(profile, "GET", path, chart_id=chart_id or None, field_id=field_id, params=params, items_key="items")
|
|
208
|
+
|
|
209
|
+
def _request(
|
|
210
|
+
self,
|
|
211
|
+
profile: str,
|
|
212
|
+
method: str,
|
|
213
|
+
path: str,
|
|
214
|
+
*,
|
|
215
|
+
json_body: JSONValue = None,
|
|
216
|
+
params: JSONObject | None = None,
|
|
217
|
+
items_key: str | None = None,
|
|
218
|
+
result_transform: object | None = None,
|
|
219
|
+
risk_operation: str | None = None,
|
|
220
|
+
risk_target: str | None = None,
|
|
221
|
+
**extra: JSONValue,
|
|
222
|
+
) -> JSONObject:
|
|
223
|
+
def runner(session_profile, context):
|
|
224
|
+
qingbi_context = BackendRequestContext(
|
|
225
|
+
base_url=_qingbi_base_url(context.base_url),
|
|
226
|
+
token=context.token,
|
|
227
|
+
ws_id=context.ws_id,
|
|
228
|
+
qf_request_id=context.qf_request_id,
|
|
229
|
+
qf_version=context.qf_version,
|
|
230
|
+
qf_version_source=context.qf_version_source,
|
|
231
|
+
)
|
|
232
|
+
result = self.backend.request(method, qingbi_context, path, json_body=json_body, params=params)
|
|
233
|
+
if callable(result_transform):
|
|
234
|
+
result = result_transform(result)
|
|
235
|
+
payload: JSONObject = {"profile": profile, "ws_id": session_profile.selected_ws_id}
|
|
236
|
+
if items_key:
|
|
237
|
+
payload[items_key] = result
|
|
238
|
+
else:
|
|
239
|
+
payload["result"] = result
|
|
240
|
+
payload.update(extra)
|
|
241
|
+
if risk_operation and risk_target:
|
|
242
|
+
return self._attach_human_review_notice(payload, operation=risk_operation, target=risk_target)
|
|
243
|
+
return payload
|
|
244
|
+
|
|
245
|
+
return self._run(profile, runner)
|
|
246
|
+
|
|
247
|
+
def _require_app_key(self, app_key: str) -> None:
|
|
248
|
+
if not app_key:
|
|
249
|
+
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
250
|
+
|
|
251
|
+
def _require_chart_id(self, chart_id: str) -> None:
|
|
252
|
+
if not chart_id:
|
|
253
|
+
raise_tool_error(QingflowApiError.config_error("chart_id is required"))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _extract_dataset_fields(result: JSONValue) -> list[JSONObject]:
|
|
257
|
+
if isinstance(result, dict) and isinstance(result.get("fields"), list):
|
|
258
|
+
return result["fields"]
|
|
259
|
+
if isinstance(result, list):
|
|
260
|
+
return result
|
|
261
|
+
return []
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _extract_sorted_chart_items(result: JSONValue) -> list[JSONObject]:
|
|
265
|
+
if isinstance(result, dict) and isinstance(result.get("list"), list):
|
|
266
|
+
return result["list"]
|
|
267
|
+
if isinstance(result, list):
|
|
268
|
+
return result
|
|
269
|
+
return []
|