@qingflow-tech/qingflow-cli 1.1.43-beta.2 → 1.1.43-beta.4
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 +11 -5
- package/entry_point.py +1 -1
- package/npm/bin/qingflow.mjs +1 -2
- package/npm/lib/runtime.mjs +3 -7
- package/package.json +1 -2
- package/pyproject.toml +1 -3
- package/skills/qingflow-app-form/SKILL.md +23 -11
- package/skills/qingflow-cli/SKILL.md +4 -2
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +1 -1
- package/skills/qingflow-cli/reference/builder/10-build-single-app.md +3 -3
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +1 -1
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +25 -24
- package/skills/qingflow-cli/reference/builder/40-layout.md +5 -5
- package/skills/qingflow-cli/reference/builder/README.md +3 -3
- package/skills/qingflow-cli/reference/builder/code-integrations/README.md +1 -1
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +7 -7
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_FIELD_DATA_TYPES.md +1 -1
- package/skills/qingflow-cli/reference/examples/README.md +1 -1
- package/skills/qingflow-common/SKILL.md +47 -0
- package/skills/qingflow-common/agents/openai.yaml +4 -0
- package/src/qingflow_mcp/__main__.py +2 -3
- package/src/qingflow_mcp/app_form_compat.py +11 -27
- package/src/qingflow_mcp/app_form_validator.py +201 -35
- package/src/qingflow_mcp/builder_facade/app_form.py +762 -148
- package/src/qingflow_mcp/builder_facade/app_form_schema.py +114 -3
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +310 -245
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +85 -32
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +82 -30
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +113 -37
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +87 -64
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +83 -28
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +83 -28
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +85 -32
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +95 -40
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +100 -28
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +212 -117
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +143 -161
- package/src/qingflow_mcp/builder_facade/service.py +3 -2
- package/src/qingflow_mcp/response_trim.py +2 -0
- package/src/qingflow_mcp/server.py +24 -3
- package/src/qingflow_mcp/server_app_builder.py +2 -2
- package/src/qingflow_mcp/tools/record_tools.py +3 -3
- package/src/qingflow_mcp/version.py +2 -4
- package/docs/local-agent-install.md +0 -370
- package/skills/qingflow-mcp-setup/SKILL.md +0 -115
- package/skills/qingflow-mcp-setup/agents/openai.yaml +0 -4
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +0 -34
- package/skills/qingflow-mcp-setup/references/environments.md +0 -62
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +0 -32
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +0 -38
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any, Iterable
|
|
9
9
|
|
|
10
10
|
from jsonschema import Draft202012Validator, FormatChecker
|
|
11
11
|
|
|
@@ -14,6 +14,116 @@ JSONObject = dict[str, Any]
|
|
|
14
14
|
SCHEMA_ROOT = Path(__file__).with_name("schemas") / "app_form"
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def _discriminator_score(schema: Any, instance: Any, root_schema: Any = None, seen_refs: set[str] | None = None) -> int:
|
|
18
|
+
"""Score a schema branch against its input discriminator values."""
|
|
19
|
+
if not isinstance(schema, dict) or not isinstance(instance, dict):
|
|
20
|
+
return 0
|
|
21
|
+
reference = schema.get("$ref")
|
|
22
|
+
if isinstance(reference, str) and reference.startswith("#/") and isinstance(root_schema, dict):
|
|
23
|
+
seen_refs = seen_refs or set()
|
|
24
|
+
if reference in seen_refs:
|
|
25
|
+
return 0
|
|
26
|
+
target: Any = root_schema
|
|
27
|
+
for part in reference[2:].split("/"):
|
|
28
|
+
if not isinstance(target, dict):
|
|
29
|
+
return 0
|
|
30
|
+
target = target.get(part.replace("~1", "/").replace("~0", "~"))
|
|
31
|
+
return _discriminator_score(target, instance, root_schema, seen_refs | {reference})
|
|
32
|
+
score = 0
|
|
33
|
+
properties = schema.get("properties")
|
|
34
|
+
if isinstance(properties, dict):
|
|
35
|
+
for key, constraint in properties.items():
|
|
36
|
+
if not isinstance(constraint, dict) or key not in instance:
|
|
37
|
+
continue
|
|
38
|
+
value = instance.get(key)
|
|
39
|
+
if "const" in constraint:
|
|
40
|
+
score += 3 if value == constraint["const"] else -3
|
|
41
|
+
elif isinstance(constraint.get("enum"), list):
|
|
42
|
+
score += 3 if value in constraint["enum"] else -3
|
|
43
|
+
for combinator in ("allOf", "anyOf", "oneOf"):
|
|
44
|
+
children = schema.get(combinator)
|
|
45
|
+
if not isinstance(children, list):
|
|
46
|
+
continue
|
|
47
|
+
child_scores = [_discriminator_score(child, instance, root_schema, seen_refs) for child in children]
|
|
48
|
+
if combinator == "allOf":
|
|
49
|
+
score += sum(child_score for child_score in child_scores if child_score > 0)
|
|
50
|
+
elif child_scores:
|
|
51
|
+
score += max(child_scores)
|
|
52
|
+
return score
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _selected_combinator_children(error: Any, root_schema: Any = None) -> list[Any] | None:
|
|
56
|
+
"""Return errors from the branch matching a known discriminator.
|
|
57
|
+
|
|
58
|
+
jsonschema flattens errors from nested ``allOf`` branches into the parent
|
|
59
|
+
combinator context, so branch identity is recovered from the schema path.
|
|
60
|
+
"""
|
|
61
|
+
if not isinstance(error.schema, dict):
|
|
62
|
+
return None
|
|
63
|
+
branches = error.schema.get(error.validator)
|
|
64
|
+
if not isinstance(branches, list) or not branches:
|
|
65
|
+
return None
|
|
66
|
+
scores = [_discriminator_score(branch, error.instance, root_schema) for branch in branches]
|
|
67
|
+
best = max(scores)
|
|
68
|
+
if best <= 0:
|
|
69
|
+
return None
|
|
70
|
+
selected = {index for index, score in enumerate(scores) if score == best}
|
|
71
|
+
prefix = tuple(error.absolute_schema_path)
|
|
72
|
+
children = [
|
|
73
|
+
child
|
|
74
|
+
for child in error.context
|
|
75
|
+
if len(tuple(child.absolute_schema_path)) > len(prefix)
|
|
76
|
+
and tuple(child.absolute_schema_path)[: len(prefix)] == prefix
|
|
77
|
+
and tuple(child.absolute_schema_path)[len(prefix)] in selected
|
|
78
|
+
]
|
|
79
|
+
return children or None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _iter_compact_schema_errors(error: Any, root_schema: Any = None) -> Iterable[Any]:
|
|
83
|
+
if error.validator in {"oneOf", "anyOf"}:
|
|
84
|
+
selected = _selected_combinator_children(error, root_schema)
|
|
85
|
+
if selected:
|
|
86
|
+
for child in selected:
|
|
87
|
+
yield from _iter_compact_schema_errors(child, root_schema)
|
|
88
|
+
return
|
|
89
|
+
yield error
|
|
90
|
+
return
|
|
91
|
+
if error.context:
|
|
92
|
+
for child in error.context:
|
|
93
|
+
yield from _iter_compact_schema_errors(child, root_schema)
|
|
94
|
+
return
|
|
95
|
+
yield error
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def compact_schema_errors(errors: Iterable[Any], root_schema: Any = None) -> list[Any]:
|
|
99
|
+
"""Collapse polymorphic-schema noise while retaining actionable errors."""
|
|
100
|
+
flattened = [leaf for error in errors for leaf in _iter_compact_schema_errors(error, root_schema)]
|
|
101
|
+
descendant_paths = [tuple(error.absolute_path) for error in flattened]
|
|
102
|
+
result: list[Any] = []
|
|
103
|
+
seen: set[tuple[tuple[Any, ...], str, str]] = set()
|
|
104
|
+
for error in flattened:
|
|
105
|
+
path = tuple(error.absolute_path)
|
|
106
|
+
if error.validator == "unevaluatedProperties" and any(
|
|
107
|
+
len(other) > len(path) and other[: len(path)] == path for other in descendant_paths
|
|
108
|
+
):
|
|
109
|
+
continue
|
|
110
|
+
key = (path, str(error.validator), schema_error_message(error))
|
|
111
|
+
if key in seen:
|
|
112
|
+
continue
|
|
113
|
+
seen.add(key)
|
|
114
|
+
result.append(error)
|
|
115
|
+
return result
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def schema_error_message(error: Any) -> str:
|
|
119
|
+
"""Render a bounded, user-facing message for a JSON Schema error."""
|
|
120
|
+
if error.validator == "oneOf":
|
|
121
|
+
return "value is not valid under any of the given schemas"
|
|
122
|
+
if error.validator == "anyOf":
|
|
123
|
+
return "value is not valid under any of the given schemas"
|
|
124
|
+
return str(error.message)
|
|
125
|
+
|
|
126
|
+
|
|
17
127
|
class AppFormContractError(ValueError):
|
|
18
128
|
def __init__(self, error_code: str, message: str, *, path: str | None = None, details: JSONObject | None = None) -> None:
|
|
19
129
|
super().__init__(message)
|
|
@@ -39,11 +149,12 @@ class AppFormSchemaContext:
|
|
|
39
149
|
errors = sorted(validator.iter_errors(payload), key=lambda item: (list(item.absolute_path), item.message))
|
|
40
150
|
if not errors:
|
|
41
151
|
return
|
|
42
|
-
|
|
152
|
+
compact_errors = compact_schema_errors(errors, self.complete_schema)
|
|
153
|
+
error = compact_errors[0] if compact_errors else errors[0]
|
|
43
154
|
pointer = "".join(f"/{str(part).replace('~', '~0').replace('/', '~1')}" for part in error.absolute_path) or "/"
|
|
44
155
|
raise AppFormContractError(
|
|
45
156
|
"INVALID_SPEC",
|
|
46
|
-
error
|
|
157
|
+
schema_error_message(error),
|
|
47
158
|
path=pointer,
|
|
48
159
|
details={"validator": error.validator, "schemaPath": "/".join(str(part) for part in error.absolute_schema_path)},
|
|
49
160
|
)
|