@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
|
@@ -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:
|