@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.
Files changed (79) hide show
  1. package/README.md +30 -0
  2. package/docs/local-agent-install.md +235 -0
  3. package/entry_point.py +13 -0
  4. package/npm/bin/qingflow.mjs +5 -0
  5. package/npm/lib/runtime.mjs +204 -0
  6. package/npm/scripts/postinstall.mjs +16 -0
  7. package/package.json +34 -0
  8. package/pyproject.toml +67 -0
  9. package/qingflow +15 -0
  10. package/src/qingflow_mcp/__init__.py +5 -0
  11. package/src/qingflow_mcp/__main__.py +5 -0
  12. package/src/qingflow_mcp/backend_client.py +547 -0
  13. package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
  14. package/src/qingflow_mcp/builder_facade/models.py +985 -0
  15. package/src/qingflow_mcp/builder_facade/service.py +8243 -0
  16. package/src/qingflow_mcp/cli/__init__.py +1 -0
  17. package/src/qingflow_mcp/cli/commands/__init__.py +15 -0
  18. package/src/qingflow_mcp/cli/commands/app.py +40 -0
  19. package/src/qingflow_mcp/cli/commands/auth.py +78 -0
  20. package/src/qingflow_mcp/cli/commands/builder.py +184 -0
  21. package/src/qingflow_mcp/cli/commands/common.py +47 -0
  22. package/src/qingflow_mcp/cli/commands/imports.py +86 -0
  23. package/src/qingflow_mcp/cli/commands/record.py +202 -0
  24. package/src/qingflow_mcp/cli/commands/task.py +87 -0
  25. package/src/qingflow_mcp/cli/commands/workspace.py +33 -0
  26. package/src/qingflow_mcp/cli/context.py +48 -0
  27. package/src/qingflow_mcp/cli/formatters.py +269 -0
  28. package/src/qingflow_mcp/cli/json_io.py +50 -0
  29. package/src/qingflow_mcp/cli/main.py +147 -0
  30. package/src/qingflow_mcp/config.py +221 -0
  31. package/src/qingflow_mcp/errors.py +66 -0
  32. package/src/qingflow_mcp/import_store.py +121 -0
  33. package/src/qingflow_mcp/json_types.py +18 -0
  34. package/src/qingflow_mcp/list_type_labels.py +76 -0
  35. package/src/qingflow_mcp/server.py +211 -0
  36. package/src/qingflow_mcp/server_app_builder.py +387 -0
  37. package/src/qingflow_mcp/server_app_user.py +317 -0
  38. package/src/qingflow_mcp/session_store.py +289 -0
  39. package/src/qingflow_mcp/solution/__init__.py +6 -0
  40. package/src/qingflow_mcp/solution/build_assembly_store.py +181 -0
  41. package/src/qingflow_mcp/solution/compiler/__init__.py +282 -0
  42. package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
  43. package/src/qingflow_mcp/solution/compiler/form_compiler.py +466 -0
  44. package/src/qingflow_mcp/solution/compiler/icon_utils.py +113 -0
  45. package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
  46. package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
  47. package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
  48. package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
  49. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
  50. package/src/qingflow_mcp/solution/design_session.py +222 -0
  51. package/src/qingflow_mcp/solution/design_store.py +100 -0
  52. package/src/qingflow_mcp/solution/executor.py +2339 -0
  53. package/src/qingflow_mcp/solution/normalizer.py +23 -0
  54. package/src/qingflow_mcp/solution/requirements_builder.py +536 -0
  55. package/src/qingflow_mcp/solution/run_store.py +244 -0
  56. package/src/qingflow_mcp/solution/spec_models.py +853 -0
  57. package/src/qingflow_mcp/tools/__init__.py +1 -0
  58. package/src/qingflow_mcp/tools/ai_builder_tools.py +2063 -0
  59. package/src/qingflow_mcp/tools/app_tools.py +850 -0
  60. package/src/qingflow_mcp/tools/approval_tools.py +833 -0
  61. package/src/qingflow_mcp/tools/auth_tools.py +697 -0
  62. package/src/qingflow_mcp/tools/base.py +81 -0
  63. package/src/qingflow_mcp/tools/code_block_tools.py +679 -0
  64. package/src/qingflow_mcp/tools/directory_tools.py +648 -0
  65. package/src/qingflow_mcp/tools/feedback_tools.py +230 -0
  66. package/src/qingflow_mcp/tools/file_tools.py +385 -0
  67. package/src/qingflow_mcp/tools/import_tools.py +1971 -0
  68. package/src/qingflow_mcp/tools/navigation_tools.py +177 -0
  69. package/src/qingflow_mcp/tools/package_tools.py +240 -0
  70. package/src/qingflow_mcp/tools/portal_tools.py +131 -0
  71. package/src/qingflow_mcp/tools/qingbi_report_tools.py +269 -0
  72. package/src/qingflow_mcp/tools/record_tools.py +12739 -0
  73. package/src/qingflow_mcp/tools/role_tools.py +94 -0
  74. package/src/qingflow_mcp/tools/solution_tools.py +3887 -0
  75. package/src/qingflow_mcp/tools/task_context_tools.py +1423 -0
  76. package/src/qingflow_mcp/tools/task_tools.py +843 -0
  77. package/src/qingflow_mcp/tools/view_tools.py +280 -0
  78. package/src/qingflow_mcp/tools/workflow_tools.py +312 -0
  79. 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 []