@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,280 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from mcp.server.fastmcp import FastMCP
|
|
4
|
+
|
|
5
|
+
from ..config import DEFAULT_PROFILE
|
|
6
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
7
|
+
from ..json_types import JSONObject, JSONValue
|
|
8
|
+
from .base import ToolBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ViewTools(ToolBase):
|
|
12
|
+
def register(self, mcp: FastMCP) -> None:
|
|
13
|
+
@mcp.tool()
|
|
14
|
+
def view_list(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
15
|
+
return self.view_list(profile=profile, app_key=app_key)
|
|
16
|
+
|
|
17
|
+
@mcp.tool()
|
|
18
|
+
def view_list_flat(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
19
|
+
return self.view_list_flat(profile=profile, app_key=app_key)
|
|
20
|
+
|
|
21
|
+
@mcp.tool()
|
|
22
|
+
def view_get_config(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "") -> JSONObject:
|
|
23
|
+
return self.view_get_config(profile=profile, viewgraph_key=viewgraph_key)
|
|
24
|
+
|
|
25
|
+
@mcp.tool()
|
|
26
|
+
def view_get_base_info(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", passcode: str | None = None) -> JSONObject:
|
|
27
|
+
return self.view_get_base_info(profile=profile, viewgraph_key=viewgraph_key, passcode=passcode)
|
|
28
|
+
|
|
29
|
+
@mcp.tool()
|
|
30
|
+
def view_list_questions(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "") -> JSONObject:
|
|
31
|
+
return self.view_list_questions(profile=profile, viewgraph_key=viewgraph_key)
|
|
32
|
+
|
|
33
|
+
@mcp.tool()
|
|
34
|
+
def view_list_associations(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "") -> JSONObject:
|
|
35
|
+
return self.view_list_associations(profile=profile, viewgraph_key=viewgraph_key)
|
|
36
|
+
|
|
37
|
+
@mcp.tool()
|
|
38
|
+
def view_board_get_lane_base_info(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", page_num: int = 1, page_size: int = 20) -> JSONObject:
|
|
39
|
+
return self.view_board_get_lane_base_info(profile=profile, viewgraph_key=viewgraph_key, page_num=page_num, page_size=page_size)
|
|
40
|
+
|
|
41
|
+
@mcp.tool()
|
|
42
|
+
def view_get_future_nodes(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", apply_id: int = 0, app_key: str = "") -> JSONObject:
|
|
43
|
+
return self.view_get_future_nodes(profile=profile, viewgraph_key=viewgraph_key, apply_id=apply_id, app_key=app_key)
|
|
44
|
+
|
|
45
|
+
@mcp.tool()
|
|
46
|
+
def view_get_workflow_status(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", row_record_id: int = 0) -> JSONObject:
|
|
47
|
+
return self.view_get_workflow_status(profile=profile, viewgraph_key=viewgraph_key, row_record_id=row_record_id)
|
|
48
|
+
|
|
49
|
+
def view_list(self, *, profile: str, app_key: str) -> JSONObject:
|
|
50
|
+
self._require_app_key(app_key)
|
|
51
|
+
return self._request(profile, "GET", f"/app/{app_key}/view/viewList", app_key=app_key)
|
|
52
|
+
|
|
53
|
+
def view_list_flat(self, *, profile: str, app_key: str) -> JSONObject:
|
|
54
|
+
self._require_app_key(app_key)
|
|
55
|
+
return self._request(profile, "GET", f"/app/{app_key}/view/views", app_key=app_key)
|
|
56
|
+
|
|
57
|
+
def view_reorder(self, *, profile: str, app_key: str, payload: list[JSONObject]) -> JSONObject:
|
|
58
|
+
self._require_app_key(app_key)
|
|
59
|
+
if not isinstance(payload, list) or not payload:
|
|
60
|
+
raise_tool_error(QingflowApiError.config_error("payload must be a non-empty array"))
|
|
61
|
+
return self._request(
|
|
62
|
+
profile,
|
|
63
|
+
"POST",
|
|
64
|
+
f"/app/{app_key}/view/ordinal",
|
|
65
|
+
app_key=app_key,
|
|
66
|
+
json_body=self._normalize_reorder_payload(payload),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def view_set_column_width(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
70
|
+
self._require_app_key(app_key)
|
|
71
|
+
body = self._require_dict(payload)
|
|
72
|
+
return self._request(profile, "POST", f"/app/{app_key}/view/que/width", app_key=app_key, json_body=body)
|
|
73
|
+
|
|
74
|
+
def view_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
75
|
+
body = self._require_dict(payload)
|
|
76
|
+
app_key = str(body.get("appKey") or "")
|
|
77
|
+
return self._request(profile, "POST", "/view/viewConfig", app_key=app_key, json_body=body)
|
|
78
|
+
|
|
79
|
+
def view_get_config(self, *, profile: str, viewgraph_key: str) -> JSONObject:
|
|
80
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
81
|
+
return self._request(profile, "GET", f"/view/{viewgraph_key}/viewConfig", viewgraph_key=viewgraph_key)
|
|
82
|
+
|
|
83
|
+
def view_get_base_info(self, *, profile: str, viewgraph_key: str, passcode: str | None) -> JSONObject:
|
|
84
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
85
|
+
params = {"pass": passcode} if passcode else None
|
|
86
|
+
return self._request(profile, "GET", f"/view/{viewgraph_key}/viewConfig/baseInfo", viewgraph_key=viewgraph_key, params=params)
|
|
87
|
+
|
|
88
|
+
def view_update(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
|
|
89
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
90
|
+
body = self._require_dict(payload)
|
|
91
|
+
return self._request(
|
|
92
|
+
profile,
|
|
93
|
+
"POST",
|
|
94
|
+
f"/view/{viewgraph_key}/viewConfig",
|
|
95
|
+
viewgraph_key=viewgraph_key,
|
|
96
|
+
json_body=body,
|
|
97
|
+
risk_operation="update",
|
|
98
|
+
risk_target="view configuration",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def view_delete(self, *, profile: str, viewgraph_key: str) -> JSONObject:
|
|
102
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
103
|
+
return self._request(
|
|
104
|
+
profile,
|
|
105
|
+
"DELETE",
|
|
106
|
+
f"/view/{viewgraph_key}",
|
|
107
|
+
viewgraph_key=viewgraph_key,
|
|
108
|
+
risk_operation="delete",
|
|
109
|
+
risk_target="view configuration",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def view_copy(self, *, profile: str, viewgraph_key: str) -> JSONObject:
|
|
113
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
114
|
+
return self._request(profile, "POST", f"/view/{viewgraph_key}/copy", viewgraph_key=viewgraph_key)
|
|
115
|
+
|
|
116
|
+
def view_list_questions(self, *, profile: str, viewgraph_key: str) -> JSONObject:
|
|
117
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
118
|
+
return self._request(profile, "GET", f"/view/{viewgraph_key}/question", viewgraph_key=viewgraph_key)
|
|
119
|
+
|
|
120
|
+
def view_list_associations(self, *, profile: str, viewgraph_key: str) -> JSONObject:
|
|
121
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
122
|
+
return self._request(profile, "GET", f"/view/{viewgraph_key}/association", viewgraph_key=viewgraph_key)
|
|
123
|
+
|
|
124
|
+
def view_update_member_config(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
|
|
125
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
126
|
+
body = self._require_dict(payload)
|
|
127
|
+
return self._request(
|
|
128
|
+
profile,
|
|
129
|
+
"POST",
|
|
130
|
+
f"/view/{viewgraph_key}/member/config",
|
|
131
|
+
viewgraph_key=viewgraph_key,
|
|
132
|
+
json_body=body,
|
|
133
|
+
risk_operation="update",
|
|
134
|
+
risk_target="view member visibility",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def view_update_apply_config(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
|
|
138
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
139
|
+
body = self._require_dict(payload)
|
|
140
|
+
return self._request(
|
|
141
|
+
profile,
|
|
142
|
+
"POST",
|
|
143
|
+
f"/view/{viewgraph_key}/apply/config",
|
|
144
|
+
viewgraph_key=viewgraph_key,
|
|
145
|
+
json_body=body,
|
|
146
|
+
risk_operation="update",
|
|
147
|
+
risk_target="view apply visibility",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def view_board_set_lane_config(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
|
|
151
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
152
|
+
body = self._require_dict(payload)
|
|
153
|
+
return self._request(
|
|
154
|
+
profile,
|
|
155
|
+
"POST",
|
|
156
|
+
f"/view/{viewgraph_key}/lane/config",
|
|
157
|
+
viewgraph_key=viewgraph_key,
|
|
158
|
+
json_body=body,
|
|
159
|
+
risk_operation="update",
|
|
160
|
+
risk_target="board lane configuration",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def view_board_get_lane_base_info(self, *, profile: str, viewgraph_key: str, page_num: int, page_size: int) -> JSONObject:
|
|
164
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
165
|
+
return self._request(
|
|
166
|
+
profile,
|
|
167
|
+
"GET",
|
|
168
|
+
f"/view/{viewgraph_key}/lane/baseInfo",
|
|
169
|
+
viewgraph_key=viewgraph_key,
|
|
170
|
+
params={"pageNum": page_num, "pageSize": page_size},
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def view_gantt_switch_auto_calibration(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
|
|
174
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
175
|
+
body = self._require_dict(payload)
|
|
176
|
+
return self._request(
|
|
177
|
+
profile,
|
|
178
|
+
"PUT",
|
|
179
|
+
f"/view/gantt/{viewgraph_key}/auto/calibration",
|
|
180
|
+
viewgraph_key=viewgraph_key,
|
|
181
|
+
json_body=body,
|
|
182
|
+
risk_operation="update",
|
|
183
|
+
risk_target="gantt auto calibration settings",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def view_gantt_batch_update_time(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
|
|
187
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
188
|
+
body = self._require_dict(payload)
|
|
189
|
+
return self._request(
|
|
190
|
+
profile,
|
|
191
|
+
"PUT",
|
|
192
|
+
f"/view/gantt/{viewgraph_key}/batch/update",
|
|
193
|
+
viewgraph_key=viewgraph_key,
|
|
194
|
+
json_body=body,
|
|
195
|
+
risk_operation="update",
|
|
196
|
+
risk_target="gantt task timing",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
def view_get_future_nodes(self, *, profile: str, viewgraph_key: str, apply_id: int, app_key: str) -> JSONObject:
|
|
200
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
201
|
+
self._require_app_key(app_key)
|
|
202
|
+
self._require_positive("apply_id", apply_id)
|
|
203
|
+
return self._request(
|
|
204
|
+
profile,
|
|
205
|
+
"GET",
|
|
206
|
+
f"/view/{viewgraph_key}/auditNode/futureList/{apply_id}",
|
|
207
|
+
viewgraph_key=viewgraph_key,
|
|
208
|
+
app_key=app_key,
|
|
209
|
+
apply_id=apply_id,
|
|
210
|
+
params={"appKey": app_key},
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def view_get_workflow_status(self, *, profile: str, viewgraph_key: str, row_record_id: int) -> JSONObject:
|
|
214
|
+
self._require_viewgraph_key(viewgraph_key)
|
|
215
|
+
self._require_positive("row_record_id", row_record_id)
|
|
216
|
+
return self._request(profile, "GET", f"/view/{viewgraph_key}/workflow/status/{row_record_id}", viewgraph_key=viewgraph_key, row_record_id=row_record_id)
|
|
217
|
+
|
|
218
|
+
def _request(
|
|
219
|
+
self,
|
|
220
|
+
profile: str,
|
|
221
|
+
method: str,
|
|
222
|
+
path: str,
|
|
223
|
+
*,
|
|
224
|
+
json_body: JSONValue = None,
|
|
225
|
+
params: JSONObject | None = None,
|
|
226
|
+
risk_operation: str | None = None,
|
|
227
|
+
risk_target: str | None = None,
|
|
228
|
+
**extra: JSONValue,
|
|
229
|
+
) -> JSONObject:
|
|
230
|
+
def runner(session_profile, context):
|
|
231
|
+
result = self.backend.request(method, context, path, json_body=json_body, params=params)
|
|
232
|
+
response: JSONObject = {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
233
|
+
response.update(extra)
|
|
234
|
+
if risk_operation and risk_target:
|
|
235
|
+
return self._attach_human_review_notice(response, operation=risk_operation, target=risk_target)
|
|
236
|
+
return response
|
|
237
|
+
|
|
238
|
+
return self._run(profile, runner)
|
|
239
|
+
|
|
240
|
+
def _require_app_key(self, app_key: str) -> None:
|
|
241
|
+
if not app_key:
|
|
242
|
+
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
243
|
+
|
|
244
|
+
def _require_viewgraph_key(self, viewgraph_key: str) -> None:
|
|
245
|
+
if not viewgraph_key:
|
|
246
|
+
raise_tool_error(QingflowApiError.config_error("viewgraph_key is required"))
|
|
247
|
+
|
|
248
|
+
def _require_positive(self, field_name: str, value: int) -> None:
|
|
249
|
+
if value <= 0:
|
|
250
|
+
raise_tool_error(QingflowApiError.config_error(f"{field_name} must be positive"))
|
|
251
|
+
|
|
252
|
+
def _normalize_reorder_payload(self, payload: list[JSONObject]) -> list[JSONObject]:
|
|
253
|
+
first_item = payload[0]
|
|
254
|
+
if "ordinalType" in first_item and "viewKeyList" in first_item:
|
|
255
|
+
return payload
|
|
256
|
+
if "ordinalType" in first_item and "viewList" in first_item:
|
|
257
|
+
normalized_groups: list[JSONObject] = []
|
|
258
|
+
for group in payload:
|
|
259
|
+
view_list = group.get("viewList")
|
|
260
|
+
view_keys = [
|
|
261
|
+
str(view.get("viewKey"))
|
|
262
|
+
for view in view_list
|
|
263
|
+
if isinstance(view_list, list) and isinstance(view, dict) and view.get("viewKey")
|
|
264
|
+
]
|
|
265
|
+
if group.get("ordinalType") and view_keys:
|
|
266
|
+
normalized_groups.append({"ordinalType": group["ordinalType"], "viewKeyList": view_keys})
|
|
267
|
+
if normalized_groups:
|
|
268
|
+
return normalized_groups
|
|
269
|
+
sorted_items = sorted(
|
|
270
|
+
payload,
|
|
271
|
+
key=lambda item: int(item.get("ordinal", payload.index(item))) if isinstance(item.get("ordinal"), int) else payload.index(item),
|
|
272
|
+
)
|
|
273
|
+
view_keys = [
|
|
274
|
+
str(item.get("viewgraphKey") or item.get("viewKey"))
|
|
275
|
+
for item in sorted_items
|
|
276
|
+
if item.get("viewgraphKey") or item.get("viewKey")
|
|
277
|
+
]
|
|
278
|
+
if not view_keys:
|
|
279
|
+
raise_tool_error(QingflowApiError.config_error("payload must include viewgraphKey/viewKey or ordinalType/viewKeyList"))
|
|
280
|
+
return [{"ordinalType": "FIXED_VIEW_LIST", "viewKeyList": view_keys}]
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from mcp.server.fastmcp import FastMCP
|
|
4
|
+
|
|
5
|
+
from ..backend_client import BackendRequestContext
|
|
6
|
+
from ..config import DEFAULT_PROFILE
|
|
7
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
8
|
+
from ..json_types import JSONObject, JSONValue
|
|
9
|
+
from .base import ToolBase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WorkflowTools(ToolBase):
|
|
13
|
+
def register(self, mcp: FastMCP) -> None:
|
|
14
|
+
@mcp.tool()
|
|
15
|
+
def workflow_list_nodes(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
16
|
+
return self.workflow_list_nodes(profile=profile, app_key=app_key)
|
|
17
|
+
|
|
18
|
+
@mcp.tool()
|
|
19
|
+
def workflow_get_node_detail(profile: str = DEFAULT_PROFILE, app_key: str = "", audit_node_id: int = 0) -> JSONObject:
|
|
20
|
+
return self.workflow_get_node_detail(profile=profile, app_key=app_key, audit_node_id=audit_node_id)
|
|
21
|
+
|
|
22
|
+
@mcp.tool()
|
|
23
|
+
def workflow_get_global_settings(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
24
|
+
return self.workflow_get_global_settings(profile=profile, app_key=app_key)
|
|
25
|
+
|
|
26
|
+
@mcp.tool()
|
|
27
|
+
def workflow_get_future_nodes(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0) -> JSONObject:
|
|
28
|
+
return self.workflow_get_future_nodes(profile=profile, app_key=app_key, apply_id=apply_id)
|
|
29
|
+
|
|
30
|
+
@mcp.tool()
|
|
31
|
+
def workflow_get_future_nodes_app(
|
|
32
|
+
profile: str = DEFAULT_PROFILE,
|
|
33
|
+
app_key: str = "",
|
|
34
|
+
apply_id: int = 0,
|
|
35
|
+
role: int = 1,
|
|
36
|
+
audit_node_id: int | None = None,
|
|
37
|
+
) -> JSONObject:
|
|
38
|
+
return self.workflow_get_future_nodes_app(
|
|
39
|
+
profile=profile,
|
|
40
|
+
app_key=app_key,
|
|
41
|
+
apply_id=apply_id,
|
|
42
|
+
role=role,
|
|
43
|
+
audit_node_id=audit_node_id,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@mcp.tool()
|
|
47
|
+
def workflow_get_qsource_active(profile: str = DEFAULT_PROFILE, app_key: str = "", qsource_id: int = 0) -> JSONObject:
|
|
48
|
+
return self.workflow_get_qsource_active(profile=profile, app_key=app_key, qsource_id=qsource_id)
|
|
49
|
+
|
|
50
|
+
@mcp.tool()
|
|
51
|
+
def workflow_get_qsource_passive(profile: str = DEFAULT_PROFILE, app_key: str = "", qsource_id: int = 0) -> JSONObject:
|
|
52
|
+
return self.workflow_get_qsource_passive(profile=profile, app_key=app_key, qsource_id=qsource_id)
|
|
53
|
+
|
|
54
|
+
@mcp.tool()
|
|
55
|
+
def workflow_get_editable_question_ids(profile: str = DEFAULT_PROFILE, app_key: str = "", audit_node_id: int = 0) -> JSONObject:
|
|
56
|
+
return self.workflow_get_editable_question_ids(profile=profile, app_key=app_key, audit_node_id=audit_node_id)
|
|
57
|
+
|
|
58
|
+
@mcp.tool()
|
|
59
|
+
def workflow_get_print_nodes(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
60
|
+
return self.workflow_get_print_nodes(profile=profile, app_key=app_key)
|
|
61
|
+
|
|
62
|
+
def workflow_list_nodes(self, *, profile: str, app_key: str) -> JSONObject:
|
|
63
|
+
self._require_app_key(app_key)
|
|
64
|
+
return self._request(profile, "GET", f"/app/{app_key}/auditNodes", app_key=app_key)
|
|
65
|
+
|
|
66
|
+
def workflow_get_node_detail(self, *, profile: str, app_key: str, audit_node_id: int) -> JSONObject:
|
|
67
|
+
self._require_app_key(app_key)
|
|
68
|
+
self._require_positive("audit_node_id", audit_node_id)
|
|
69
|
+
return self._request(profile, "GET", f"/app/{app_key}/auditNodes/{audit_node_id}", app_key=app_key, audit_node_id=audit_node_id)
|
|
70
|
+
|
|
71
|
+
def workflow_add_node(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
72
|
+
self._require_app_key(app_key)
|
|
73
|
+
body = self._require_dict(payload)
|
|
74
|
+
return self._request_with_post_fallbacks(
|
|
75
|
+
profile=profile,
|
|
76
|
+
app_key=app_key,
|
|
77
|
+
path=f"/app/{app_key}/auditNodes",
|
|
78
|
+
json_body=body,
|
|
79
|
+
alternate_paths=[f"/app/{app_key}/auditNode"],
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def workflow_update_node(self, *, profile: str, app_key: str, audit_node_id: int, payload: JSONObject) -> JSONObject:
|
|
83
|
+
self._require_app_key(app_key)
|
|
84
|
+
self._require_positive("audit_node_id", audit_node_id)
|
|
85
|
+
body = self._require_dict(payload)
|
|
86
|
+
return self._request_with_post_fallbacks(
|
|
87
|
+
profile=profile,
|
|
88
|
+
app_key=app_key,
|
|
89
|
+
path=f"/app/{app_key}/auditNodes/{audit_node_id}",
|
|
90
|
+
json_body=body,
|
|
91
|
+
alternate_paths=[f"/app/{app_key}/auditNode/{audit_node_id}"],
|
|
92
|
+
risk_operation="update",
|
|
93
|
+
risk_target="workflow node configuration",
|
|
94
|
+
audit_node_id=audit_node_id,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def workflow_delete_node(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
98
|
+
self._require_app_key(app_key)
|
|
99
|
+
body = self._require_dict(payload)
|
|
100
|
+
return self._request(profile, "DELETE", f"/app/{app_key}/auditNode", app_key=app_key, json_body=body, risk_operation="delete", risk_target="workflow node configuration")
|
|
101
|
+
|
|
102
|
+
def workflow_copy_paste_node(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
103
|
+
self._require_app_key(app_key)
|
|
104
|
+
body = self._require_dict(payload)
|
|
105
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/copyAndPaste", app_key=app_key, json_body=body)
|
|
106
|
+
|
|
107
|
+
def workflow_cut_paste_node(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
108
|
+
self._require_app_key(app_key)
|
|
109
|
+
body = self._require_dict(payload)
|
|
110
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/cutAndPaste", app_key=app_key, json_body=body)
|
|
111
|
+
|
|
112
|
+
def workflow_create_sub_branch(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
113
|
+
self._require_app_key(app_key)
|
|
114
|
+
body = self._require_dict(payload)
|
|
115
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/subBranch", app_key=app_key, json_body=body)
|
|
116
|
+
|
|
117
|
+
def workflow_delete_sub_branch(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
118
|
+
self._require_app_key(app_key)
|
|
119
|
+
body = self._require_dict(payload)
|
|
120
|
+
return self._request(profile, "DELETE", f"/app/{app_key}/auditNode/subBranch", app_key=app_key, json_body=body, risk_operation="delete", risk_target="workflow branch configuration")
|
|
121
|
+
|
|
122
|
+
def workflow_get_global_settings(self, *, profile: str, app_key: str) -> JSONObject:
|
|
123
|
+
self._require_app_key(app_key)
|
|
124
|
+
return self._request(profile, "GET", f"/app/{app_key}/workflow/global/setting", app_key=app_key)
|
|
125
|
+
|
|
126
|
+
def workflow_update_global_settings(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
127
|
+
self._require_app_key(app_key)
|
|
128
|
+
body = self._require_dict(payload)
|
|
129
|
+
return self._request_with_post_fallbacks(
|
|
130
|
+
profile=profile,
|
|
131
|
+
app_key=app_key,
|
|
132
|
+
path=f"/app/{app_key}/workflow/global/setting",
|
|
133
|
+
json_body=body,
|
|
134
|
+
alternate_paths=[],
|
|
135
|
+
risk_operation="update",
|
|
136
|
+
risk_target="workflow global settings",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def workflow_publish(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
140
|
+
self._require_app_key(app_key)
|
|
141
|
+
body = self._require_dict(payload)
|
|
142
|
+
return self._request_with_post_fallbacks(
|
|
143
|
+
profile=profile,
|
|
144
|
+
app_key=app_key,
|
|
145
|
+
path=f"/app/{app_key}/publish",
|
|
146
|
+
json_body=body,
|
|
147
|
+
alternate_paths=[],
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def workflow_get_future_nodes(self, *, profile: str, app_key: str, apply_id: int) -> JSONObject:
|
|
151
|
+
self._require_app_key(app_key)
|
|
152
|
+
self._require_positive("apply_id", apply_id)
|
|
153
|
+
return self._request(profile, "GET", f"/app/{app_key}/auditNode/futureList/{apply_id}", app_key=app_key, apply_id=apply_id)
|
|
154
|
+
|
|
155
|
+
def workflow_get_future_nodes_app(
|
|
156
|
+
self,
|
|
157
|
+
*,
|
|
158
|
+
profile: str,
|
|
159
|
+
app_key: str,
|
|
160
|
+
apply_id: int,
|
|
161
|
+
role: int,
|
|
162
|
+
audit_node_id: int | None,
|
|
163
|
+
) -> JSONObject:
|
|
164
|
+
self._require_app_key(app_key)
|
|
165
|
+
self._require_positive("apply_id", apply_id)
|
|
166
|
+
params: JSONObject = {"role": role}
|
|
167
|
+
if audit_node_id is not None:
|
|
168
|
+
self._require_positive("audit_node_id", audit_node_id)
|
|
169
|
+
params["auditNodeId"] = audit_node_id
|
|
170
|
+
return self._request(
|
|
171
|
+
profile,
|
|
172
|
+
"GET",
|
|
173
|
+
f"/app/{app_key}/auditNode/appFutureListV2/{apply_id}",
|
|
174
|
+
app_key=app_key,
|
|
175
|
+
apply_id=apply_id,
|
|
176
|
+
params=params,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def workflow_webhook_test(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
180
|
+
self._require_app_key(app_key)
|
|
181
|
+
body = self._require_dict(payload)
|
|
182
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/webhookTest", app_key=app_key, json_body=body)
|
|
183
|
+
|
|
184
|
+
def workflow_qsource_query(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
185
|
+
self._require_app_key(app_key)
|
|
186
|
+
body = self._require_dict(payload)
|
|
187
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/qSourceQuery", app_key=app_key, json_body=body)
|
|
188
|
+
|
|
189
|
+
def workflow_qsource_test(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
190
|
+
self._require_app_key(app_key)
|
|
191
|
+
body = self._require_dict(payload)
|
|
192
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/qSourceQueryTest", app_key=app_key, json_body=body)
|
|
193
|
+
|
|
194
|
+
def workflow_get_qsource_active(self, *, profile: str, app_key: str, qsource_id: int) -> JSONObject:
|
|
195
|
+
self._require_app_key(app_key)
|
|
196
|
+
self._require_positive("qsource_id", qsource_id)
|
|
197
|
+
return self._request(profile, "GET", f"/app/{app_key}/auditNode/active/qsource", app_key=app_key, params={"qSourceId": qsource_id})
|
|
198
|
+
|
|
199
|
+
def workflow_upsert_qsource_active(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
200
|
+
self._require_app_key(app_key)
|
|
201
|
+
body = self._require_dict(payload)
|
|
202
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/active/qsource", app_key=app_key, json_body=body)
|
|
203
|
+
|
|
204
|
+
def workflow_get_qsource_passive(self, *, profile: str, app_key: str, qsource_id: int) -> JSONObject:
|
|
205
|
+
self._require_app_key(app_key)
|
|
206
|
+
self._require_positive("qsource_id", qsource_id)
|
|
207
|
+
return self._request(profile, "GET", f"/app/{app_key}/auditNode/passive/qsource", app_key=app_key, params={"qSourceId": qsource_id})
|
|
208
|
+
|
|
209
|
+
def workflow_upsert_qsource_passive(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
210
|
+
self._require_app_key(app_key)
|
|
211
|
+
body = self._require_dict(payload)
|
|
212
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/passive/qsource", app_key=app_key, json_body=body)
|
|
213
|
+
|
|
214
|
+
def workflow_switch_qsource_status(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
215
|
+
self._require_app_key(app_key)
|
|
216
|
+
body = self._require_dict(payload)
|
|
217
|
+
return self._request(profile, "POST", f"/app/{app_key}/auditNode/source/status", app_key=app_key, json_body=body)
|
|
218
|
+
|
|
219
|
+
def workflow_delete_qsource(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
220
|
+
self._require_app_key(app_key)
|
|
221
|
+
body = self._require_dict(payload)
|
|
222
|
+
return self._request(profile, "DELETE", f"/app/{app_key}/auditNode/qsource", app_key=app_key, json_body=body, risk_operation="delete", risk_target="workflow qsource configuration")
|
|
223
|
+
|
|
224
|
+
def workflow_get_editable_question_ids(self, *, profile: str, app_key: str, audit_node_id: int) -> JSONObject:
|
|
225
|
+
self._require_app_key(app_key)
|
|
226
|
+
self._require_positive("audit_node_id", audit_node_id)
|
|
227
|
+
return self._request(
|
|
228
|
+
profile,
|
|
229
|
+
"GET",
|
|
230
|
+
f"/app/{app_key}/auditNode/{audit_node_id}/editableQueIds",
|
|
231
|
+
app_key=app_key,
|
|
232
|
+
audit_node_id=audit_node_id,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def workflow_get_print_nodes(self, *, profile: str, app_key: str) -> JSONObject:
|
|
236
|
+
self._require_app_key(app_key)
|
|
237
|
+
return self._request(profile, "GET", f"/app/{app_key}/auditNode/printNodes", app_key=app_key)
|
|
238
|
+
|
|
239
|
+
def _request(
|
|
240
|
+
self,
|
|
241
|
+
profile: str,
|
|
242
|
+
method: str,
|
|
243
|
+
path: str,
|
|
244
|
+
*,
|
|
245
|
+
app_key: str,
|
|
246
|
+
json_body: JSONValue = None,
|
|
247
|
+
params: JSONObject | None = None,
|
|
248
|
+
risk_operation: str | None = None,
|
|
249
|
+
risk_target: str | None = None,
|
|
250
|
+
**extra: JSONValue,
|
|
251
|
+
) -> JSONObject:
|
|
252
|
+
def runner(session_profile, context):
|
|
253
|
+
result = self.backend.request(method, context, path, json_body=json_body, params=params)
|
|
254
|
+
response: JSONObject = {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result}
|
|
255
|
+
response.update(extra)
|
|
256
|
+
if risk_operation and risk_target:
|
|
257
|
+
return self._attach_human_review_notice(response, operation=risk_operation, target=risk_target)
|
|
258
|
+
return response
|
|
259
|
+
|
|
260
|
+
return self._run(profile, runner)
|
|
261
|
+
|
|
262
|
+
def _request_with_post_fallbacks(
|
|
263
|
+
self,
|
|
264
|
+
*,
|
|
265
|
+
profile: str,
|
|
266
|
+
app_key: str,
|
|
267
|
+
path: str,
|
|
268
|
+
json_body: JSONObject,
|
|
269
|
+
alternate_paths: list[str],
|
|
270
|
+
risk_operation: str | None = None,
|
|
271
|
+
risk_target: str | None = None,
|
|
272
|
+
**extra: JSONValue,
|
|
273
|
+
) -> JSONObject:
|
|
274
|
+
def runner(session_profile, context):
|
|
275
|
+
attempted_contexts = [context]
|
|
276
|
+
if context.qf_version is not None:
|
|
277
|
+
attempted_contexts.append(
|
|
278
|
+
BackendRequestContext(
|
|
279
|
+
base_url=context.base_url,
|
|
280
|
+
token=context.token,
|
|
281
|
+
ws_id=context.ws_id,
|
|
282
|
+
qf_version=None,
|
|
283
|
+
qf_version_source="workflow_retry_without_qf_version",
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
paths = [path, *alternate_paths]
|
|
287
|
+
last_error: QingflowApiError | None = None
|
|
288
|
+
for call_context in attempted_contexts:
|
|
289
|
+
for candidate_path in paths:
|
|
290
|
+
try:
|
|
291
|
+
result = self.backend.request("POST", call_context, candidate_path, json_body=json_body)
|
|
292
|
+
response: JSONObject = {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
293
|
+
response.update(extra)
|
|
294
|
+
if risk_operation and risk_target:
|
|
295
|
+
return self._attach_human_review_notice(response, operation=risk_operation, target=risk_target)
|
|
296
|
+
return response
|
|
297
|
+
except QingflowApiError as error:
|
|
298
|
+
last_error = error
|
|
299
|
+
if error.http_status != 404:
|
|
300
|
+
raise
|
|
301
|
+
assert last_error is not None
|
|
302
|
+
raise last_error
|
|
303
|
+
|
|
304
|
+
return self._run(profile, runner)
|
|
305
|
+
|
|
306
|
+
def _require_app_key(self, app_key: str) -> None:
|
|
307
|
+
if not app_key:
|
|
308
|
+
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
309
|
+
|
|
310
|
+
def _require_positive(self, field_name: str, value: int) -> None:
|
|
311
|
+
if value <= 0:
|
|
312
|
+
raise_tool_error(QingflowApiError.config_error(f"{field_name} must be positive"))
|