@josephyan/qingflow-app-user-mcp 0.2.0-beta.91 → 0.2.0-beta.92
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 +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/__init__.py +33 -1
- package/src/qingflow_mcp/builder_facade/service.py +101 -6
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.92
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.92 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, packages_distributions, version as _dist_version
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
3
6
|
__all__ = ["__version__"]
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
_FALLBACK_VERSION = "0.2.0b92"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _resolve_local_pyproject_version() -> str | None:
|
|
12
|
+
module_path = Path(__file__).resolve()
|
|
13
|
+
for parent in module_path.parents:
|
|
14
|
+
candidate = parent / "pyproject.toml"
|
|
15
|
+
if not candidate.is_file():
|
|
16
|
+
continue
|
|
17
|
+
for line in candidate.read_text(encoding="utf-8").splitlines():
|
|
18
|
+
stripped = line.strip()
|
|
19
|
+
if stripped.startswith("version = "):
|
|
20
|
+
return stripped.split("=", 1)[1].strip().strip('"')
|
|
21
|
+
break
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _resolve_runtime_version() -> str:
|
|
26
|
+
local_version = _resolve_local_pyproject_version()
|
|
27
|
+
if local_version:
|
|
28
|
+
return local_version
|
|
29
|
+
for dist_name in packages_distributions().get("qingflow_mcp", []):
|
|
30
|
+
try:
|
|
31
|
+
return _dist_version(dist_name)
|
|
32
|
+
except PackageNotFoundError:
|
|
33
|
+
continue
|
|
34
|
+
return _FALLBACK_VERSION
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__version__ = _resolve_runtime_version()
|
|
@@ -9865,8 +9865,9 @@ def _parse_field(question: dict[str, Any], *, field_id_hint: str | None = None)
|
|
|
9865
9865
|
"que_id": que_id,
|
|
9866
9866
|
"que_type": que_type,
|
|
9867
9867
|
"default_type": _coerce_positive_int(question.get("queDefaultType")) if "queDefaultType" in question else None,
|
|
9868
|
-
"default_value": question.get("queDefaultValue") if "queDefaultValue" in question else None,
|
|
9869
9868
|
}
|
|
9869
|
+
if "queDefaultValue" in question:
|
|
9870
|
+
field["default_value"] = question.get("queDefaultValue")
|
|
9870
9871
|
if field_type in {FieldType.single_select.value, FieldType.multi_select.value, FieldType.boolean.value}:
|
|
9871
9872
|
options = question.get("options")
|
|
9872
9873
|
if isinstance(options, list):
|
|
@@ -12753,6 +12754,65 @@ def _field_needs_question_rebuild(field: dict[str, Any]) -> bool:
|
|
|
12753
12754
|
return not isinstance(field.get("_question_template"), dict) or bool(field.get("_question_rebuild_required"))
|
|
12754
12755
|
|
|
12755
12756
|
|
|
12757
|
+
def _extract_template_row_lengths(schema: dict[str, Any]) -> tuple[dict[int, int], dict[str, int]]:
|
|
12758
|
+
lengths_by_que_id: dict[int, int] = {}
|
|
12759
|
+
lengths_by_title: dict[str, int] = {}
|
|
12760
|
+
|
|
12761
|
+
def remember_row(row: Any) -> None:
|
|
12762
|
+
if not isinstance(row, list):
|
|
12763
|
+
return
|
|
12764
|
+
questions = [question for question in row if isinstance(question, dict)]
|
|
12765
|
+
row_length = len(questions)
|
|
12766
|
+
if row_length <= 0:
|
|
12767
|
+
return
|
|
12768
|
+
for question in questions:
|
|
12769
|
+
que_id = _coerce_nonnegative_int(question.get("queId"))
|
|
12770
|
+
if que_id is not None:
|
|
12771
|
+
lengths_by_que_id[que_id] = row_length
|
|
12772
|
+
title = str(question.get("queTitle") or "").strip()
|
|
12773
|
+
if title:
|
|
12774
|
+
lengths_by_title[title] = row_length
|
|
12775
|
+
|
|
12776
|
+
for row in schema.get("formQues", []) or []:
|
|
12777
|
+
if not isinstance(row, list):
|
|
12778
|
+
continue
|
|
12779
|
+
if len(row) == 1 and isinstance(row[0], dict) and _coerce_positive_int(row[0].get("queType")) == 24:
|
|
12780
|
+
for inner_row in row[0].get("innerQuestions", []) or []:
|
|
12781
|
+
remember_row(inner_row)
|
|
12782
|
+
continue
|
|
12783
|
+
remember_row(row)
|
|
12784
|
+
return lengths_by_que_id, lengths_by_title
|
|
12785
|
+
|
|
12786
|
+
|
|
12787
|
+
def _field_template_row_length(
|
|
12788
|
+
field: dict[str, Any],
|
|
12789
|
+
*,
|
|
12790
|
+
lengths_by_que_id: dict[int, int],
|
|
12791
|
+
lengths_by_title: dict[str, int],
|
|
12792
|
+
) -> int | None:
|
|
12793
|
+
que_id = _coerce_nonnegative_int(field.get("que_id"))
|
|
12794
|
+
if que_id is not None and que_id in lengths_by_que_id:
|
|
12795
|
+
return lengths_by_que_id[que_id]
|
|
12796
|
+
template = field.get("_question_template")
|
|
12797
|
+
if isinstance(template, dict):
|
|
12798
|
+
template_que_id = _coerce_nonnegative_int(template.get("queId"))
|
|
12799
|
+
if template_que_id is not None and template_que_id in lengths_by_que_id:
|
|
12800
|
+
return lengths_by_que_id[template_que_id]
|
|
12801
|
+
template_title = str(template.get("queTitle") or "").strip()
|
|
12802
|
+
if template_title and template_title in lengths_by_title:
|
|
12803
|
+
return lengths_by_title[template_title]
|
|
12804
|
+
field_name = str(field.get("name") or "").strip()
|
|
12805
|
+
if field_name and field_name in lengths_by_title:
|
|
12806
|
+
return lengths_by_title[field_name]
|
|
12807
|
+
return None
|
|
12808
|
+
|
|
12809
|
+
|
|
12810
|
+
def _row_needs_width_reflow(expected_template_lengths: list[int], current_row_length: int) -> bool:
|
|
12811
|
+
if current_row_length <= 0:
|
|
12812
|
+
return False
|
|
12813
|
+
return any(length != current_row_length for length in expected_template_lengths)
|
|
12814
|
+
|
|
12815
|
+
|
|
12756
12816
|
def _materialize_preserved_question(field: dict[str, Any]) -> dict[str, Any] | None:
|
|
12757
12817
|
template = deepcopy(field.get("_question_template"))
|
|
12758
12818
|
if not isinstance(template, dict):
|
|
@@ -12802,12 +12862,21 @@ def _field_to_question(field: dict[str, Any], *, temp_id: int) -> dict[str, Any]
|
|
|
12802
12862
|
},
|
|
12803
12863
|
temp_id,
|
|
12804
12864
|
)
|
|
12865
|
+
relation_config_explicit = bool(field.get("_relation_config_explicit"))
|
|
12805
12866
|
relation_question_template = (
|
|
12806
12867
|
deepcopy(field.get("_question_template"))
|
|
12807
12868
|
if field.get("type") == FieldType.relation.value and isinstance(field.get("_question_template"), dict)
|
|
12808
12869
|
else None
|
|
12809
12870
|
)
|
|
12810
|
-
question =
|
|
12871
|
+
question = (
|
|
12872
|
+
relation_question_template
|
|
12873
|
+
if relation_question_template is not None and not relation_config_explicit
|
|
12874
|
+
else built_question
|
|
12875
|
+
)
|
|
12876
|
+
if relation_config_explicit and relation_question_template is not None:
|
|
12877
|
+
for key in ("queOriginType", "relationDisplayMode", "customRenderConfig"):
|
|
12878
|
+
if key in relation_question_template:
|
|
12879
|
+
question[key] = deepcopy(relation_question_template[key])
|
|
12811
12880
|
if _coerce_nonnegative_int(field.get("que_id")) is not None:
|
|
12812
12881
|
question["queId"] = field["que_id"]
|
|
12813
12882
|
question.pop("queTempId", None)
|
|
@@ -12826,19 +12895,28 @@ def _field_to_question(field: dict[str, Any], *, temp_id: int) -> dict[str, Any]
|
|
|
12826
12895
|
if field.get("type") == FieldType.relation.value:
|
|
12827
12896
|
preserved_reference = (
|
|
12828
12897
|
deepcopy(field.get("_reference_config_template"))
|
|
12829
|
-
if not
|
|
12898
|
+
if not relation_config_explicit and isinstance(field.get("_reference_config_template"), dict)
|
|
12830
12899
|
else None
|
|
12831
12900
|
)
|
|
12832
12901
|
if preserved_reference is not None:
|
|
12833
12902
|
preserved_reference["referAppKey"] = field.get("target_app_key")
|
|
12834
12903
|
question["referenceConfig"] = preserved_reference
|
|
12835
12904
|
else:
|
|
12836
|
-
reference =
|
|
12905
|
+
reference = (
|
|
12906
|
+
deepcopy(built_question.get("referenceConfig"))
|
|
12907
|
+
if relation_config_explicit and isinstance(built_question.get("referenceConfig"), dict)
|
|
12908
|
+
else deepcopy(question.get("referenceConfig"))
|
|
12909
|
+
if isinstance(question.get("referenceConfig"), dict)
|
|
12910
|
+
else {}
|
|
12911
|
+
)
|
|
12837
12912
|
built_reference = (
|
|
12838
12913
|
deepcopy(built_question.get("referenceConfig"))
|
|
12839
12914
|
if isinstance(built_question.get("referenceConfig"), dict)
|
|
12840
12915
|
else {}
|
|
12841
12916
|
)
|
|
12917
|
+
if relation_config_explicit:
|
|
12918
|
+
for stale_key in ("customButtonText", "customAdvancedSetting", "configShowForm", "dataShowForm"):
|
|
12919
|
+
reference.pop(stale_key, None)
|
|
12842
12920
|
for key in (
|
|
12843
12921
|
"referQueId",
|
|
12844
12922
|
"referQuestions",
|
|
@@ -13007,6 +13085,7 @@ def _build_form_payload_for_edit_fields(
|
|
|
13007
13085
|
question_relations: list[dict[str, Any]] | None = None,
|
|
13008
13086
|
) -> dict[str, Any]:
|
|
13009
13087
|
_, section_templates = _extract_question_templates(current_schema)
|
|
13088
|
+
template_row_lengths_by_que_id, template_row_lengths_by_title = _extract_template_row_lengths(current_schema)
|
|
13010
13089
|
fields_by_name = {
|
|
13011
13090
|
str(field.get("name") or ""): field
|
|
13012
13091
|
for field in fields
|
|
@@ -13017,18 +13096,26 @@ def _build_form_payload_for_edit_fields(
|
|
|
13017
13096
|
|
|
13018
13097
|
for row in layout.get("root_rows", []) or []:
|
|
13019
13098
|
questions: list[dict[str, Any]] = []
|
|
13099
|
+
expected_template_lengths: list[int] = []
|
|
13020
13100
|
row_preserved = True
|
|
13021
13101
|
for name in row:
|
|
13022
13102
|
field = fields_by_name.get(str(name))
|
|
13023
13103
|
if field is None:
|
|
13024
13104
|
continue
|
|
13105
|
+
template_row_length = _field_template_row_length(
|
|
13106
|
+
field,
|
|
13107
|
+
lengths_by_que_id=template_row_lengths_by_que_id,
|
|
13108
|
+
lengths_by_title=template_row_lengths_by_title,
|
|
13109
|
+
)
|
|
13110
|
+
if template_row_length is not None:
|
|
13111
|
+
expected_template_lengths.append(template_row_length)
|
|
13025
13112
|
question, preserved = _materialize_edit_question(field, temp_id=temp_id)
|
|
13026
13113
|
questions.append(question)
|
|
13027
13114
|
row_preserved = row_preserved and preserved
|
|
13028
13115
|
temp_id -= 100
|
|
13029
13116
|
if not questions:
|
|
13030
13117
|
continue
|
|
13031
|
-
if not row_preserved:
|
|
13118
|
+
if not row_preserved or _row_needs_width_reflow(expected_template_lengths, len(questions)):
|
|
13032
13119
|
_apply_row_widths(questions)
|
|
13033
13120
|
form_rows.append(questions)
|
|
13034
13121
|
|
|
@@ -13036,18 +13123,26 @@ def _build_form_payload_for_edit_fields(
|
|
|
13036
13123
|
inner_rows: list[list[dict[str, Any]]] = []
|
|
13037
13124
|
for row in section.get("rows", []) or []:
|
|
13038
13125
|
questions: list[dict[str, Any]] = []
|
|
13126
|
+
expected_template_lengths: list[int] = []
|
|
13039
13127
|
row_preserved = True
|
|
13040
13128
|
for name in row:
|
|
13041
13129
|
field = fields_by_name.get(str(name))
|
|
13042
13130
|
if field is None:
|
|
13043
13131
|
continue
|
|
13132
|
+
template_row_length = _field_template_row_length(
|
|
13133
|
+
field,
|
|
13134
|
+
lengths_by_que_id=template_row_lengths_by_que_id,
|
|
13135
|
+
lengths_by_title=template_row_lengths_by_title,
|
|
13136
|
+
)
|
|
13137
|
+
if template_row_length is not None:
|
|
13138
|
+
expected_template_lengths.append(template_row_length)
|
|
13044
13139
|
question, preserved = _materialize_edit_question(field, temp_id=temp_id)
|
|
13045
13140
|
questions.append(question)
|
|
13046
13141
|
row_preserved = row_preserved and preserved
|
|
13047
13142
|
temp_id -= 100
|
|
13048
13143
|
if not questions:
|
|
13049
13144
|
continue
|
|
13050
|
-
if not row_preserved:
|
|
13145
|
+
if not row_preserved or _row_needs_width_reflow(expected_template_lengths, len(questions)):
|
|
13051
13146
|
_apply_row_widths(questions)
|
|
13052
13147
|
inner_rows.append(questions)
|
|
13053
13148
|
if not inner_rows:
|