@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
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ..backend_client import BackendClient, BackendRequestContext
6
+ from ..json_types import JSONObject, JSONValue
7
+
8
+ WORKFLOW_SPEC_SCHEMA_PATH = "/api/workflow/spec/schema"
9
+ WORKFLOW_SPEC_GET_PATH = "/api/workflow/spec"
10
+ WORKFLOW_SPEC_APPLY_PATH = "/api/workflow/spec:apply"
11
+ DEFAULT_SCHEMA_VERSION = "vnext-2026-06"
12
+
13
+
14
+ def fetch_schema(
15
+ backend: BackendClient,
16
+ context: BackendRequestContext,
17
+ *,
18
+ schema_version: str | None = None,
19
+ ) -> JSONValue:
20
+ params: JSONObject = {}
21
+ if schema_version:
22
+ params["schemaVersion"] = schema_version
23
+ return backend.request(
24
+ "GET",
25
+ context,
26
+ WORKFLOW_SPEC_SCHEMA_PATH,
27
+ params=params or None,
28
+ )
29
+
30
+
31
+ def fetch_spec(
32
+ backend: BackendClient,
33
+ context: BackendRequestContext,
34
+ *,
35
+ app_key: str,
36
+ version_id: str | None = None,
37
+ ) -> JSONValue:
38
+ params: JSONObject = {"appKey": app_key}
39
+ if version_id:
40
+ params["versionId"] = version_id
41
+ return backend.request("GET", context, WORKFLOW_SPEC_GET_PATH, params=params)
42
+
43
+
44
+ def post_apply(
45
+ backend: BackendClient,
46
+ context: BackendRequestContext,
47
+ *,
48
+ apply_body: JSONObject,
49
+ ) -> JSONValue:
50
+ return backend.request("POST", context, WORKFLOW_SPEC_APPLY_PATH, json_body=apply_body)
51
+
52
+
53
+ def verify_apply_response(
54
+ *,
55
+ input_spec: JSONObject,
56
+ apply_result: JSONObject,
57
+ ) -> tuple[JSONObject, list[dict[str, Any]], bool]:
58
+ warnings: list[dict[str, Any]] = []
59
+ applied_spec = apply_result.get("appliedSpec")
60
+ diff_summary = apply_result.get("diffSummary")
61
+ semantic_lint = apply_result.get("semanticLint") if isinstance(apply_result.get("semanticLint"), list) else []
62
+
63
+ verification: JSONObject = {
64
+ "applied_spec_available": isinstance(applied_spec, dict),
65
+ "version_snapshot_confirmed": False,
66
+ }
67
+
68
+ lint_errors = [
69
+ item
70
+ for item in semantic_lint
71
+ if isinstance(item, dict) and str(item.get("severity") or item.get("level") or "").upper() == "ERROR"
72
+ ]
73
+ if lint_errors:
74
+ verification["semantic_lint_error_count"] = len(lint_errors)
75
+ for item in lint_errors:
76
+ warnings.append(
77
+ {
78
+ "code": item.get("code") or "SEMANTIC_LINT_ERROR",
79
+ "message": item.get("message") or "workflow spec semantic lint error",
80
+ "path": item.get("path"),
81
+ }
82
+ )
83
+
84
+ diff_failed = False
85
+ input_ids: set[str] = set()
86
+ if isinstance(input_spec, dict):
87
+ input_ids = {
88
+ str(node.get("id"))
89
+ for node in (input_spec.get("nodes") or [])
90
+ if isinstance(node, dict) and node.get("id") is not None
91
+ }
92
+ if isinstance(diff_summary, dict) and input_ids:
93
+ removed = diff_summary.get("nodes", {}).get("removed") if isinstance(diff_summary.get("nodes"), dict) else None
94
+ if isinstance(removed, list):
95
+ unexpected = [node_id for node_id in removed if str(node_id) in input_ids]
96
+ if unexpected:
97
+ diff_failed = True
98
+ verification["unexpected_removed_node_ids"] = unexpected
99
+
100
+ if isinstance(applied_spec, dict) and isinstance(input_spec, dict):
101
+ applied_ids = {
102
+ str(node.get("id"))
103
+ for node in (applied_spec.get("nodes") or [])
104
+ if isinstance(node, dict) and node.get("id") is not None
105
+ }
106
+ missing_input_ids = sorted(node_id for node_id in input_ids if node_id not in applied_ids)
107
+ if missing_input_ids and not diff_failed:
108
+ verification["missing_input_node_ids"] = missing_input_ids
109
+
110
+ verified = not lint_errors and not diff_failed and verification.get("applied_spec_available") is True
111
+ return verification, warnings, verified
@@ -13,30 +13,17 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
13
13
  app_subparsers = parser.add_subparsers(dest="app_command", required=True)
14
14
 
15
15
  list_parser = app_subparsers.add_parser("list", help="列出可见应用")
16
+ list_parser.add_argument("--query", default="", help="按关键词在可见应用列表中本地过滤")
17
+ list_parser.add_argument("--keyword", default="", help="兼容别名;建议使用 --query")
16
18
  list_parser.set_defaults(handler=_handle_list, format_hint="app_list")
17
19
 
18
- search = app_subparsers.add_parser("search", help="搜索应用")
19
- search.add_argument("--keyword", default="")
20
- search.add_argument("--page", type=int, default=1)
21
- search.add_argument("--page-size", type=int, default=50)
22
- search.set_defaults(handler=_handle_search, format_hint="app_search")
23
-
24
20
  get = app_subparsers.add_parser("get", help="读取应用可访问视图与导入能力")
25
21
  get.add_argument("--app-key", help="不传时在交互终端中选择应用")
26
22
  get.set_defaults(handler=_handle_get, format_hint="app_get")
27
23
 
28
24
 
29
25
  def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
30
- return context.app.app_list(profile=args.profile)
31
-
32
-
33
- def _handle_search(args: argparse.Namespace, context: CliContext) -> dict:
34
- return context.app.app_search(
35
- profile=args.profile,
36
- keyword=args.keyword,
37
- page_num=args.page,
38
- page_size=args.page_size,
39
- )
26
+ return context.app.app_list(profile=args.profile, query=args.query, keyword=args.keyword)
40
27
 
41
28
 
42
29
  def _handle_get(args: argparse.Namespace, context: CliContext) -> dict: