@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.
Files changed (67) hide show
  1. package/README.md +3 -3
  2. package/npm/bin/qingflow.mjs +40 -2
  3. package/npm/lib/runtime.mjs +386 -15
  4. package/npm/scripts/postinstall.mjs +7 -2
  5. package/package.json +1 -1
  6. package/pyproject.toml +1 -1
  7. package/skills/qingflow-cli/SKILL.md +440 -0
  8. package/skills/qingflow-cli/manifest.yaml +10 -0
  9. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +94 -0
  10. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +485 -0
  11. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md +237 -0
  12. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_MATCH_RULES.md +137 -0
  13. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md +263 -0
  14. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +304 -0
  15. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md +41 -0
  16. package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +139 -0
  17. package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +84 -0
  18. package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +129 -0
  19. package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +195 -0
  20. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +159 -0
  21. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +20 -0
  22. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md +176 -0
  23. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +163 -0
  24. package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +107 -0
  25. package/skills/qingflow-cli/reference/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +151 -0
  26. package/skills/qingflow-cli/reference/_batch_schema_complex.json +18 -0
  27. package/skills/qingflow-cli/reference/_batch_schema_scalar.json +17 -0
  28. package/skills/qingflow-cli/reference/charts_remove.example.json +1 -0
  29. package/skills/qingflow-cli/reference/charts_reorder.example.json +1 -0
  30. package/skills/qingflow-cli/reference/charts_upsert_bar.example.json +8 -0
  31. package/skills/qingflow-cli/reference/charts_upsert_dashboard_starter.example.json +37 -0
  32. package/skills/qingflow-cli/reference/charts_upsert_minimal.example.json +13 -0
  33. package/skills/qingflow-cli/reference/portal_sections_all_types.example.json +131 -0
  34. package/skills/qingflow-cli/reference/portal_sections_five_types.example.json +126 -0
  35. package/skills/qingflow-cli/reference/portal_sections_standard_workbench.example.json +128 -0
  36. package/skills/qingflow-cli/reference/schema_add_fields_minimal.example.json +7 -0
  37. package/skills/qingflow-cli/reference/schema_apply_add_fields_all_types.json +78 -0
  38. package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +7 -0
  39. package/skills/qingflow-cli/scripts/builder-package-from-app-list.py +140 -0
  40. package/skills/qingflow-cli/scripts/find-app-by-keyword.py +132 -0
  41. package/skills/qingflow-cli/scripts/validate_qingflow_output_files.py +87 -0
  42. package/src/qingflow_mcp/__init__.py +1 -1
  43. package/src/qingflow_mcp/builder_facade/models.py +532 -48
  44. package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
  45. package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
  46. package/src/qingflow_mcp/cli/commands/app.py +3 -16
  47. package/src/qingflow_mcp/cli/commands/builder.py +354 -56
  48. package/src/qingflow_mcp/cli/commands/record.py +89 -2
  49. package/src/qingflow_mcp/cli/formatters.py +32 -1
  50. package/src/qingflow_mcp/cli/main.py +245 -3
  51. package/src/qingflow_mcp/public_surface.py +11 -8
  52. package/src/qingflow_mcp/response_trim.py +143 -14
  53. package/src/qingflow_mcp/server.py +15 -12
  54. package/src/qingflow_mcp/server_app_builder.py +108 -30
  55. package/src/qingflow_mcp/server_app_user.py +17 -18
  56. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  57. package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
  58. package/src/qingflow_mcp/solution/executor.py +3 -133
  59. package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
  60. package/src/qingflow_mcp/tools/app_tools.py +53 -8
  61. package/src/qingflow_mcp/tools/package_tools.py +16 -2
  62. package/src/qingflow_mcp/tools/record_tools.py +2095 -176
  63. package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
  64. package/src/qingflow_mcp/tools/solution_tools.py +30 -2
  65. package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
  66. package/src/qingflow_mcp/version.py +110 -0
  67. 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": result.get("tagIcon") if result.get("tagIcon") is not None else permission_source.get("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": len(tag_items),
304
+ "itemCount": item_count,
291
305
  "itemPreview": preview,
292
306
  "addAppStatus": permission_source.get("addAppStatus"),
293
307
  "editAppStatus": permission_source.get("editAppStatus"),