@josephyan/qingflow-cli 0.2.0-beta.1000
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 +31 -0
- package/docs/local-agent-install.md +309 -0
- package/entry_point.py +13 -0
- package/npm/bin/qingflow.mjs +5 -0
- package/npm/lib/runtime.mjs +346 -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 +37 -0
- package/src/qingflow_mcp/__main__.py +5 -0
- package/src/qingflow_mcp/backend_client.py +649 -0
- package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
- package/src/qingflow_mcp/builder_facade/models.py +1846 -0
- package/src/qingflow_mcp/builder_facade/service.py +16502 -0
- package/src/qingflow_mcp/cli/__init__.py +1 -0
- package/src/qingflow_mcp/cli/commands/__init__.py +18 -0
- package/src/qingflow_mcp/cli/commands/app.py +40 -0
- package/src/qingflow_mcp/cli/commands/auth.py +112 -0
- package/src/qingflow_mcp/cli/commands/builder.py +539 -0
- package/src/qingflow_mcp/cli/commands/chart.py +18 -0
- package/src/qingflow_mcp/cli/commands/common.py +62 -0
- package/src/qingflow_mcp/cli/commands/imports.py +96 -0
- package/src/qingflow_mcp/cli/commands/portal.py +25 -0
- package/src/qingflow_mcp/cli/commands/record.py +331 -0
- package/src/qingflow_mcp/cli/commands/repo.py +80 -0
- package/src/qingflow_mcp/cli/commands/task.py +141 -0
- package/src/qingflow_mcp/cli/commands/view.py +18 -0
- package/src/qingflow_mcp/cli/commands/workspace.py +110 -0
- package/src/qingflow_mcp/cli/context.py +60 -0
- package/src/qingflow_mcp/cli/formatters.py +573 -0
- package/src/qingflow_mcp/cli/json_io.py +50 -0
- package/src/qingflow_mcp/cli/main.py +186 -0
- package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
- package/src/qingflow_mcp/cli/terminal_ui.py +173 -0
- package/src/qingflow_mcp/config.py +407 -0
- package/src/qingflow_mcp/errors.py +66 -0
- package/src/qingflow_mcp/id_utils.py +49 -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/public_surface.py +243 -0
- package/src/qingflow_mcp/repository_store.py +71 -0
- package/src/qingflow_mcp/response_trim.py +841 -0
- package/src/qingflow_mcp/server.py +216 -0
- package/src/qingflow_mcp/server_app_builder.py +543 -0
- package/src/qingflow_mcp/server_app_user.py +386 -0
- package/src/qingflow_mcp/session_store.py +369 -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 +495 -0
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +187 -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 +2398 -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 +855 -0
- package/src/qingflow_mcp/tools/__init__.py +1 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +3449 -0
- package/src/qingflow_mcp/tools/app_tools.py +926 -0
- package/src/qingflow_mcp/tools/approval_tools.py +1062 -0
- package/src/qingflow_mcp/tools/auth_tools.py +1133 -0
- package/src/qingflow_mcp/tools/base.py +281 -0
- package/src/qingflow_mcp/tools/code_block_tools.py +777 -0
- package/src/qingflow_mcp/tools/custom_button_tools.py +202 -0
- package/src/qingflow_mcp/tools/directory_tools.py +675 -0
- package/src/qingflow_mcp/tools/feedback_tools.py +238 -0
- package/src/qingflow_mcp/tools/file_tools.py +409 -0
- package/src/qingflow_mcp/tools/import_tools.py +2223 -0
- package/src/qingflow_mcp/tools/navigation_tools.py +210 -0
- package/src/qingflow_mcp/tools/package_tools.py +326 -0
- package/src/qingflow_mcp/tools/portal_tools.py +158 -0
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +374 -0
- package/src/qingflow_mcp/tools/record_tools.py +14291 -0
- package/src/qingflow_mcp/tools/repository_dev_tools.py +552 -0
- package/src/qingflow_mcp/tools/resource_read_tools.py +503 -0
- package/src/qingflow_mcp/tools/role_tools.py +112 -0
- package/src/qingflow_mcp/tools/solution_tools.py +4054 -0
- package/src/qingflow_mcp/tools/task_context_tools.py +2986 -0
- package/src/qingflow_mcp/tools/task_tools.py +889 -0
- package/src/qingflow_mcp/tools/view_tools.py +335 -0
- package/src/qingflow_mcp/tools/workflow_tools.py +376 -0
- package/src/qingflow_mcp/tools/workspace_tools.py +266 -0
|
@@ -0,0 +1,210 @@
|
|
|
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
|
|
8
|
+
from .base import ToolBase, tool_cn_name
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NavigationTools(ToolBase):
|
|
12
|
+
"""导航工具(中文名:导航菜单管理)。
|
|
13
|
+
|
|
14
|
+
类型:门户导航配置工具。
|
|
15
|
+
主要职责:
|
|
16
|
+
1. 查询导航结构与发布状态;
|
|
17
|
+
2. 读取指定导航详情;
|
|
18
|
+
3. 应用导航配置变更。
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def register(self, mcp: FastMCP) -> None:
|
|
22
|
+
"""注册当前工具到 MCP 服务。"""
|
|
23
|
+
@mcp.tool()
|
|
24
|
+
def navigation_list_published(profile: str = DEFAULT_PROFILE, page_num: int = 1, page_size: int = 50) -> JSONObject:
|
|
25
|
+
return self.navigation_list_published(profile=profile, page_num=page_num, page_size=page_size)
|
|
26
|
+
|
|
27
|
+
@mcp.tool()
|
|
28
|
+
def navigation_list_draft_page(
|
|
29
|
+
profile: str = DEFAULT_PROFILE,
|
|
30
|
+
page_num: int = 1,
|
|
31
|
+
page_size: int = 50,
|
|
32
|
+
query_key: str | None = None,
|
|
33
|
+
) -> JSONObject:
|
|
34
|
+
return self.navigation_list_draft_page(profile=profile, page_num=page_num, page_size=page_size, query_key=query_key)
|
|
35
|
+
|
|
36
|
+
@mcp.tool()
|
|
37
|
+
def navigation_list_draft_all(profile: str = DEFAULT_PROFILE, query_key: str | None = None) -> JSONObject:
|
|
38
|
+
return self.navigation_list_draft_all(profile=profile, query_key=query_key)
|
|
39
|
+
|
|
40
|
+
@mcp.tool()
|
|
41
|
+
def navigation_get_detail(profile: str = DEFAULT_PROFILE, navigation_item_id: int = 0, status: str = "draft") -> JSONObject:
|
|
42
|
+
return self.navigation_get_detail(profile=profile, navigation_item_id=navigation_item_id, status=status)
|
|
43
|
+
|
|
44
|
+
@mcp.tool()
|
|
45
|
+
def navigation_get_status(profile: str = DEFAULT_PROFILE) -> JSONObject:
|
|
46
|
+
return self.navigation_get_status(profile=profile)
|
|
47
|
+
|
|
48
|
+
@mcp.tool()
|
|
49
|
+
def navigation_set_visible(profile: str = DEFAULT_PROFILE, payload: JSONObject | None = None) -> JSONObject:
|
|
50
|
+
return self.navigation_set_visible(profile=profile, payload=payload or {})
|
|
51
|
+
|
|
52
|
+
@mcp.tool()
|
|
53
|
+
def navigation_create(profile: str = DEFAULT_PROFILE, payload: JSONObject | None = None) -> JSONObject:
|
|
54
|
+
return self.navigation_create(profile=profile, payload=payload or {})
|
|
55
|
+
|
|
56
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="update", target="navigation item configuration"))
|
|
57
|
+
def navigation_update(profile: str = DEFAULT_PROFILE, navigation_item_id: int = 0, payload: JSONObject | None = None) -> JSONObject:
|
|
58
|
+
return self.navigation_update(profile=profile, navigation_item_id=navigation_item_id, payload=payload or {})
|
|
59
|
+
|
|
60
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="navigation item configuration"))
|
|
61
|
+
def navigation_delete(profile: str = DEFAULT_PROFILE, navigation_item_id: int = 0) -> JSONObject:
|
|
62
|
+
return self.navigation_delete(profile=profile, navigation_item_id=navigation_item_id)
|
|
63
|
+
|
|
64
|
+
@mcp.tool()
|
|
65
|
+
def navigation_publish(profile: str = DEFAULT_PROFILE, navigation_id: int = 0) -> JSONObject:
|
|
66
|
+
return self.navigation_publish(profile=profile, navigation_id=navigation_id)
|
|
67
|
+
|
|
68
|
+
@mcp.tool()
|
|
69
|
+
def navigation_reorder(profile: str = DEFAULT_PROFILE, payload: list[JSONObject] | None = None) -> JSONObject:
|
|
70
|
+
return self.navigation_reorder(profile=profile, payload=payload or [])
|
|
71
|
+
|
|
72
|
+
@tool_cn_name("导航已发布列表")
|
|
73
|
+
def navigation_list_published(self, *, profile: str, page_num: int = 1, page_size: int = 50) -> JSONObject:
|
|
74
|
+
"""执行工具方法逻辑。"""
|
|
75
|
+
def runner(session_profile, context):
|
|
76
|
+
result = self.backend.request("GET", context, "/navigation", params={"pageNum": page_num, "pageSize": page_size})
|
|
77
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "page": result}
|
|
78
|
+
|
|
79
|
+
return self._run(profile, runner)
|
|
80
|
+
|
|
81
|
+
@tool_cn_name("导航草稿分页")
|
|
82
|
+
def navigation_list_draft_page(self, *, profile: str, page_num: int = 1, page_size: int = 50, query_key: str | None = None) -> JSONObject:
|
|
83
|
+
"""执行工具方法逻辑。"""
|
|
84
|
+
def runner(session_profile, context):
|
|
85
|
+
params: JSONObject = {"pageNum": page_num, "pageSize": page_size}
|
|
86
|
+
if query_key:
|
|
87
|
+
params["queryCondition"] = query_key
|
|
88
|
+
result = self.backend.request("GET", context, "/navigation/page", params=params)
|
|
89
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "page": result}
|
|
90
|
+
|
|
91
|
+
return self._run(profile, runner)
|
|
92
|
+
|
|
93
|
+
@tool_cn_name("导航草稿全量")
|
|
94
|
+
def navigation_list_draft_all(self, *, profile: str, query_key: str | None = None) -> JSONObject:
|
|
95
|
+
"""执行工具方法逻辑。"""
|
|
96
|
+
def runner(session_profile, context):
|
|
97
|
+
params: JSONObject = {}
|
|
98
|
+
if query_key:
|
|
99
|
+
params["queryCondition"] = query_key
|
|
100
|
+
result = self.backend.request("GET", context, "/navigation/all", params=params)
|
|
101
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "items": result}
|
|
102
|
+
|
|
103
|
+
return self._run(profile, runner)
|
|
104
|
+
|
|
105
|
+
@tool_cn_name("导航详情")
|
|
106
|
+
def navigation_get_detail(self, *, profile: str, navigation_item_id: int, status: str = "draft") -> JSONObject:
|
|
107
|
+
"""执行工具方法逻辑。"""
|
|
108
|
+
self._require_navigation_item_id(navigation_item_id)
|
|
109
|
+
|
|
110
|
+
def runner(session_profile, context):
|
|
111
|
+
result = self.backend.request(
|
|
112
|
+
"GET",
|
|
113
|
+
context,
|
|
114
|
+
f"/navigation/detail/{navigation_item_id}",
|
|
115
|
+
params={"status": status},
|
|
116
|
+
)
|
|
117
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "navigation_item_id": navigation_item_id, "result": result}
|
|
118
|
+
|
|
119
|
+
return self._run(profile, runner)
|
|
120
|
+
|
|
121
|
+
@tool_cn_name("导航发布状态")
|
|
122
|
+
def navigation_get_status(self, *, profile: str) -> JSONObject:
|
|
123
|
+
"""执行工具方法逻辑。"""
|
|
124
|
+
def runner(session_profile, context):
|
|
125
|
+
result = self.backend.request("GET", context, "/navigation/status")
|
|
126
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
127
|
+
|
|
128
|
+
return self._run(profile, runner)
|
|
129
|
+
|
|
130
|
+
@tool_cn_name("设置导航可见性")
|
|
131
|
+
def navigation_set_visible(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
132
|
+
"""执行工具方法逻辑。"""
|
|
133
|
+
body = self._require_dict(payload)
|
|
134
|
+
|
|
135
|
+
def runner(session_profile, context):
|
|
136
|
+
result = self.backend.request("POST", context, "/navigation/visible", json_body=body)
|
|
137
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
138
|
+
|
|
139
|
+
return self._run(profile, runner)
|
|
140
|
+
|
|
141
|
+
@tool_cn_name("创建导航项")
|
|
142
|
+
def navigation_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
143
|
+
"""执行工具方法逻辑。"""
|
|
144
|
+
body = self._require_dict(payload)
|
|
145
|
+
|
|
146
|
+
def runner(session_profile, context):
|
|
147
|
+
result = self.backend.request("POST", context, "/navigation", json_body=body)
|
|
148
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
149
|
+
|
|
150
|
+
return self._run(profile, runner)
|
|
151
|
+
|
|
152
|
+
@tool_cn_name("更新导航项")
|
|
153
|
+
def navigation_update(self, *, profile: str, navigation_item_id: int, payload: JSONObject) -> JSONObject:
|
|
154
|
+
"""执行工具方法逻辑。"""
|
|
155
|
+
self._require_navigation_item_id(navigation_item_id)
|
|
156
|
+
body = self._require_dict(payload)
|
|
157
|
+
|
|
158
|
+
def runner(session_profile, context):
|
|
159
|
+
result = self.backend.request("PUT", context, f"/navigation/{navigation_item_id}", json_body=body)
|
|
160
|
+
return self._attach_human_review_notice(
|
|
161
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "navigation_item_id": navigation_item_id, "result": result},
|
|
162
|
+
operation="update",
|
|
163
|
+
target="navigation item configuration",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return self._run(profile, runner)
|
|
167
|
+
|
|
168
|
+
@tool_cn_name("删除导航项")
|
|
169
|
+
def navigation_delete(self, *, profile: str, navigation_item_id: int) -> JSONObject:
|
|
170
|
+
"""执行工具方法逻辑。"""
|
|
171
|
+
self._require_navigation_item_id(navigation_item_id)
|
|
172
|
+
|
|
173
|
+
def runner(session_profile, context):
|
|
174
|
+
result = self.backend.request("DELETE", context, f"/navigation/{navigation_item_id}")
|
|
175
|
+
return self._attach_human_review_notice(
|
|
176
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "navigation_item_id": navigation_item_id, "result": result},
|
|
177
|
+
operation="delete",
|
|
178
|
+
target="navigation item configuration",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return self._run(profile, runner)
|
|
182
|
+
|
|
183
|
+
@tool_cn_name("发布导航")
|
|
184
|
+
def navigation_publish(self, *, profile: str, navigation_id: int) -> JSONObject:
|
|
185
|
+
"""执行工具方法逻辑。"""
|
|
186
|
+
if navigation_id <= 0:
|
|
187
|
+
raise_tool_error(QingflowApiError.config_error("navigation_id must be positive"))
|
|
188
|
+
|
|
189
|
+
def runner(session_profile, context):
|
|
190
|
+
result = self.backend.request("POST", context, f"/navigation/publish/{navigation_id}")
|
|
191
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "navigation_id": navigation_id, "result": result}
|
|
192
|
+
|
|
193
|
+
return self._run(profile, runner)
|
|
194
|
+
|
|
195
|
+
@tool_cn_name("导航排序")
|
|
196
|
+
def navigation_reorder(self, *, profile: str, payload: list[JSONObject]) -> JSONObject:
|
|
197
|
+
"""执行工具方法逻辑。"""
|
|
198
|
+
if not isinstance(payload, list) or not payload:
|
|
199
|
+
raise_tool_error(QingflowApiError.config_error("payload must be a non-empty array"))
|
|
200
|
+
|
|
201
|
+
def runner(session_profile, context):
|
|
202
|
+
result = self.backend.request("POST", context, "/navigation/ordinal", json_body=payload)
|
|
203
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
204
|
+
|
|
205
|
+
return self._run(profile, runner)
|
|
206
|
+
|
|
207
|
+
def _require_navigation_item_id(self, navigation_item_id: int) -> None:
|
|
208
|
+
"""执行内部辅助逻辑。"""
|
|
209
|
+
if navigation_item_id <= 0:
|
|
210
|
+
raise_tool_error(QingflowApiError.config_error("navigation_item_id must be positive"))
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from ..config import DEFAULT_PROFILE
|
|
8
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
9
|
+
from ..json_types import JSONObject
|
|
10
|
+
from .base import ToolBase, tool_cn_name
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PackageTools(ToolBase):
|
|
14
|
+
"""分组工具(中文名:分组与归档管理)。
|
|
15
|
+
|
|
16
|
+
类型:资源组织工具。
|
|
17
|
+
主要职责:
|
|
18
|
+
1. 查询、解析、创建分组;
|
|
19
|
+
2. 管理应用与分组的挂载关系;
|
|
20
|
+
3. 为应用搭建提供分组定位能力。
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def register(self, mcp: FastMCP) -> None:
|
|
24
|
+
"""注册当前工具到 MCP 服务。"""
|
|
25
|
+
@mcp.tool()
|
|
26
|
+
def package_list(profile: str = DEFAULT_PROFILE, trial_status: str = "all", include_raw: bool = False) -> JSONObject:
|
|
27
|
+
return self.package_list(profile=profile, trial_status=trial_status, include_raw=include_raw)
|
|
28
|
+
|
|
29
|
+
@mcp.tool()
|
|
30
|
+
def package_get(profile: str = DEFAULT_PROFILE, tag_id: int = 0, include_raw: bool = False) -> JSONObject:
|
|
31
|
+
return self.package_get(profile=profile, tag_id=tag_id, include_raw=include_raw)
|
|
32
|
+
|
|
33
|
+
@mcp.tool()
|
|
34
|
+
def package_get_base(profile: str = DEFAULT_PROFILE, tag_id: int = 0, include_raw: bool = False) -> JSONObject:
|
|
35
|
+
return self.package_get_base(profile=profile, tag_id=tag_id, include_raw=include_raw)
|
|
36
|
+
|
|
37
|
+
@mcp.tool()
|
|
38
|
+
def package_create(profile: str = DEFAULT_PROFILE, payload: JSONObject | None = None) -> JSONObject:
|
|
39
|
+
return self.package_create(profile=profile, payload=payload or {})
|
|
40
|
+
|
|
41
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="update", target="app package settings"))
|
|
42
|
+
def package_update(profile: str = DEFAULT_PROFILE, tag_id: int = 0, payload: JSONObject | None = None) -> JSONObject:
|
|
43
|
+
return self.package_update(profile=profile, tag_id=tag_id, payload=payload or {})
|
|
44
|
+
|
|
45
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="app package and linked data"))
|
|
46
|
+
def package_delete(profile: str = DEFAULT_PROFILE, tag_id: int = 0, deleted_all_data: bool = False) -> JSONObject:
|
|
47
|
+
return self.package_delete(profile=profile, tag_id=tag_id, deleted_all_data=deleted_all_data)
|
|
48
|
+
|
|
49
|
+
@tool_cn_name("分组列表")
|
|
50
|
+
def package_list(self, *, profile: str, trial_status: str = "all", include_raw: bool = False) -> JSONObject:
|
|
51
|
+
"""执行分组与包相关逻辑。"""
|
|
52
|
+
def runner(session_profile, context):
|
|
53
|
+
result = self.backend.request("GET", context, "/tag", params={"trialStatus": trial_status})
|
|
54
|
+
raw_items, source_shape = self._extract_package_items(result)
|
|
55
|
+
compact_items = [self._compact_package(item) for item in raw_items if isinstance(item, dict)]
|
|
56
|
+
response = {
|
|
57
|
+
"profile": profile,
|
|
58
|
+
"ws_id": session_profile.selected_ws_id,
|
|
59
|
+
"trial_status": trial_status,
|
|
60
|
+
"items": result if include_raw else compact_items,
|
|
61
|
+
"count": len(compact_items),
|
|
62
|
+
"compact": not include_raw,
|
|
63
|
+
"source_shape": source_shape,
|
|
64
|
+
"retried": False,
|
|
65
|
+
}
|
|
66
|
+
if include_raw:
|
|
67
|
+
response["summary"] = compact_items
|
|
68
|
+
return response
|
|
69
|
+
|
|
70
|
+
return self._run(profile, runner)
|
|
71
|
+
|
|
72
|
+
@tool_cn_name("分组详情")
|
|
73
|
+
def package_get(self, *, profile: str, tag_id: int, include_raw: bool = False) -> JSONObject:
|
|
74
|
+
"""执行分组与包相关逻辑。"""
|
|
75
|
+
self._require_tag_id(tag_id)
|
|
76
|
+
|
|
77
|
+
def runner(session_profile, context):
|
|
78
|
+
result = self.backend.request("GET", context, f"/tag/{tag_id}")
|
|
79
|
+
base_info = self.backend.request("GET", context, f"/tag/{tag_id}/baseInfo")
|
|
80
|
+
compact = self._compact_package(
|
|
81
|
+
result if isinstance(result, dict) else {},
|
|
82
|
+
base_info=base_info if isinstance(base_info, dict) else None,
|
|
83
|
+
)
|
|
84
|
+
response = {
|
|
85
|
+
"profile": profile,
|
|
86
|
+
"ws_id": session_profile.selected_ws_id,
|
|
87
|
+
"tag_id": tag_id,
|
|
88
|
+
"result": result if include_raw else compact,
|
|
89
|
+
"compact": not include_raw,
|
|
90
|
+
}
|
|
91
|
+
if include_raw:
|
|
92
|
+
response["summary"] = compact
|
|
93
|
+
return response
|
|
94
|
+
|
|
95
|
+
return self._run(profile, runner)
|
|
96
|
+
|
|
97
|
+
@tool_cn_name("分组基础信息")
|
|
98
|
+
def package_get_base(self, *, profile: str, tag_id: int, include_raw: bool = False) -> JSONObject:
|
|
99
|
+
"""执行分组与包相关逻辑。"""
|
|
100
|
+
self._require_readable_tag_id(tag_id)
|
|
101
|
+
|
|
102
|
+
def runner(session_profile, context):
|
|
103
|
+
result = self.backend.request("GET", context, f"/tag/{tag_id}/baseInfo")
|
|
104
|
+
compact = self._compact_package(result if isinstance(result, dict) else {})
|
|
105
|
+
response = {
|
|
106
|
+
"profile": profile,
|
|
107
|
+
"ws_id": session_profile.selected_ws_id,
|
|
108
|
+
"tag_id": tag_id,
|
|
109
|
+
"result": result if include_raw else compact,
|
|
110
|
+
"compact": not include_raw,
|
|
111
|
+
}
|
|
112
|
+
if include_raw:
|
|
113
|
+
response["summary"] = compact
|
|
114
|
+
return response
|
|
115
|
+
|
|
116
|
+
return self._run(profile, runner)
|
|
117
|
+
|
|
118
|
+
@tool_cn_name("创建分组")
|
|
119
|
+
def package_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
|
|
120
|
+
"""执行分组与包相关逻辑。"""
|
|
121
|
+
body = self._require_dict(payload)
|
|
122
|
+
|
|
123
|
+
def runner(session_profile, context):
|
|
124
|
+
result = self.backend.request("POST", context, "/tag", json_body=self._normalize_package_payload(body))
|
|
125
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
|
|
126
|
+
|
|
127
|
+
return self._run(profile, runner)
|
|
128
|
+
|
|
129
|
+
@tool_cn_name("更新分组")
|
|
130
|
+
def package_update(self, *, profile: str, tag_id: int, payload: JSONObject) -> JSONObject:
|
|
131
|
+
"""执行分组与包相关逻辑。"""
|
|
132
|
+
self._require_tag_id(tag_id)
|
|
133
|
+
body = self._require_dict(payload)
|
|
134
|
+
|
|
135
|
+
def runner(session_profile, context):
|
|
136
|
+
current = self.backend.request("GET", context, f"/tag/{tag_id}")
|
|
137
|
+
result = self.backend.request(
|
|
138
|
+
"PUT",
|
|
139
|
+
context,
|
|
140
|
+
f"/tag/{tag_id}",
|
|
141
|
+
json_body=self._normalize_package_payload(body, existing=current),
|
|
142
|
+
)
|
|
143
|
+
return self._attach_human_review_notice(
|
|
144
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "result": result},
|
|
145
|
+
operation="update",
|
|
146
|
+
target="app package settings",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return self._run(profile, runner)
|
|
150
|
+
|
|
151
|
+
@tool_cn_name("分组排序")
|
|
152
|
+
def package_sort_items(self, *, profile: str, tag_id: int, tag_items: list[JSONObject]) -> JSONObject:
|
|
153
|
+
"""执行分组与包相关逻辑。"""
|
|
154
|
+
self._require_tag_id(tag_id)
|
|
155
|
+
|
|
156
|
+
def runner(session_profile, context):
|
|
157
|
+
result = self.backend.request(
|
|
158
|
+
"POST",
|
|
159
|
+
context,
|
|
160
|
+
f"/tag/{tag_id}/ordinal",
|
|
161
|
+
json_body={"tagItems": deepcopy(tag_items)},
|
|
162
|
+
)
|
|
163
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "result": result}
|
|
164
|
+
|
|
165
|
+
return self._run(profile, runner)
|
|
166
|
+
|
|
167
|
+
@tool_cn_name("创建分组分栏")
|
|
168
|
+
def package_group_create(self, *, profile: str, tag_id: int, group_name: str) -> JSONObject:
|
|
169
|
+
"""执行分组与包相关逻辑。"""
|
|
170
|
+
self._require_tag_id(tag_id)
|
|
171
|
+
normalized_name = str(group_name or "").strip()
|
|
172
|
+
if not normalized_name:
|
|
173
|
+
raise_tool_error(QingflowApiError.config_error("group_name is required"))
|
|
174
|
+
|
|
175
|
+
def runner(session_profile, context):
|
|
176
|
+
result = self.backend.request(
|
|
177
|
+
"POST",
|
|
178
|
+
context,
|
|
179
|
+
f"/tag/{tag_id}/group",
|
|
180
|
+
json_body={"groupName": normalized_name},
|
|
181
|
+
)
|
|
182
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "result": result}
|
|
183
|
+
|
|
184
|
+
return self._run(profile, runner)
|
|
185
|
+
|
|
186
|
+
@tool_cn_name("更新分组分栏")
|
|
187
|
+
def package_group_update(self, *, profile: str, tag_id: int, group_id: int, group_name: str) -> JSONObject:
|
|
188
|
+
"""执行分组与包相关逻辑。"""
|
|
189
|
+
self._require_tag_id(tag_id)
|
|
190
|
+
self._require_group_id(group_id)
|
|
191
|
+
normalized_name = str(group_name or "").strip()
|
|
192
|
+
if not normalized_name:
|
|
193
|
+
raise_tool_error(QingflowApiError.config_error("group_name is required"))
|
|
194
|
+
|
|
195
|
+
def runner(session_profile, context):
|
|
196
|
+
result = self.backend.request(
|
|
197
|
+
"POST",
|
|
198
|
+
context,
|
|
199
|
+
f"/tag/{tag_id}/group/{group_id}",
|
|
200
|
+
json_body={"groupName": normalized_name},
|
|
201
|
+
)
|
|
202
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "group_id": group_id, "result": result}
|
|
203
|
+
|
|
204
|
+
return self._run(profile, runner)
|
|
205
|
+
|
|
206
|
+
@tool_cn_name("删除分组分栏")
|
|
207
|
+
def package_group_delete(self, *, profile: str, tag_id: int, group_id: int) -> JSONObject:
|
|
208
|
+
"""执行分组与包相关逻辑。"""
|
|
209
|
+
self._require_tag_id(tag_id)
|
|
210
|
+
self._require_group_id(group_id)
|
|
211
|
+
|
|
212
|
+
def runner(session_profile, context):
|
|
213
|
+
result = self.backend.request("DELETE", context, f"/tag/{tag_id}/group/{group_id}")
|
|
214
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "group_id": group_id, "result": result}
|
|
215
|
+
|
|
216
|
+
return self._run(profile, runner)
|
|
217
|
+
|
|
218
|
+
@tool_cn_name("删除分组")
|
|
219
|
+
def package_delete(self, *, profile: str, tag_id: int, deleted_all_data: bool = False) -> JSONObject:
|
|
220
|
+
"""执行分组与包相关逻辑。"""
|
|
221
|
+
self._require_tag_id(tag_id)
|
|
222
|
+
|
|
223
|
+
def runner(session_profile, context):
|
|
224
|
+
result = self.backend.request(
|
|
225
|
+
"DELETE",
|
|
226
|
+
context,
|
|
227
|
+
"/tag",
|
|
228
|
+
json_body={"tagId": tag_id, "deletedAllData": deleted_all_data},
|
|
229
|
+
)
|
|
230
|
+
return self._attach_human_review_notice(
|
|
231
|
+
{"profile": profile, "ws_id": session_profile.selected_ws_id, "tag_id": tag_id, "deleted_all_data": deleted_all_data, "result": result},
|
|
232
|
+
operation="delete",
|
|
233
|
+
target="app package and linked data",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return self._run(profile, runner)
|
|
237
|
+
|
|
238
|
+
def _require_tag_id(self, tag_id: int) -> None:
|
|
239
|
+
"""执行内部辅助逻辑。"""
|
|
240
|
+
if tag_id <= 0:
|
|
241
|
+
raise_tool_error(QingflowApiError.config_error("tag_id must be positive"))
|
|
242
|
+
|
|
243
|
+
def _require_group_id(self, group_id: int) -> None:
|
|
244
|
+
"""执行内部辅助逻辑。"""
|
|
245
|
+
if group_id <= 0:
|
|
246
|
+
raise_tool_error(QingflowApiError.config_error("group_id must be positive"))
|
|
247
|
+
|
|
248
|
+
def _require_readable_tag_id(self, tag_id: int) -> None:
|
|
249
|
+
"""执行内部辅助逻辑。"""
|
|
250
|
+
if tag_id < 0:
|
|
251
|
+
raise_tool_error(QingflowApiError.config_error("tag_id must be non-negative"))
|
|
252
|
+
|
|
253
|
+
def _normalize_package_payload(self, payload: JSONObject, existing: JSONObject | None = None) -> JSONObject:
|
|
254
|
+
"""执行内部辅助逻辑。"""
|
|
255
|
+
data = deepcopy(existing) if isinstance(existing, dict) else {}
|
|
256
|
+
data.update(deepcopy(payload))
|
|
257
|
+
data.pop("tagId", None)
|
|
258
|
+
data.pop("createTime", None)
|
|
259
|
+
data.pop("creator", None)
|
|
260
|
+
data.pop("publishStatus", None)
|
|
261
|
+
data.pop("beingTrial", None)
|
|
262
|
+
data.pop("trialExpireDate", None)
|
|
263
|
+
data.setdefault("tagIcon", "")
|
|
264
|
+
data.setdefault("tagItems", [])
|
|
265
|
+
data.setdefault("auth", _default_package_auth())
|
|
266
|
+
return data
|
|
267
|
+
|
|
268
|
+
def _compact_package(self, result: dict[str, object], *, base_info: dict[str, object] | None = None) -> JSONObject:
|
|
269
|
+
"""执行内部辅助逻辑。"""
|
|
270
|
+
tag_items = result.get("tagItems") if isinstance(result.get("tagItems"), list) else []
|
|
271
|
+
permission_source = base_info if isinstance(base_info, dict) else result
|
|
272
|
+
preview = []
|
|
273
|
+
for item in tag_items[:10]:
|
|
274
|
+
if not isinstance(item, dict):
|
|
275
|
+
continue
|
|
276
|
+
preview.append(
|
|
277
|
+
{
|
|
278
|
+
"title": item.get("title"),
|
|
279
|
+
"itemType": item.get("itemType"),
|
|
280
|
+
"appKey": item.get("appKey"),
|
|
281
|
+
"dashKey": item.get("dashKey"),
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
compact = {
|
|
285
|
+
"tagId": result.get("tagId"),
|
|
286
|
+
"tagName": result.get("tagName"),
|
|
287
|
+
"tagIcon": result.get("tagIcon") if result.get("tagIcon") is not None else permission_source.get("tagIcon"),
|
|
288
|
+
"publishStatus": result.get("publishStatus") if result.get("publishStatus") is not None else permission_source.get("publishStatus"),
|
|
289
|
+
"beingTrial": result.get("beingTrial"),
|
|
290
|
+
"itemCount": len(tag_items),
|
|
291
|
+
"itemPreview": preview,
|
|
292
|
+
"addAppStatus": permission_source.get("addAppStatus"),
|
|
293
|
+
"editAppStatus": permission_source.get("editAppStatus"),
|
|
294
|
+
"delAppStatus": permission_source.get("delAppStatus"),
|
|
295
|
+
"editTagStatus": permission_source.get("editTagStatus"),
|
|
296
|
+
}
|
|
297
|
+
return {key: value for key, value in compact.items() if value is not None}
|
|
298
|
+
|
|
299
|
+
def _extract_package_items(self, result: object) -> tuple[list[dict[str, object]], str]:
|
|
300
|
+
"""执行内部辅助逻辑。"""
|
|
301
|
+
if isinstance(result, list):
|
|
302
|
+
return [item for item in result if isinstance(item, dict)], "list"
|
|
303
|
+
if isinstance(result, dict):
|
|
304
|
+
tags = result.get("tags")
|
|
305
|
+
if isinstance(tags, list):
|
|
306
|
+
return [item for item in tags if isinstance(item, dict)], "dict.tags"
|
|
307
|
+
items = result.get("items")
|
|
308
|
+
if isinstance(items, list):
|
|
309
|
+
return [item for item in items if isinstance(item, dict)], "dict.items"
|
|
310
|
+
return [], "dict.unknown"
|
|
311
|
+
return [], type(result).__name__
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _default_package_auth() -> JSONObject:
|
|
315
|
+
members = {
|
|
316
|
+
"depart": [],
|
|
317
|
+
"dynamic": [],
|
|
318
|
+
"includeSubDeparts": None,
|
|
319
|
+
"member": [],
|
|
320
|
+
"role": [],
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
"type": "WORKSPACE",
|
|
324
|
+
"contactAuth": {"type": "WORKSPACE_ALL", "authMembers": deepcopy(members)},
|
|
325
|
+
"externalMemberAuth": {"type": "NOT", "authMembers": deepcopy(members)},
|
|
326
|
+
}
|