@josephyan/qingflow-cli 1.0.11 → 1.1.2
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 +3 -3
- package/npm/bin/qingflow.mjs +40 -2
- package/npm/lib/runtime.mjs +386 -15
- package/npm/scripts/postinstall.mjs +7 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +440 -0
- package/skills/qingflow-cli/manifest.yaml +10 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +94 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +485 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md +237 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_MATCH_RULES.md +137 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md +263 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +304 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md +41 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +139 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +84 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +129 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +195 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +159 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +20 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md +176 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +163 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +107 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +151 -0
- package/skills/qingflow-cli/reference/_batch_schema_complex.json +18 -0
- package/skills/qingflow-cli/reference/_batch_schema_scalar.json +17 -0
- package/skills/qingflow-cli/reference/charts_remove.example.json +1 -0
- package/skills/qingflow-cli/reference/charts_reorder.example.json +1 -0
- package/skills/qingflow-cli/reference/charts_upsert_bar.example.json +8 -0
- package/skills/qingflow-cli/reference/charts_upsert_dashboard_starter.example.json +37 -0
- package/skills/qingflow-cli/reference/charts_upsert_minimal.example.json +13 -0
- package/skills/qingflow-cli/reference/portal_sections_all_types.example.json +131 -0
- package/skills/qingflow-cli/reference/portal_sections_five_types.example.json +126 -0
- package/skills/qingflow-cli/reference/portal_sections_standard_workbench.example.json +128 -0
- package/skills/qingflow-cli/reference/schema_add_fields_minimal.example.json +7 -0
- package/skills/qingflow-cli/reference/schema_apply_add_fields_all_types.json +78 -0
- package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +7 -0
- package/skills/qingflow-cli/scripts/builder-package-from-app-list.py +140 -0
- package/skills/qingflow-cli/scripts/find-app-by-keyword.py +132 -0
- package/skills/qingflow-cli/scripts/validate_qingflow_output_files.py +87 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/builder_facade/models.py +532 -48
- package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
- package/src/qingflow_mcp/cli/commands/app.py +3 -16
- package/src/qingflow_mcp/cli/commands/builder.py +354 -56
- package/src/qingflow_mcp/cli/commands/record.py +89 -2
- package/src/qingflow_mcp/cli/formatters.py +32 -1
- package/src/qingflow_mcp/cli/main.py +245 -3
- package/src/qingflow_mcp/public_surface.py +11 -8
- package/src/qingflow_mcp/response_trim.py +143 -14
- package/src/qingflow_mcp/server.py +15 -12
- package/src/qingflow_mcp/server_app_builder.py +108 -30
- package/src/qingflow_mcp/server_app_user.py +17 -18
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
- package/src/qingflow_mcp/solution/executor.py +3 -133
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
- package/src/qingflow_mcp/tools/app_tools.py +53 -8
- package/src/qingflow_mcp/tools/package_tools.py +16 -2
- package/src/qingflow_mcp/tools/record_tools.py +2095 -176
- package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
- package/src/qingflow_mcp/tools/solution_tools.py +30 -2
- package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
- package/src/qingflow_mcp/version.py +110 -0
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -10,6 +10,7 @@ from ..config import DEFAULT_PROFILE
|
|
|
10
10
|
from ..errors import QingflowApiError, raise_tool_error
|
|
11
11
|
from ..json_types import JSONObject
|
|
12
12
|
from ..list_type_labels import SYSTEM_VIEW_DEFINITIONS, get_app_publish_status_label
|
|
13
|
+
from ..solution.compiler.icon_utils import workspace_icon_config
|
|
13
14
|
from .base import ToolBase, tool_cn_name
|
|
14
15
|
|
|
15
16
|
|
|
@@ -18,7 +19,7 @@ class AppTools(ToolBase):
|
|
|
18
19
|
|
|
19
20
|
类型:应用元数据与配置工具。
|
|
20
21
|
主要职责:
|
|
21
|
-
1.
|
|
22
|
+
1. 查询应用列表、读取应用详情;
|
|
22
23
|
2. 读取与更新应用基础配置、表单结构与发布状态;
|
|
23
24
|
3. 提供应用创建、删除、发布等管理能力。
|
|
24
25
|
"""
|
|
@@ -26,12 +27,8 @@ class AppTools(ToolBase):
|
|
|
26
27
|
def register(self, mcp: FastMCP) -> None:
|
|
27
28
|
"""注册当前工具到 MCP 服务。"""
|
|
28
29
|
@mcp.tool()
|
|
29
|
-
def app_list(profile: str = DEFAULT_PROFILE, ship_auth: bool = False) -> JSONObject:
|
|
30
|
-
return self.app_list(profile=profile, ship_auth=ship_auth)
|
|
31
|
-
|
|
32
|
-
@mcp.tool()
|
|
33
|
-
def app_search(profile: str = DEFAULT_PROFILE, keyword: str = "", page_num: int = 1, page_size: int = 50) -> JSONObject:
|
|
34
|
-
return self.app_search(profile=profile, keyword=keyword, page_num=page_num, page_size=page_size)
|
|
30
|
+
def app_list(profile: str = DEFAULT_PROFILE, query: str = "", keyword: str = "", ship_auth: bool = False) -> JSONObject:
|
|
31
|
+
return self.app_list(profile=profile, query=query, keyword=keyword, ship_auth=ship_auth)
|
|
35
32
|
|
|
36
33
|
@mcp.tool()
|
|
37
34
|
def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
@@ -90,24 +87,51 @@ class AppTools(ToolBase):
|
|
|
90
87
|
return self.app_publish(profile=profile, app_key=app_key, payload=payload or {})
|
|
91
88
|
|
|
92
89
|
@tool_cn_name("应用列表")
|
|
93
|
-
def app_list(self, *, profile: str, ship_auth: bool = False) -> JSONObject:
|
|
90
|
+
def app_list(self, *, profile: str, query: str = "", keyword: str = "", ship_auth: bool = False) -> JSONObject:
|
|
94
91
|
"""List current-user visible apps in the selected workspace."""
|
|
95
92
|
def runner(session_profile, context):
|
|
96
93
|
result = self.backend.request("GET", context, "/tag/apps")
|
|
97
94
|
items, source_shape = self._extract_visible_apps(result)
|
|
95
|
+
normalized_query, warnings = _normalize_app_list_query(query=query, keyword=keyword)
|
|
96
|
+
unfiltered_count = len(items)
|
|
97
|
+
if normalized_query:
|
|
98
|
+
items = self._filter_visible_apps(items, normalized_query)
|
|
98
99
|
response = {
|
|
99
100
|
"profile": profile,
|
|
100
101
|
"ws_id": session_profile.selected_ws_id,
|
|
101
102
|
"items": items,
|
|
102
103
|
"count": len(items),
|
|
103
104
|
"source_shape": source_shape,
|
|
105
|
+
"query": normalized_query,
|
|
106
|
+
"matched_count": len(items),
|
|
107
|
+
"unfiltered_count": unfiltered_count,
|
|
108
|
+
"filter_mode": "local_visible_apps",
|
|
104
109
|
}
|
|
110
|
+
if warnings:
|
|
111
|
+
response["warnings"] = warnings
|
|
105
112
|
if ship_auth:
|
|
106
113
|
response["raw"] = result
|
|
107
114
|
return response
|
|
108
115
|
|
|
109
116
|
return self._run(profile, runner)
|
|
110
117
|
|
|
118
|
+
def _filter_visible_apps(self, items: list[JSONObject], query: str) -> list[JSONObject]:
|
|
119
|
+
"""Filter current-user visible apps locally without calling admin search APIs."""
|
|
120
|
+
needle = query.casefold()
|
|
121
|
+
matched: list[JSONObject] = []
|
|
122
|
+
for item in items:
|
|
123
|
+
haystacks = (
|
|
124
|
+
item.get("app_key"),
|
|
125
|
+
item.get("app_name"),
|
|
126
|
+
item.get("title"),
|
|
127
|
+
item.get("package_name"),
|
|
128
|
+
item.get("tag_name"),
|
|
129
|
+
item.get("group_name"),
|
|
130
|
+
)
|
|
131
|
+
if any(needle in str(value).casefold() for value in haystacks if value not in (None, "")):
|
|
132
|
+
matched.append(item)
|
|
133
|
+
return matched
|
|
134
|
+
|
|
111
135
|
@tool_cn_name("应用搜索")
|
|
112
136
|
def app_search(self, *, profile: str, keyword: str = "", page_num: int = 1, page_size: int = 50) -> JSONObject:
|
|
113
137
|
"""Search apps by keyword in name/title using backend search API.
|
|
@@ -157,6 +181,7 @@ class AppTools(ToolBase):
|
|
|
157
181
|
warnings: list[JSONObject] = []
|
|
158
182
|
app_name = app_key
|
|
159
183
|
base_info: JSONObject | None = None
|
|
184
|
+
app_icon = None
|
|
160
185
|
|
|
161
186
|
try:
|
|
162
187
|
base_info = self.backend.request("GET", context, f"/app/{app_key}/baseInfo")
|
|
@@ -165,6 +190,7 @@ class AppTools(ToolBase):
|
|
|
165
190
|
str(base_info.get("formTitle") or base_info.get("title") or base_info.get("appName") or app_key).strip()
|
|
166
191
|
or app_key
|
|
167
192
|
)
|
|
193
|
+
app_icon = str(base_info.get("appIcon") or "").strip() or None
|
|
168
194
|
except QingflowApiError as exc:
|
|
169
195
|
if exc.backend_code not in {40002, 40027}:
|
|
170
196
|
raise
|
|
@@ -202,6 +228,8 @@ class AppTools(ToolBase):
|
|
|
202
228
|
"data": {
|
|
203
229
|
"app_key": app_key,
|
|
204
230
|
"app_name": app_name,
|
|
231
|
+
"app_icon": app_icon,
|
|
232
|
+
"icon_config": workspace_icon_config(app_icon),
|
|
205
233
|
"can_create": can_create,
|
|
206
234
|
"import_capability": import_capability,
|
|
207
235
|
"accessible_views": accessible_views,
|
|
@@ -924,3 +952,20 @@ def _coerce_optional_bool(value: Any) -> bool | None:
|
|
|
924
952
|
if isinstance(value, bool):
|
|
925
953
|
return value
|
|
926
954
|
return None
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
def _normalize_app_list_query(*, query: str = "", keyword: str = "") -> tuple[str, list[JSONObject]]:
|
|
958
|
+
normalized_query = str(query or "").strip()
|
|
959
|
+
normalized_keyword = str(keyword or "").strip()
|
|
960
|
+
warnings: list[JSONObject] = []
|
|
961
|
+
if normalized_query and normalized_keyword:
|
|
962
|
+
warnings.append(
|
|
963
|
+
{
|
|
964
|
+
"code": "APP_LIST_QUERY_TAKES_PRECEDENCE",
|
|
965
|
+
"message": "Both query and keyword were provided; query was used and keyword was ignored.",
|
|
966
|
+
"ignored_keyword": normalized_keyword,
|
|
967
|
+
}
|
|
968
|
+
)
|
|
969
|
+
if normalized_query:
|
|
970
|
+
return normalized_query, warnings
|
|
971
|
+
return normalized_keyword, warnings
|
|
@@ -7,6 +7,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
7
7
|
from ..config import DEFAULT_PROFILE
|
|
8
8
|
from ..errors import QingflowApiError, raise_tool_error
|
|
9
9
|
from ..json_types import JSONObject
|
|
10
|
+
from ..solution.compiler.icon_utils import workspace_icon_config
|
|
10
11
|
from .base import ToolBase, tool_cn_name
|
|
11
12
|
|
|
12
13
|
|
|
@@ -281,13 +282,26 @@ class PackageTools(ToolBase):
|
|
|
281
282
|
"dashKey": item.get("dashKey"),
|
|
282
283
|
}
|
|
283
284
|
)
|
|
285
|
+
item_count = None
|
|
286
|
+
raw_item_count = result.get("itemCount")
|
|
287
|
+
try:
|
|
288
|
+
if raw_item_count is not None:
|
|
289
|
+
coerced_count = int(raw_item_count)
|
|
290
|
+
if coerced_count >= 0:
|
|
291
|
+
item_count = coerced_count
|
|
292
|
+
except (TypeError, ValueError):
|
|
293
|
+
item_count = None
|
|
294
|
+
if item_count is None and "tagItems" in result:
|
|
295
|
+
item_count = len(tag_items)
|
|
296
|
+
tag_icon = result.get("tagIcon") if result.get("tagIcon") is not None else permission_source.get("tagIcon")
|
|
284
297
|
compact = {
|
|
285
298
|
"tagId": result.get("tagId"),
|
|
286
299
|
"tagName": result.get("tagName"),
|
|
287
|
-
"tagIcon":
|
|
300
|
+
"tagIcon": tag_icon,
|
|
301
|
+
"iconConfig": workspace_icon_config(str(tag_icon).strip() if tag_icon not in (None, "") else None),
|
|
288
302
|
"publishStatus": result.get("publishStatus") if result.get("publishStatus") is not None else permission_source.get("publishStatus"),
|
|
289
303
|
"beingTrial": result.get("beingTrial"),
|
|
290
|
-
"itemCount":
|
|
304
|
+
"itemCount": item_count,
|
|
291
305
|
"itemPreview": preview,
|
|
292
306
|
"addAppStatus": permission_source.get("addAppStatus"),
|
|
293
307
|
"editAppStatus": permission_source.get("editAppStatus"),
|