@qingflow-tech/qingflow-app-builder-mcp 1.0.6 → 1.0.8

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.
@@ -17,7 +17,9 @@ from ..config import DEFAULT_PROFILE
17
17
  from ..errors import QingflowApiError
18
18
  from ..json_types import JSONObject
19
19
  from ..builder_facade.models import (
20
+ AssociatedResourcesApplyRequest,
20
21
  ChartApplyRequest,
22
+ CustomButtonsApplyRequest,
21
23
  CustomButtonPatch,
22
24
  FIELD_TYPE_ID_ALIASES,
23
25
  FieldPatch,
@@ -32,6 +34,7 @@ from ..builder_facade.models import (
32
34
  LayoutPreset,
33
35
  LayoutSectionPatch,
34
36
  PortalApplyRequest,
37
+ PublicButtonPlacement,
35
38
  PublicButtonTriggerAction,
36
39
  PublicFieldType,
37
40
  PublicRelationMode,
@@ -40,6 +43,7 @@ from ..builder_facade.models import (
40
43
  SchemaPlanRequest,
41
44
  VisibilityPatch,
42
45
  ViewFilterOperator,
46
+ ViewPartialPatch,
43
47
  ViewUpsertPatch,
44
48
  ViewsPreset,
45
49
  ViewsPlanRequest,
@@ -57,6 +61,14 @@ from .solution_tools import SolutionTools
57
61
  from .view_tools import ViewTools
58
62
  from .workflow_tools import WorkflowTools
59
63
 
64
+
65
+ def _normalize_builder_view_key(value: str) -> str:
66
+ raw = str(value or "").strip()
67
+ if raw.startswith("custom:"):
68
+ return raw.split(":", 1)[1].strip()
69
+ return raw
70
+
71
+
60
72
  PUBLIC_STABLE_FLOW_NODE_TYPES = ["start", "approve", "fill", "copy", "webhook", "end"]
61
73
 
62
74
 
@@ -220,33 +232,42 @@ class AiBuilderTools(ToolBase):
220
232
  return self.button_style_catalog_get(profile=profile)
221
233
 
222
234
  @mcp.tool()
223
- def app_custom_button_list(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
224
- return self.app_custom_button_list(profile=profile, app_key=app_key)
225
-
226
- @mcp.tool()
227
- def app_custom_button_get(profile: str = DEFAULT_PROFILE, app_key: str = "", button_id: int = 0) -> JSONObject:
228
- return self.app_custom_button_get(profile=profile, app_key=app_key, button_id=button_id)
229
-
230
- @mcp.tool()
231
- def app_custom_button_create(
235
+ def app_custom_buttons_apply(
232
236
  profile: str = DEFAULT_PROFILE,
233
237
  app_key: str = "",
234
- payload: JSONObject | None = None,
238
+ upsert_buttons: list[JSONObject] | None = None,
239
+ patch_buttons: list[JSONObject] | None = None,
240
+ remove_buttons: list[JSONObject] | None = None,
241
+ view_configs: list[JSONObject] | None = None,
235
242
  ) -> JSONObject:
236
- return self.app_custom_button_create(profile=profile, app_key=app_key, payload=payload or {})
243
+ return self.app_custom_buttons_apply(
244
+ profile=profile,
245
+ app_key=app_key,
246
+ upsert_buttons=upsert_buttons or [],
247
+ patch_buttons=patch_buttons or [],
248
+ remove_buttons=remove_buttons or [],
249
+ view_configs=view_configs or [],
250
+ )
237
251
 
238
252
  @mcp.tool()
239
- def app_custom_button_update(
253
+ def app_associated_resources_apply(
240
254
  profile: str = DEFAULT_PROFILE,
241
255
  app_key: str = "",
242
- button_id: int = 0,
243
- payload: JSONObject | None = None,
256
+ upsert_resources: list[JSONObject] | None = None,
257
+ patch_resources: list[JSONObject] | None = None,
258
+ remove_associated_item_ids: list[int] | None = None,
259
+ reorder_associated_item_ids: list[int] | None = None,
260
+ view_configs: list[JSONObject] | None = None,
244
261
  ) -> JSONObject:
245
- return self.app_custom_button_update(profile=profile, app_key=app_key, button_id=button_id, payload=payload or {})
246
-
247
- @mcp.tool()
248
- def app_custom_button_delete(profile: str = DEFAULT_PROFILE, app_key: str = "", button_id: int = 0) -> JSONObject:
249
- return self.app_custom_button_delete(profile=profile, app_key=app_key, button_id=button_id)
262
+ return self.app_associated_resources_apply(
263
+ profile=profile,
264
+ app_key=app_key,
265
+ upsert_resources=upsert_resources or [],
266
+ patch_resources=patch_resources or [],
267
+ remove_associated_item_ids=remove_associated_item_ids or [],
268
+ reorder_associated_item_ids=reorder_associated_item_ids or [],
269
+ view_configs=view_configs or [],
270
+ )
250
271
 
251
272
  @mcp.tool()
252
273
  def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
@@ -387,6 +408,7 @@ class AiBuilderTools(ToolBase):
387
408
  app_key: str = "",
388
409
  publish: bool = True,
389
410
  upsert_views: list[JSONObject] | None = None,
411
+ patch_views: list[JSONObject] | None = None,
390
412
  remove_views: list[str] | None = None,
391
413
  ) -> JSONObject:
392
414
  return self.app_views_apply(
@@ -394,6 +416,7 @@ class AiBuilderTools(ToolBase):
394
416
  app_key=app_key,
395
417
  publish=publish,
396
418
  upsert_views=upsert_views or [],
419
+ patch_views=patch_views or [],
397
420
  remove_views=remove_views or [],
398
421
  )
399
422
 
@@ -402,6 +425,7 @@ class AiBuilderTools(ToolBase):
402
425
  profile: str = DEFAULT_PROFILE,
403
426
  app_key: str = "",
404
427
  upsert_charts: list[JSONObject] | None = None,
428
+ patch_charts: list[JSONObject] | None = None,
405
429
  remove_chart_ids: list[str] | None = None,
406
430
  reorder_chart_ids: list[str] | None = None,
407
431
  ) -> JSONObject:
@@ -409,6 +433,7 @@ class AiBuilderTools(ToolBase):
409
433
  profile=profile,
410
434
  app_key=app_key,
411
435
  upsert_charts=upsert_charts or [],
436
+ patch_charts=patch_charts or [],
412
437
  remove_chart_ids=remove_chart_ids or [],
413
438
  reorder_chart_ids=reorder_chart_ids or [],
414
439
  )
@@ -867,6 +892,119 @@ class AiBuilderTools(ToolBase):
867
892
  suggested_next_call={"tool_name": "button_style_catalog_get", "arguments": {"profile": profile}},
868
893
  )
869
894
 
895
+ @tool_cn_name("应用按钮声明式应用")
896
+ def app_custom_buttons_apply(
897
+ self,
898
+ *,
899
+ profile: str,
900
+ app_key: str,
901
+ upsert_buttons: list[JSONObject],
902
+ patch_buttons: list[JSONObject] | None = None,
903
+ remove_buttons: list[JSONObject],
904
+ view_configs: list[JSONObject] | None = None,
905
+ ) -> JSONObject:
906
+ """执行应用按钮 apply 逻辑。"""
907
+ raw_request = {
908
+ "app_key": app_key,
909
+ "upsert_buttons": upsert_buttons,
910
+ "patch_buttons": patch_buttons or [],
911
+ "remove_buttons": remove_buttons,
912
+ "view_configs": view_configs or [],
913
+ }
914
+ try:
915
+ request = CustomButtonsApplyRequest.model_validate(raw_request)
916
+ except ValidationError as exc:
917
+ return _validation_failure(
918
+ str(exc),
919
+ tool_name="app_custom_buttons_apply",
920
+ exc=exc,
921
+ suggested_next_call={
922
+ "tool_name": "app_custom_buttons_apply",
923
+ "arguments": {
924
+ "profile": profile,
925
+ "app_key": app_key or "APP_KEY",
926
+ "upsert_buttons": [
927
+ {
928
+ "button_text": "同步客户",
929
+ "style_preset": "primary_blue",
930
+ "button_icon": "ex-switch",
931
+ "trigger_action": "link",
932
+ "trigger_link_url": "https://example.com",
933
+ }
934
+ ],
935
+ "remove_buttons": [],
936
+ "view_configs": [],
937
+ },
938
+ },
939
+ )
940
+ normalized_args = request.model_dump(mode="json")
941
+ return _safe_tool_call(
942
+ lambda: self._facade.app_custom_buttons_apply(profile=profile, request=request),
943
+ error_code="CUSTOM_BUTTONS_APPLY_FAILED",
944
+ normalized_args=normalized_args,
945
+ suggested_next_call={"tool_name": "app_custom_buttons_apply", "arguments": {"profile": profile, **normalized_args}},
946
+ )
947
+
948
+ @tool_cn_name("应用关联资源声明式应用")
949
+ def app_associated_resources_apply(
950
+ self,
951
+ *,
952
+ profile: str,
953
+ app_key: str,
954
+ upsert_resources: list[JSONObject],
955
+ remove_associated_item_ids: list[int],
956
+ reorder_associated_item_ids: list[int],
957
+ view_configs: list[JSONObject],
958
+ patch_resources: list[JSONObject] | None = None,
959
+ ) -> JSONObject:
960
+ """执行应用关联资源 apply 逻辑。"""
961
+ raw_request = {
962
+ "app_key": app_key,
963
+ "upsert_resources": upsert_resources,
964
+ "patch_resources": patch_resources or [],
965
+ "remove_associated_item_ids": remove_associated_item_ids,
966
+ "reorder_associated_item_ids": reorder_associated_item_ids,
967
+ "view_configs": view_configs,
968
+ }
969
+ try:
970
+ request = AssociatedResourcesApplyRequest.model_validate(raw_request)
971
+ except ValidationError as exc:
972
+ return _validation_failure(
973
+ str(exc),
974
+ tool_name="app_associated_resources_apply",
975
+ exc=exc,
976
+ suggested_next_call={
977
+ "tool_name": "app_associated_resources_apply",
978
+ "arguments": {
979
+ "profile": profile,
980
+ "app_key": app_key or "APP_KEY",
981
+ "upsert_resources": [
982
+ {
983
+ "client_key": "customer_view",
984
+ "graph_type": "view",
985
+ "target_app_key": "TARGET_APP",
986
+ "view_key": "VIEW_KEY",
987
+ }
988
+ ],
989
+ "view_configs": [
990
+ {
991
+ "view_key": "MAIN_VIEW",
992
+ "visible": True,
993
+ "limit_type": "select",
994
+ "associated_item_refs": ["customer_view"],
995
+ }
996
+ ],
997
+ },
998
+ },
999
+ )
1000
+ normalized_args = request.model_dump(mode="json", exclude_none=True)
1001
+ return _safe_tool_call(
1002
+ lambda: self._facade.app_associated_resources_apply(profile=profile, request=request),
1003
+ error_code="ASSOCIATED_RESOURCES_APPLY_FAILED",
1004
+ normalized_args=normalized_args,
1005
+ suggested_next_call={"tool_name": "app_associated_resources_apply", "arguments": {"profile": profile, **normalized_args}},
1006
+ )
1007
+
870
1008
  @tool_cn_name("应用按钮列表")
871
1009
  def app_custom_button_list(self, *, profile: str, app_key: str) -> JSONObject:
872
1010
  """执行应用相关逻辑。"""
@@ -909,7 +1047,10 @@ class AiBuilderTools(ToolBase):
909
1047
  "style_preset": "primary_blue",
910
1048
  "button_icon": "ex-plus-circle",
911
1049
  "trigger_action": "addData",
912
- "trigger_add_data_config": {"related_app_key": "TARGET_APP_KEY", "que_relation": []},
1050
+ "trigger_add_data_config": {
1051
+ "target_app_key": "TARGET_APP_KEY",
1052
+ "field_mappings": [{"source_field": "客户名称", "target_field": "客户"}],
1053
+ },
913
1054
  },
914
1055
  },
915
1056
  },
@@ -1152,7 +1293,7 @@ class AiBuilderTools(ToolBase):
1152
1293
  @tool_cn_name("视图详情查询")
1153
1294
  def view_get(self, *, profile: str, view_key: str = "", viewgraph_key: str = "") -> JSONObject:
1154
1295
  """执行视图相关逻辑。"""
1155
- resolved_view_key = str(view_key or viewgraph_key or "").strip()
1296
+ resolved_view_key = _normalize_builder_view_key(str(view_key or viewgraph_key or "").strip())
1156
1297
  normalized_args = {"view_key": resolved_view_key}
1157
1298
  return _safe_tool_call(
1158
1299
  lambda: self._facade.view_get(profile=profile, view_key=resolved_view_key),
@@ -1733,6 +1874,7 @@ class AiBuilderTools(ToolBase):
1733
1874
  app_key: str,
1734
1875
  publish: bool = True,
1735
1876
  upsert_views: list[JSONObject],
1877
+ patch_views: list[JSONObject] | None = None,
1736
1878
  remove_views: list[str],
1737
1879
  ) -> JSONObject:
1738
1880
  """执行应用相关逻辑。"""
@@ -1741,6 +1883,7 @@ class AiBuilderTools(ToolBase):
1741
1883
  app_key=app_key,
1742
1884
  publish=publish,
1743
1885
  upsert_views=upsert_views,
1886
+ patch_views=patch_views or [],
1744
1887
  remove_views=remove_views,
1745
1888
  )
1746
1889
  return self._retry_after_self_lock_release(
@@ -1752,6 +1895,7 @@ class AiBuilderTools(ToolBase):
1752
1895
  publish=publish,
1753
1896
  remove_views=remove_views,
1754
1897
  upsert_views=upsert_views,
1898
+ patch_views=patch_views or [],
1755
1899
  ),
1756
1900
  )
1757
1901
 
@@ -1762,9 +1906,53 @@ class AiBuilderTools(ToolBase):
1762
1906
  app_key: str,
1763
1907
  publish: bool = True,
1764
1908
  upsert_views: list[JSONObject],
1909
+ patch_views: list[JSONObject],
1765
1910
  remove_views: list[str],
1766
1911
  ) -> JSONObject:
1767
1912
  """执行内部辅助逻辑。"""
1913
+ if patch_views:
1914
+ try:
1915
+ parsed_views = [ViewUpsertPatch.model_validate(item) for item in (upsert_views or [])]
1916
+ parsed_patch_views = [ViewPartialPatch.model_validate(item) for item in patch_views]
1917
+ except ValidationError as exc:
1918
+ return _visibility_validation_failure(
1919
+ str(exc),
1920
+ tool_name="app_views_apply",
1921
+ exc=exc,
1922
+ suggested_next_call={
1923
+ "tool_name": "app_views_apply",
1924
+ "arguments": {
1925
+ "profile": profile,
1926
+ "app_key": app_key,
1927
+ "publish": publish,
1928
+ "upsert_views": upsert_views or [],
1929
+ "patch_views": [
1930
+ {"view_key": "VIEW_KEY", "set": {"query_conditions": {"enabled": True, "rows": [["字段A"]]}}},
1931
+ ],
1932
+ "remove_views": remove_views or [],
1933
+ },
1934
+ },
1935
+ )
1936
+ normalized_args = {
1937
+ "app_key": app_key,
1938
+ "publish": publish,
1939
+ "upsert_views": [view.model_dump(mode="json") for view in parsed_views],
1940
+ "patch_views": [patch.model_dump(mode="json") for patch in parsed_patch_views],
1941
+ "remove_views": list(remove_views or []),
1942
+ }
1943
+ return _safe_tool_call(
1944
+ lambda: self._facade.app_views_apply(
1945
+ profile=profile,
1946
+ app_key=app_key,
1947
+ publish=publish,
1948
+ upsert_views=parsed_views,
1949
+ patch_views=parsed_patch_views,
1950
+ remove_views=list(remove_views or []),
1951
+ ),
1952
+ error_code="VIEWS_APPLY_FAILED",
1953
+ normalized_args=normalized_args,
1954
+ suggested_next_call={"tool_name": "app_views_apply", "arguments": {"profile": profile, **normalized_args}},
1955
+ )
1768
1956
  plan_result = self._rewrite_plan_result_for_apply(
1769
1957
  result=self.app_views_plan(
1770
1958
  profile=profile,
@@ -1813,6 +2001,7 @@ class AiBuilderTools(ToolBase):
1813
2001
  app_key=str(plan_args.get("app_key") or app_key),
1814
2002
  publish=publish,
1815
2003
  upsert_views=parsed_views,
2004
+ patch_views=[],
1816
2005
  remove_views=list(plan_args.get("remove_views") or remove_views),
1817
2006
  ),
1818
2007
  error_code="VIEWS_APPLY_FAILED",
@@ -1827,6 +2016,7 @@ class AiBuilderTools(ToolBase):
1827
2016
  profile: str,
1828
2017
  app_key: str,
1829
2018
  upsert_charts: list[JSONObject],
2019
+ patch_charts: list[JSONObject] | None = None,
1830
2020
  remove_chart_ids: list[str],
1831
2021
  reorder_chart_ids: list[str],
1832
2022
  ) -> JSONObject:
@@ -1835,6 +2025,7 @@ class AiBuilderTools(ToolBase):
1835
2025
  profile=profile,
1836
2026
  app_key=app_key,
1837
2027
  upsert_charts=upsert_charts,
2028
+ patch_charts=patch_charts or [],
1838
2029
  remove_chart_ids=remove_chart_ids,
1839
2030
  reorder_chart_ids=reorder_chart_ids,
1840
2031
  )
@@ -1848,6 +2039,7 @@ class AiBuilderTools(ToolBase):
1848
2039
  upsert_charts: list[JSONObject],
1849
2040
  remove_chart_ids: list[str],
1850
2041
  reorder_chart_ids: list[str],
2042
+ patch_charts: list[JSONObject] | None = None,
1851
2043
  ) -> JSONObject:
1852
2044
  """执行应用相关逻辑。"""
1853
2045
  try:
@@ -1855,6 +2047,7 @@ class AiBuilderTools(ToolBase):
1855
2047
  {
1856
2048
  "app_key": app_key,
1857
2049
  "upsert_charts": upsert_charts or [],
2050
+ "patch_charts": patch_charts or [],
1858
2051
  "remove_chart_ids": remove_chart_ids or [],
1859
2052
  "reorder_chart_ids": reorder_chart_ids or [],
1860
2053
  }
@@ -2430,6 +2623,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2430
2623
  "allowed_values": deepcopy(_VISIBILITY_ALLOWED_VALUES),
2431
2624
  "execution_notes": [
2432
2625
  "create or update package metadata, visibility, grouping, and ordering in one call",
2626
+ "metadata keys omitted on update are preserved",
2433
2627
  "package_id maps internally to backend tagId; do not use tag_id in public calls",
2434
2628
  "items is a full package layout tree; omitting existing app/portal items is blocked unless allow_detach=true",
2435
2629
  "item shapes: {type:'app', app_key}, {type:'portal', dash_key}, or {type:'group', group_id?, name, items:[...]}",
@@ -2572,115 +2766,271 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2572
2766
  "profile": "default",
2573
2767
  },
2574
2768
  },
2575
- "app_custom_button_list": {
2576
- "allowed_keys": ["app_key"],
2577
- "aliases": {},
2578
- "allowed_values": {},
2579
- "minimal_example": {
2580
- "profile": "default",
2581
- "app_key": "APP_KEY",
2582
- },
2583
- },
2584
- "app_custom_button_get": {
2585
- "allowed_keys": ["app_key", "button_id"],
2586
- "aliases": {"buttonId": "button_id"},
2587
- "allowed_values": {},
2588
- "minimal_example": {
2589
- "profile": "default",
2590
- "app_key": "APP_KEY",
2591
- "button_id": 1001,
2592
- },
2593
- },
2594
- "app_custom_button_create": {
2595
- "allowed_keys": ["app_key", "payload"],
2769
+ "app_custom_buttons_apply": {
2770
+ "allowed_keys": [
2771
+ "app_key",
2772
+ "upsert_buttons",
2773
+ "patch_buttons",
2774
+ "remove_buttons",
2775
+ "view_configs",
2776
+ "upsert_buttons[].client_key",
2777
+ "upsert_buttons[].button_id",
2778
+ "upsert_buttons[].button_text",
2779
+ "upsert_buttons[].style_preset",
2780
+ "upsert_buttons[].background_color",
2781
+ "upsert_buttons[].text_color",
2782
+ "upsert_buttons[].button_icon",
2783
+ "upsert_buttons[].trigger_action",
2784
+ "upsert_buttons[].trigger_link_url",
2785
+ "upsert_buttons[].trigger_add_data_config",
2786
+ "upsert_buttons[].trigger_add_data_config.target_app_key",
2787
+ "upsert_buttons[].trigger_add_data_config.field_mappings",
2788
+ "upsert_buttons[].trigger_add_data_config.field_mappings[].source_field",
2789
+ "upsert_buttons[].trigger_add_data_config.field_mappings[].target_field",
2790
+ "upsert_buttons[].trigger_add_data_config.default_values",
2791
+ "patch_buttons[].button_id",
2792
+ "patch_buttons[].button_text",
2793
+ "patch_buttons[].set",
2794
+ "patch_buttons[].unset",
2795
+ "view_configs[].view_key",
2796
+ "view_configs[].mode",
2797
+ "view_configs[].buttons",
2798
+ "view_configs[].buttons[].button_ref",
2799
+ "view_configs[].buttons[].placement",
2800
+ "view_configs[].buttons[].primary",
2801
+ "view_configs[].buttons[].button_limit",
2802
+ "view_configs[].buttons[].button_formula",
2803
+ "view_configs[].buttons[].button_formula_type",
2804
+ "view_configs[].buttons[].print_tpls",
2805
+ "remove_buttons[].button_id",
2806
+ "remove_buttons[].button_text",
2807
+ ],
2596
2808
  "aliases": {
2597
- "payload.buttonText": "payload.button_text",
2598
- "payload.stylePreset": "payload.style_preset",
2599
- "payload.backgroundColor": "payload.background_color",
2600
- "payload.textColor": "payload.text_color",
2601
- "payload.buttonIcon": "payload.button_icon",
2602
- "payload.triggerAction": "payload.trigger_action",
2603
- "payload.triggerLinkUrl": "payload.trigger_link_url",
2604
- "payload.triggerAddDataConfig": "payload.trigger_add_data_config",
2605
- "payload.externalQrobotConfig": "payload.external_qrobot_config",
2606
- "payload.customButtonExternalQRobotRelationVO": "payload.external_qrobot_config",
2607
- "payload.triggerWingsConfig": "payload.trigger_wings_config",
2809
+ "upsertButtons": "upsert_buttons",
2810
+ "patchButtons": "patch_buttons",
2811
+ "removeButtons": "remove_buttons",
2812
+ "viewConfigs": "view_configs",
2813
+ "clientKey": "client_key",
2814
+ "buttonId": "button_id",
2815
+ "buttonText": "button_text",
2816
+ "stylePreset": "style_preset",
2817
+ "backgroundColor": "background_color",
2818
+ "textColor": "text_color",
2819
+ "buttonIcon": "button_icon",
2820
+ "triggerAction": "trigger_action",
2821
+ "triggerLinkUrl": "trigger_link_url",
2822
+ "triggerAddDataConfig": "trigger_add_data_config",
2823
+ "targetAppKey": "trigger_add_data_config.target_app_key",
2824
+ "fieldMappings": "trigger_add_data_config.field_mappings",
2825
+ "defaultValues": "trigger_add_data_config.default_values",
2826
+ "mode": "view_configs[].mode",
2827
+ "applyMode": "view_configs[].mode",
2828
+ "buttonRef": "view_configs[].buttons[].button_ref",
2829
+ "beingMain": "view_configs[].buttons[].primary",
2830
+ "buttonLimit": "view_configs[].buttons[].button_limit",
2831
+ "visibleWhen": "view_configs[].buttons[].button_limit",
2832
+ "buttonFormula": "view_configs[].buttons[].button_formula",
2833
+ "buttonFormulaType": "view_configs[].buttons[].button_formula_type",
2834
+ "printTpls": "view_configs[].buttons[].print_tpls",
2835
+ "externalQrobotConfig": "external_qrobot_config",
2836
+ "customButtonExternalQRobotRelationVO": "external_qrobot_config",
2837
+ "triggerWingsConfig": "trigger_wings_config",
2608
2838
  },
2609
2839
  "allowed_values": {
2610
- "payload.trigger_action": [member.value for member in PublicButtonTriggerAction],
2611
- "payload.style_preset": [item["key"] for item in BUTTON_STYLE_PRESETS],
2612
- "payload.button_icon": list(BUTTON_ICONS),
2613
- "payload.background_color": list(BUTTON_BACKGROUND_COLORS),
2614
- "payload.text_color": list(BUTTON_TEXT_COLORS),
2840
+ "upsert_buttons[].trigger_action": [member.value for member in PublicButtonTriggerAction],
2841
+ "upsert_buttons[].style_preset": [item["key"] for item in BUTTON_STYLE_PRESETS],
2842
+ "upsert_buttons[].button_icon": list(BUTTON_ICONS),
2843
+ "upsert_buttons[].background_color": list(BUTTON_BACKGROUND_COLORS),
2844
+ "upsert_buttons[].text_color": list(BUTTON_TEXT_COLORS),
2845
+ "view_configs[].mode": ["merge", "replace"],
2846
+ "view_configs[].buttons[].placement": [member.value for member in PublicButtonPlacement],
2615
2847
  },
2616
2848
  "execution_notes": [
2617
- "custom button writes now auto-publish the current app draft as a fixed closing step",
2849
+ "this is the default custom-button write path; old list/get/create/update/delete tools are hidden from the public agent surface",
2850
+ "use patch_buttons for partial parameter replacement on existing buttons; the tool reads current button detail, merges patch_buttons[].set/unset, then submits the backend full-save payload internally",
2851
+ "button_id targets an existing button; without button_id, button_text is used as an exact unique upsert key, otherwise a new button is created",
2852
+ "for addData buttons, use trigger_add_data_config.target_app_key plus field_mappings/default_values; field_mappings compiles to backend queRelation",
2853
+ "field_mappings.source_field accepts source schema fields and supported system fields: 数据ID/row_record_id/apply_id/_id maps to current record id (-17), 编号/record_number maps to visible record number (0)",
2854
+ "to fill a target relation field with the current source record, map source_field='数据ID' to the target relation field; default_values is for static constants, not dynamic current-record values",
2855
+ "do not write raw que_relation unless maintaining a legacy config; field_mappings/default_values and que_relation are mutually exclusive",
2856
+ "view_configs binds custom buttons into views in the same apply call; button_ref may be a same-call client_key, a button_id, or an exact unique existing button_text",
2857
+ "view_configs[].view_key is the raw builder view key from app_get.views[].view_key; do not pass record-data view_id values like custom:VIEW_KEY",
2858
+ "view_configs[].buttons is required in merge mode; omitting buttons is blocked to avoid no-op writes and accidental publish",
2859
+ "view_configs[].mode defaults to merge; use mode=replace or an explicit empty buttons list to replace/clear a view's custom button bindings",
2860
+ "advanced view button binding supports button_limit, button_formula, button_formula_type, and print_tpls when visibility or print-template behavior is required",
2861
+ "default placements are header and detail; header maps to frontend top buttons",
2862
+ "placement=list configures backend INSIDE row/list buttons; header maps to TOP and detail maps to DETAIL",
2863
+ "remove_buttons supports button_id or exact unique button_text",
2864
+ "all operations share one edit context and publish after at least one write succeeds; there is no draft-only mode for this tool",
2618
2865
  "background_color and text_color cannot both be white",
2619
- "payload accepts either style_preset + optional button_icon, or explicit button_icon/background_color/text_color from button_style_catalog_get",
2620
- "for addData buttons, put field mappings in payload.trigger_add_data_config.que_relation",
2621
2866
  ],
2622
2867
  "minimal_example": {
2623
2868
  "profile": "default",
2624
2869
  "app_key": "APP_KEY",
2625
- "payload": {
2626
- "button_text": "新增记录",
2627
- "style_preset": "primary_blue",
2628
- "button_icon": "ex-plus-circle",
2629
- "trigger_action": "link",
2630
- "trigger_link_url": "https://example.com",
2631
- },
2870
+ "upsert_buttons": [
2871
+ {
2872
+ "button_text": "同步客户",
2873
+ "style_preset": "primary_blue",
2874
+ "button_icon": "ex-switch",
2875
+ "trigger_action": "link",
2876
+ "trigger_link_url": "https://example.com",
2877
+ }
2878
+ ],
2879
+ "patch_buttons": [
2880
+ {
2881
+ "button_text": "同步客户",
2882
+ "set": {"trigger_link_url": "https://example.com/new"},
2883
+ }
2884
+ ],
2885
+ "remove_buttons": [{"button_text": "旧按钮"}],
2886
+ "view_configs": [],
2887
+ },
2888
+ "add_data_example": {
2889
+ "profile": "default",
2890
+ "app_key": "APP_KEY",
2891
+ "upsert_buttons": [
2892
+ {
2893
+ "client_key": "add_order",
2894
+ "button_text": "新增订单",
2895
+ "style_preset": "neutral_outline",
2896
+ "button_icon": "ex-plus-circle",
2897
+ "trigger_action": "addData",
2898
+ "trigger_add_data_config": {
2899
+ "target_app_key": "TARGET_APP",
2900
+ "field_mappings": [{"source_field": "客户名称", "target_field": "客户"}],
2901
+ "default_values": {"状态": "待提交"},
2902
+ },
2903
+ }
2904
+ ],
2905
+ "remove_buttons": [],
2906
+ "view_configs": [
2907
+ {
2908
+ "view_key": "VIEW_KEY",
2909
+ "buttons": [{"button_ref": "add_order", "placement": "detail", "primary": True}],
2910
+ }
2911
+ ],
2912
+ },
2913
+ "relation_current_record_example": {
2914
+ "profile": "default",
2915
+ "app_key": "EMPLOYEE_APP",
2916
+ "upsert_buttons": [
2917
+ {
2918
+ "client_key": "add_worklog",
2919
+ "button_text": "快捷添加工时",
2920
+ "style_preset": "neutral_outline",
2921
+ "button_icon": "ex-plus-circle",
2922
+ "trigger_action": "addData",
2923
+ "trigger_add_data_config": {
2924
+ "target_app_key": "WORKLOG_APP",
2925
+ "field_mappings": [{"source_field": "数据ID", "target_field": "关联员工"}],
2926
+ "default_values": {"状态": "待提交"},
2927
+ },
2928
+ }
2929
+ ],
2930
+ "view_configs": [
2931
+ {
2932
+ "view_key": "RAW_VIEW_KEY",
2933
+ "buttons": [{"button_ref": "add_worklog", "placement": "detail", "primary": True}],
2934
+ }
2935
+ ],
2632
2936
  },
2633
2937
  },
2634
- "app_custom_button_update": {
2635
- "allowed_keys": ["app_key", "button_id", "payload"],
2938
+ "app_associated_resources_apply": {
2939
+ "allowed_keys": [
2940
+ "app_key",
2941
+ "upsert_resources",
2942
+ "patch_resources",
2943
+ "remove_associated_item_ids",
2944
+ "reorder_associated_item_ids",
2945
+ "view_configs",
2946
+ "upsert_resources[].client_key",
2947
+ "upsert_resources[].associated_item_id",
2948
+ "upsert_resources[].graph_type",
2949
+ "upsert_resources[].target_app_key",
2950
+ "upsert_resources[].view_key",
2951
+ "upsert_resources[].chart_key",
2952
+ "upsert_resources[].report_source",
2953
+ "upsert_resources[].match_mappings",
2954
+ "upsert_resources[].match_mappings[].target_field",
2955
+ "upsert_resources[].match_mappings[].source_field",
2956
+ "upsert_resources[].match_mappings[].value",
2957
+ "upsert_resources[].match_mappings[].operator",
2958
+ "upsert_resources[].match_rules",
2959
+ "patch_resources[].associated_item_id",
2960
+ "patch_resources[].set",
2961
+ "patch_resources[].unset",
2962
+ "view_configs[].view_key",
2963
+ "view_configs[].visible",
2964
+ "view_configs[].limit_type",
2965
+ "view_configs[].associated_item_ids",
2966
+ "view_configs[].associated_item_refs",
2967
+ ],
2636
2968
  "aliases": {
2637
- "buttonId": "button_id",
2638
- "payload.buttonText": "payload.button_text",
2639
- "payload.stylePreset": "payload.style_preset",
2640
- "payload.backgroundColor": "payload.background_color",
2641
- "payload.textColor": "payload.text_color",
2642
- "payload.buttonIcon": "payload.button_icon",
2643
- "payload.triggerAction": "payload.trigger_action",
2644
- "payload.triggerLinkUrl": "payload.trigger_link_url",
2645
- "payload.triggerAddDataConfig": "payload.trigger_add_data_config",
2646
- "payload.externalQrobotConfig": "payload.external_qrobot_config",
2647
- "payload.customButtonExternalQRobotRelationVO": "payload.external_qrobot_config",
2648
- "payload.triggerWingsConfig": "payload.trigger_wings_config",
2969
+ "upsertResources": "upsert_resources",
2970
+ "patchResources": "patch_resources",
2971
+ "resources": "upsert_resources",
2972
+ "removeAssociatedItemIds": "remove_associated_item_ids",
2973
+ "reorderAssociatedItemIds": "reorder_associated_item_ids",
2974
+ "viewConfigs": "view_configs",
2975
+ "associatedItemId": "associated_item_id",
2976
+ "graphType": "graph_type",
2977
+ "targetAppKey": "target_app_key",
2978
+ "chartKey": "chart_key",
2979
+ "viewKey": "view_key",
2980
+ "viewgraphKey": "view_key",
2981
+ "reportSource": "report_source",
2982
+ "matchMappings": "match_mappings",
2983
+ "matchRules": "match_rules",
2984
+ "associatedItemRefs": "associated_item_refs",
2985
+ "associatedItemIds": "associated_item_ids",
2649
2986
  },
2650
2987
  "allowed_values": {
2651
- "payload.trigger_action": [member.value for member in PublicButtonTriggerAction],
2652
- "payload.style_preset": [item["key"] for item in BUTTON_STYLE_PRESETS],
2653
- "payload.button_icon": list(BUTTON_ICONS),
2654
- "payload.background_color": list(BUTTON_BACKGROUND_COLORS),
2655
- "payload.text_color": list(BUTTON_TEXT_COLORS),
2988
+ "upsert_resources[].graph_type": ["chart", "view"],
2989
+ "upsert_resources[].report_source": ["app", "dataset"],
2990
+ "view_configs[].limit_type": ["all", "select"],
2656
2991
  },
2657
2992
  "execution_notes": [
2658
- "custom button writes now auto-publish the current app draft as a fixed closing step",
2659
- "background_color and text_color cannot both be white",
2660
- "payload accepts either style_preset + optional button_icon, or explicit button_icon/background_color/text_color from button_style_catalog_get",
2661
- "for addData buttons, put field mappings in payload.trigger_add_data_config.que_relation",
2993
+ "this is the default associated report/view path; it manages both the app-level associated resource pool and per-view display config",
2994
+ "use patch_resources for partial parameter replacement on existing associated resources; the tool reads the current resource including backend-required raw fields, merges patch_resources[].set/unset, then submits the backend full-save payload internally",
2995
+ "associated_item_id is form_asos_chart.id from app_get.associated_resources[].associated_item_id; it is not chart_id, chart_key, or view_key",
2996
+ "before creating an associated resource, read app_get.associated_resources and reuse an existing item with patch_resources when target_app_key + view_key/chart_key already matches; repeated upsert_resources without associated_item_id can create duplicate associated items",
2997
+ "graph_type=view uses view_key and internally compiles to the Qingflow view source; graph_type=chart uses chart_key and defaults to report_source=app",
2998
+ "report_source=app maps to BI_QINGFLOW; report_source=dataset maps to BI_DATASET; do not pass raw backend sourceType",
2999
+ "use match_mappings for associated view/report filtering; dynamic conditions use source_field and static conditions use value",
3000
+ "match_mappings.source_field accepts source schema fields plus system fields 数据ID(-17) and 编号(0); match_mappings compiles to backend matchRules",
3001
+ "do not write raw match_rules unless preserving a legacy backend config; match_mappings and match_rules are mutually exclusive",
3002
+ "client_key only lets a view_config reference a resource created earlier in the same apply call through associated_item_refs; it is not persisted and cannot deduplicate later apply calls",
3003
+ "this tool publishes after at least one write succeeds; there is no draft-only mode",
3004
+ "visible=false hides the associated-resource area without clearing previous selected ids; visible=true with limit_type=all shows the whole app-level pool",
2662
3005
  ],
2663
3006
  "minimal_example": {
2664
3007
  "profile": "default",
2665
3008
  "app_key": "APP_KEY",
2666
- "button_id": 1001,
2667
- "payload": {
2668
- "button_text": "查看详情",
2669
- "style_preset": "neutral_outline",
2670
- "button_icon": "ex-edit",
2671
- "trigger_action": "link",
2672
- "trigger_link_url": "https://example.com/detail",
2673
- },
2674
- },
2675
- },
2676
- "app_custom_button_delete": {
2677
- "allowed_keys": ["app_key", "button_id"],
2678
- "aliases": {"buttonId": "button_id"},
2679
- "allowed_values": {},
2680
- "minimal_example": {
2681
- "profile": "default",
2682
- "app_key": "APP_KEY",
2683
- "button_id": 1001,
3009
+ "upsert_resources": [
3010
+ {
3011
+ "client_key": "customer_view",
3012
+ "graph_type": "view",
3013
+ "target_app_key": "TARGET_APP",
3014
+ "view_key": "VIEW_KEY",
3015
+ "match_mappings": [{"target_field": "关联员工", "source_field": "数据ID"}],
3016
+ }
3017
+ ],
3018
+ "patch_resources": [
3019
+ {
3020
+ "associated_item_id": 123,
3021
+ "set": {"match_mappings": [{"target_field": "状态", "value": "待提交"}]},
3022
+ }
3023
+ ],
3024
+ "remove_associated_item_ids": [],
3025
+ "reorder_associated_item_ids": [],
3026
+ "view_configs": [
3027
+ {
3028
+ "view_key": "MAIN_VIEW",
3029
+ "visible": True,
3030
+ "limit_type": "select",
3031
+ "associated_item_refs": ["customer_view"],
3032
+ }
3033
+ ],
2684
3034
  },
2685
3035
  },
2686
3036
  "app_schema_plan": {
@@ -2709,6 +3059,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2709
3059
  "field.customBtnTextStatus": "field.custom_button_text_enabled",
2710
3060
  "field.customBtnText": "field.custom_button_text",
2711
3061
  "field.subfieldUpdates": "field.subfield_updates",
3062
+ "field.asDataTitle": "field.as_data_title",
3063
+ "field.asDataCover": "field.as_data_cover",
2712
3064
  },
2713
3065
  "allowed_values": {
2714
3066
  "field.type": [member.value for member in PublicFieldType],
@@ -2728,7 +3080,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2728
3080
  "color": "emerald",
2729
3081
  "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE),
2730
3082
  "create_if_missing": True,
2731
- "add_fields": [{"name": "项目名称", "type": "text"}],
3083
+ "add_fields": [{"name": "项目名称", "type": "text", "as_data_title": True}],
2732
3084
  "update_fields": [],
2733
3085
  "remove_fields": [],
2734
3086
  },
@@ -2775,6 +3127,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2775
3127
  "field.customBtnTextStatus": "field.custom_button_text_enabled",
2776
3128
  "field.customBtnText": "field.custom_button_text",
2777
3129
  "field.subfieldUpdates": "field.subfield_updates",
3130
+ "field.asDataTitle": "field.as_data_title",
3131
+ "field.asDataCover": "field.as_data_cover",
2778
3132
  },
2779
3133
  "allowed_values": {
2780
3134
  "field.type": [member.value for member in PublicFieldType],
@@ -2791,6 +3145,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2791
3145
  "create mode: package_id + app_name + create_if_missing=true",
2792
3146
  "create mode defaults new app visibility to workspace/not when visibility is omitted; edit mode preserves current visibility when omitted",
2793
3147
  *_VISIBILITY_EXECUTION_NOTES,
3148
+ "update_fields is the field-level partial update path; it reads current form schema and preserves untouched field config",
2794
3149
  "multiple relation fields are backend-risky; read verification.relation_field_limit_verified and warnings before declaring the schema stable",
2795
3150
  "backend 49614 is normalized to MULTIPLE_RELATION_FIELDS_UNSUPPORTED with a workaround message",
2796
3151
  "relation_mode=multiple maps to referenceConfig.optionalDataNum=0",
@@ -2806,6 +3161,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2806
3161
  "code block outputs must be emitted through qf_output assignment; use qf_output = {...} or assign qf_output after building the result object, never const/let qf_output =",
2807
3162
  "builder automatically normalizes const/let qf_output assignments on write and rejects output-bound code blocks that still do not contain a valid qf_output assignment",
2808
3163
  "code_block_binding and q_linker_binding target fields are limited to text, long_text, number, amount, date, datetime, single_select, multi_select, and boolean",
3164
+ "data title is required: mark exactly one top-level field with as_data_title=true; use a readable name/number/date-like field",
3165
+ "data cover is optional: mark at most one top-level attachment field with as_data_cover=true",
2809
3166
  ],
2810
3167
  "minimal_example": {
2811
3168
  "profile": "default",
@@ -2816,7 +3173,10 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2816
3173
  "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE),
2817
3174
  "create_if_missing": True,
2818
3175
  "publish": True,
2819
- "add_fields": [{"name": "项目名称", "type": "text"}],
3176
+ "add_fields": [
3177
+ {"name": "项目名称", "type": "text", "as_data_title": True},
3178
+ {"name": "项目封面", "type": "attachment", "as_data_cover": True},
3179
+ ],
2820
3180
  "update_fields": [],
2821
3181
  "remove_fields": [],
2822
3182
  },
@@ -2985,6 +3345,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2985
3345
  },
2986
3346
  "allowed_values": {"mode": [member.value for member in LayoutApplyMode]},
2987
3347
  "execution_notes": [
3348
+ "mode=merge is the layout partial update path: mentioned sections are merged and unmentioned fields are preserved",
3349
+ "mode=replace is full layout replacement and should be used only when intentionally rewriting all sections",
2988
3350
  "layout verification is split into layout_verified and layout_summary_verified",
2989
3351
  "LAYOUT_SUMMARY_UNVERIFIED means raw form readback is stronger than the compact summary",
2990
3352
  ],
@@ -3062,6 +3424,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3062
3424
  ],
3063
3425
  "execution_notes": [
3064
3426
  "public flow building is intentionally limited to linear workflows",
3427
+ "app_flow_apply is replace-only; do not treat node snippets as partial patches",
3065
3428
  "branch and condition nodes are disabled because the backend workflow route is not front-end stable for these node types",
3066
3429
  "workflow verification only covers linear node structure in the public tool surface",
3067
3430
  ],
@@ -3085,7 +3448,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3085
3448
  },
3086
3449
  },
3087
3450
  "app_views_plan": {
3088
- "allowed_keys": ["app_key", "upsert_views", "remove_views", "preset", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility"],
3451
+ "allowed_keys": ["app_key", "upsert_views", "patch_views", "remove_views", "preset", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility", "upsert_views[].query_conditions", "patch_views[].view_key", "patch_views[].name", "patch_views[].set", "patch_views[].unset"],
3089
3452
  "aliases": {
3090
3453
  "fields": "columns",
3091
3454
  "column_names": "columns",
@@ -3096,6 +3459,10 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3096
3459
  "kanban": "board",
3097
3460
  "filter_rules": "filters",
3098
3461
  "filterRules": "filters",
3462
+ "queryConditions": "query_conditions",
3463
+ "query_condition": "query_conditions",
3464
+ "queryCondition": "query_conditions",
3465
+ "patchViews": "patch_views",
3099
3466
  "startField": "start_field",
3100
3467
  "endField": "end_field",
3101
3468
  "titleField": "title_field",
@@ -3118,6 +3485,10 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3118
3485
  },
3119
3486
  "execution_notes": [
3120
3487
  "upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
3488
+ "filters are saved fixed filters that apply when the view opens; query_conditions configure the frontend query panel and only apply after a user enters query values",
3489
+ "upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
3490
+ "use patch_views for partial parameter replacement on existing views; the tool reads current config, merges patch_views[].set/unset, then submits the backend full-save payload internally",
3491
+ "view associated report/view display is now configured through app_associated_resources_apply, not app_views_apply",
3121
3492
  "for multi-value operators such as in, pass values as a list; value may also be used as an alias when it already contains a list",
3122
3493
  *_VISIBILITY_EXECUTION_NOTES,
3123
3494
  ],
@@ -3127,6 +3498,41 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3127
3498
  "upsert_views": [{"name": "全部数据", "type": "table", "columns": ["项目名称"], "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE)}],
3128
3499
  "remove_views": [],
3129
3500
  },
3501
+ "query_conditions_example": {
3502
+ "profile": "default",
3503
+ "app_key": "APP_KEY",
3504
+ "patch_views": [
3505
+ {
3506
+ "view_key": "VIEW_KEY",
3507
+ "set": {
3508
+ "query_conditions": {
3509
+ "enabled": True,
3510
+ "rows": [["客户名称", "负责人"], ["创建时间"]],
3511
+ }
3512
+ },
3513
+ }
3514
+ ],
3515
+ "remove_views": [],
3516
+ },
3517
+ "full_upsert_query_conditions_example": {
3518
+ "profile": "default",
3519
+ "app_key": "APP_KEY",
3520
+ "upsert_views": [
3521
+ {
3522
+ "name": "客户查询视图",
3523
+ "type": "table",
3524
+ "columns": ["客户名称", "负责人", "客户状态", "创建时间"],
3525
+ "filters": [{"field_name": "客户状态", "operator": "eq", "value": "有效"}],
3526
+ "query_conditions": {
3527
+ "enabled": True,
3528
+ "exact": False,
3529
+ "hide_before_query": False,
3530
+ "rows": [["客户名称", "负责人"], ["创建时间"]],
3531
+ },
3532
+ }
3533
+ ],
3534
+ "remove_views": [],
3535
+ },
3130
3536
  "gantt_example": {
3131
3537
  "profile": "default",
3132
3538
  "app_key": "APP_KEY",
@@ -3145,7 +3551,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3145
3551
  },
3146
3552
  },
3147
3553
  "app_views_apply": {
3148
- "allowed_keys": ["app_key", "publish", "upsert_views", "remove_views", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility"],
3554
+ "allowed_keys": ["app_key", "publish", "upsert_views", "patch_views", "remove_views", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility", "upsert_views[].query_conditions", "patch_views[].view_key", "patch_views[].name", "patch_views[].set", "patch_views[].unset"],
3149
3555
  "aliases": {
3150
3556
  "fields": "columns",
3151
3557
  "column_names": "columns",
@@ -3156,6 +3562,10 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3156
3562
  "kanban": "board",
3157
3563
  "filter_rules": "filters",
3158
3564
  "filterRules": "filters",
3565
+ "queryConditions": "query_conditions",
3566
+ "query_condition": "query_conditions",
3567
+ "queryCondition": "query_conditions",
3568
+ "patchViews": "patch_views",
3159
3569
  "startField": "start_field",
3160
3570
  "endField": "end_field",
3161
3571
  "titleField": "title_field",
@@ -3178,10 +3588,14 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3178
3588
  "execution_notes": [
3179
3589
  "apply may return partial_success when some views land and others fail",
3180
3590
  "when duplicate view names exist, supply view_key to target the exact view",
3181
- "read back app_get_views after any failed or partial view apply",
3591
+ "read back app_get after any failed or partial view apply",
3182
3592
  "view existence verification and saved-filter verification are separate; treat filters as unverified until verification.view_filters_verified is true",
3183
3593
  "buttons omitted preserves existing button config; buttons=[] clears all buttons; buttons=[...] replaces the full button config",
3184
3594
  "upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
3595
+ "filters are saved fixed filters that apply when the view opens; query_conditions configure the frontend query panel and only apply after a user enters query values",
3596
+ "upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
3597
+ "use patch_views for partial parameter replacement on existing views; the public update mode is patch even though the backend save is still a full view payload",
3598
+ "view associated report/view display is now configured through app_associated_resources_apply; app_views_apply keeps legacy associated_resources input compatible but it is no longer the recommended public contract",
3185
3599
  "for multi-value operators such as in, pass values as a list; value may also be used as an alias when it already contains a list",
3186
3600
  *_VISIBILITY_EXECUTION_NOTES,
3187
3601
  ],
@@ -3192,6 +3606,43 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3192
3606
  "upsert_views": [{"name": "全部数据", "type": "table", "columns": ["项目名称"], "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE)}],
3193
3607
  "remove_views": [],
3194
3608
  },
3609
+ "query_conditions_example": {
3610
+ "profile": "default",
3611
+ "app_key": "APP_KEY",
3612
+ "publish": True,
3613
+ "patch_views": [
3614
+ {
3615
+ "view_key": "VIEW_KEY",
3616
+ "set": {
3617
+ "query_conditions": {
3618
+ "enabled": True,
3619
+ "rows": [["客户名称", "负责人"], ["创建时间"]],
3620
+ }
3621
+ },
3622
+ }
3623
+ ],
3624
+ "remove_views": [],
3625
+ },
3626
+ "full_upsert_query_conditions_example": {
3627
+ "profile": "default",
3628
+ "app_key": "APP_KEY",
3629
+ "publish": True,
3630
+ "upsert_views": [
3631
+ {
3632
+ "name": "客户查询视图",
3633
+ "type": "table",
3634
+ "columns": ["客户名称", "负责人", "客户状态", "创建时间"],
3635
+ "filters": [{"field_name": "客户状态", "operator": "eq", "value": "有效"}],
3636
+ "query_conditions": {
3637
+ "enabled": True,
3638
+ "exact": False,
3639
+ "hide_before_query": False,
3640
+ "rows": [["客户名称", "负责人"], ["创建时间"]],
3641
+ },
3642
+ }
3643
+ ],
3644
+ "remove_views": [],
3645
+ },
3195
3646
  "gantt_example": {
3196
3647
  "profile": "default",
3197
3648
  "app_key": "APP_KEY",
@@ -3215,12 +3666,14 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3215
3666
  "aliases": {},
3216
3667
  "allowed_values": {},
3217
3668
  "execution_notes": [
3218
- "returns builder-side app configuration summary and editability",
3219
- "use this as the default builder discovery read before fields/layout/views/flow/charts detail reads",
3669
+ "returns builder-side app map: base summary, editability, field/view/chart/button counts, compact views, compact charts, custom_buttons, and app-level associated_resources",
3670
+ "use this as the default builder discovery read before view_get/chart_get/apply detail work",
3220
3671
  "editability is route-aware builder capability summary, not end-user data visibility",
3221
3672
  "can_edit_app_base covers app base-info writes such as app_name, icon, and visibility",
3222
3673
  "can_edit_form covers form/schema routes only and does not imply app base-info writes",
3223
3674
  "returns normalized app visibility when backend auth is readable",
3675
+ "custom_buttons[].button_id is the id required by app_custom_buttons_apply view_configs[].buttons[].button_ref",
3676
+ "associated_resources[].associated_item_id is the id required by app_associated_resources_apply.view_configs.associated_item_ids; do not pass chart_id there",
3224
3677
  ],
3225
3678
  "minimal_example": {
3226
3679
  "profile": "default",
@@ -3260,7 +3713,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3260
3713
  "allowed_values": {},
3261
3714
  "execution_notes": [
3262
3715
  "returns compact current view inventory for one app",
3263
- "use this before app_views_apply when you need exact current view keys",
3716
+ "compatibility/specialized inventory tool; default builder discovery should start with app_get",
3717
+ "use this before app_views_apply only when you need an exact current view inventory beyond app_get",
3264
3718
  "view items include visibility_summary when backend view auth is readable",
3265
3719
  ],
3266
3720
  "minimal_example": {
@@ -3287,7 +3741,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3287
3741
  "allowed_values": {},
3288
3742
  "execution_notes": [
3289
3743
  "returns a compact current chart inventory for one app",
3290
- "use this before app_charts_apply when you need exact current chart_id values",
3744
+ "compatibility/specialized inventory tool; default builder discovery should start with app_get",
3745
+ "use this before app_charts_apply when you need exact current chart_id values beyond the app_get summary",
3291
3746
  "chart summaries do not include full qingbi config payloads",
3292
3747
  "chart items include visibility_summary when QingBI base info is readable",
3293
3748
  ],
@@ -3326,8 +3781,9 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3326
3781
  },
3327
3782
  },
3328
3783
  "app_charts_apply": {
3329
- "allowed_keys": ["app_key", "upsert_charts", "remove_chart_ids", "reorder_chart_ids", "upsert_charts[].visibility"],
3784
+ "allowed_keys": ["app_key", "upsert_charts", "patch_charts", "remove_chart_ids", "reorder_chart_ids", "upsert_charts[].visibility", "patch_charts[].chart_id", "patch_charts[].name", "patch_charts[].set", "patch_charts[].unset"],
3330
3785
  "aliases": {
3786
+ "patchCharts": "patch_charts",
3331
3787
  "chart.id": "chart.chart_id",
3332
3788
  "chart.type": "chart.chart_type",
3333
3789
  "chart.dimension_fields": "chart.dimension_field_ids",
@@ -3342,6 +3798,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3342
3798
  },
3343
3799
  "execution_notes": [
3344
3800
  "app_charts_apply is immediate-live and does not publish",
3801
+ "use patch_charts for partial parameter replacement on existing charts; the tool reads current chart base/config, merges patch_charts[].set/unset, then submits full QingBI base/config payloads internally",
3345
3802
  "chart matching precedence is chart_id first, then exact unique chart name",
3346
3803
  "when chart names are not unique, supply chart_id instead of guessing by name",
3347
3804
  "successful create results must return a real backend chart_id",
@@ -3353,6 +3810,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3353
3810
  "profile": "default",
3354
3811
  "app_key": "APP_KEY",
3355
3812
  "upsert_charts": [{"name": "数据总量", "chart_type": "target", "indicator_field_ids": [], "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE)}],
3813
+ "patch_charts": [{"chart_id": "CHART_ID", "set": {"name": "数据总量-新版"}}],
3356
3814
  "remove_chart_ids": [],
3357
3815
  "reorder_chart_ids": [],
3358
3816
  },
@@ -3364,6 +3822,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3364
3822
  "execution_notes": [
3365
3823
  "returns one builder-side view definition detail",
3366
3824
  "does not return record data; use user-side view_get or record_list for runtime rows",
3825
+ "view_key is a raw builder view key; if a record-data custom:VIEW_KEY value is passed, the tool normalizes it to VIEW_KEY",
3367
3826
  "use this after builder portal_get when a component references a view_ref.view_key",
3368
3827
  "returns normalized view visibility when backend auth is readable",
3369
3828
  ],
@@ -3410,6 +3869,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3410
3869
  "create mode: package_id + dash_name",
3411
3870
  "portal_apply uses replace semantics for sections",
3412
3871
  "when editing an existing portal, sections may be omitted to update only base info such as visibility, icon, or package",
3872
+ "portal section-level patch is not exposed; supplying sections means full sections replacement",
3413
3873
  "remove a section by omitting it from the new sections list",
3414
3874
  "package_id is required when creating a new portal",
3415
3875
  "publish=false only guarantees draft and base-info updates; it does not claim live has changed",