@josephyan/qingflow-cli 0.2.0-beta.78 → 0.2.0-beta.80

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.
@@ -32,6 +32,7 @@ from ..builder_facade.models import (
32
32
  PublicChartType,
33
33
  PublicViewType,
34
34
  SchemaPlanRequest,
35
+ VisibilityPatch,
35
36
  ViewFilterOperator,
36
37
  ViewUpsertPatch,
37
38
  ViewsPreset,
@@ -88,8 +89,31 @@ class AiBuilderTools(ToolBase):
88
89
  package_name: str = "",
89
90
  icon: str | None = None,
90
91
  color: str | None = None,
92
+ visibility: JSONObject | None = None,
91
93
  ) -> JSONObject:
92
- return self.package_create(profile=profile, package_name=package_name, icon=icon, color=color)
94
+ return self.package_create(profile=profile, package_name=package_name, icon=icon, color=color, visibility=visibility)
95
+
96
+ @mcp.tool()
97
+ def package_get(profile: str = DEFAULT_PROFILE, tag_id: int = 0) -> JSONObject:
98
+ return self.package_get(profile=profile, tag_id=tag_id)
99
+
100
+ @mcp.tool()
101
+ def package_update(
102
+ profile: str = DEFAULT_PROFILE,
103
+ tag_id: int = 0,
104
+ package_name: str | None = None,
105
+ icon: str | None = None,
106
+ color: str | None = None,
107
+ visibility: JSONObject | None = None,
108
+ ) -> JSONObject:
109
+ return self.package_update(
110
+ profile=profile,
111
+ tag_id=tag_id,
112
+ package_name=package_name,
113
+ icon=icon,
114
+ color=color,
115
+ visibility=visibility,
116
+ )
93
117
 
94
118
  @mcp.tool()
95
119
  def solution_install(
@@ -288,6 +312,7 @@ class AiBuilderTools(ToolBase):
288
312
  app_title: str = "",
289
313
  icon: str = "",
290
314
  color: str = "",
315
+ visibility: JSONObject | None = None,
291
316
  create_if_missing: bool = False,
292
317
  publish: bool = True,
293
318
  add_fields: list[JSONObject] | None = None,
@@ -319,6 +344,7 @@ class AiBuilderTools(ToolBase):
319
344
  app_title=app_title,
320
345
  icon=icon,
321
346
  color=color,
347
+ visibility=visibility,
322
348
  create_if_missing=create_if_missing,
323
349
  publish=publish,
324
350
  add_fields=add_fields or [],
@@ -394,6 +420,7 @@ class AiBuilderTools(ToolBase):
394
420
  package_tag_id: int | None = None,
395
421
  publish: bool = True,
396
422
  sections: list[JSONObject] | None = None,
423
+ visibility: JSONObject | None = None,
397
424
  auth: JSONObject | None = None,
398
425
  icon: str | None = None,
399
426
  color: str | None = None,
@@ -423,6 +450,7 @@ class AiBuilderTools(ToolBase):
423
450
  package_tag_id=package_tag_id,
424
451
  publish=publish,
425
452
  sections=sections or [],
453
+ visibility=visibility,
426
454
  auth=auth,
427
455
  icon=icon,
428
456
  color=color,
@@ -518,14 +546,28 @@ class AiBuilderTools(ToolBase):
518
546
  package_name: str,
519
547
  icon: str | None = None,
520
548
  color: str | None = None,
549
+ visibility: JSONObject | None = None,
521
550
  ) -> JSONObject:
551
+ visibility_patch = None
552
+ if visibility is not None:
553
+ try:
554
+ visibility_patch = VisibilityPatch.model_validate(visibility)
555
+ except ValidationError as exc:
556
+ return _visibility_validation_failure(str(exc), tool_name="package_create", exc=exc)
522
557
  normalized_args = {
523
558
  "package_name": package_name,
524
559
  **({"icon": icon} if icon else {}),
525
560
  **({"color": color} if color else {}),
561
+ **({"visibility": visibility_patch.model_dump(mode="json")} if visibility_patch is not None else {}),
526
562
  }
527
563
  return _safe_tool_call(
528
- lambda: self._facade.package_create(profile=profile, package_name=package_name, icon=icon, color=color),
564
+ lambda: self._facade.package_create(
565
+ profile=profile,
566
+ package_name=package_name,
567
+ icon=icon,
568
+ color=color,
569
+ visibility=visibility_patch,
570
+ ),
529
571
  error_code="PACKAGE_CREATE_FAILED",
530
572
  normalized_args=normalized_args,
531
573
  suggested_next_call={
@@ -535,10 +577,57 @@ class AiBuilderTools(ToolBase):
535
577
  "package_name": package_name,
536
578
  **({"icon": icon} if icon else {}),
537
579
  **({"color": color} if color else {}),
580
+ **({"visibility": visibility_patch.model_dump(mode="json")} if visibility_patch is not None else {}),
538
581
  },
539
582
  },
540
583
  )
541
584
 
585
+ def package_get(self, *, profile: str, tag_id: int) -> JSONObject:
586
+ normalized_args = {"tag_id": tag_id}
587
+ return _safe_tool_call(
588
+ lambda: self._facade.package_get(profile=profile, tag_id=tag_id),
589
+ error_code="PACKAGE_GET_FAILED",
590
+ normalized_args=normalized_args,
591
+ suggested_next_call={"tool_name": "package_get", "arguments": {"profile": profile, "tag_id": tag_id}},
592
+ )
593
+
594
+ def package_update(
595
+ self,
596
+ *,
597
+ profile: str,
598
+ tag_id: int,
599
+ package_name: str | None = None,
600
+ icon: str | None = None,
601
+ color: str | None = None,
602
+ visibility: JSONObject | None = None,
603
+ ) -> JSONObject:
604
+ visibility_patch = None
605
+ if visibility is not None:
606
+ try:
607
+ visibility_patch = VisibilityPatch.model_validate(visibility)
608
+ except ValidationError as exc:
609
+ return _visibility_validation_failure(str(exc), tool_name="package_update", exc=exc)
610
+ normalized_args = {
611
+ "tag_id": tag_id,
612
+ **({"package_name": package_name} if str(package_name or "").strip() else {}),
613
+ **({"icon": icon} if icon else {}),
614
+ **({"color": color} if color else {}),
615
+ **({"visibility": visibility_patch.model_dump(mode="json")} if visibility_patch is not None else {}),
616
+ }
617
+ return _safe_tool_call(
618
+ lambda: self._facade.package_update(
619
+ profile=profile,
620
+ tag_id=tag_id,
621
+ package_name=package_name,
622
+ icon=icon,
623
+ color=color,
624
+ visibility=visibility_patch,
625
+ ),
626
+ error_code="PACKAGE_UPDATE_FAILED",
627
+ normalized_args=normalized_args,
628
+ suggested_next_call={"tool_name": "package_update", "arguments": {"profile": profile, **normalized_args}},
629
+ )
630
+
542
631
  def solution_install(
543
632
  self,
544
633
  *,
@@ -960,6 +1049,7 @@ class AiBuilderTools(ToolBase):
960
1049
  app_name: str = "",
961
1050
  icon: str = "",
962
1051
  color: str = "",
1052
+ visibility: JSONObject | None = None,
963
1053
  create_if_missing: bool = False,
964
1054
  add_fields: list[JSONObject],
965
1055
  update_fields: list[JSONObject],
@@ -973,6 +1063,7 @@ class AiBuilderTools(ToolBase):
973
1063
  "app_name": app_name,
974
1064
  "icon": icon,
975
1065
  "color": color,
1066
+ "visibility": visibility,
976
1067
  "create_if_missing": create_if_missing,
977
1068
  "add_fields": add_fields,
978
1069
  "update_fields": update_fields,
@@ -980,7 +1071,7 @@ class AiBuilderTools(ToolBase):
980
1071
  }
981
1072
  )
982
1073
  except ValidationError as exc:
983
- return _validation_failure(
1074
+ return _visibility_validation_failure(
984
1075
  str(exc),
985
1076
  tool_name="app_schema_plan",
986
1077
  exc=exc,
@@ -993,6 +1084,7 @@ class AiBuilderTools(ToolBase):
993
1084
  "app_name": app_name,
994
1085
  "icon": icon,
995
1086
  "color": color,
1087
+ "visibility": visibility,
996
1088
  "create_if_missing": create_if_missing,
997
1089
  "add_fields": [{"name": "字段名称", "type": "text"}],
998
1090
  "update_fields": [],
@@ -1116,7 +1208,7 @@ class AiBuilderTools(ToolBase):
1116
1208
  }
1117
1209
  )
1118
1210
  except ValidationError as exc:
1119
- return _validation_failure(
1211
+ return _visibility_validation_failure(
1120
1212
  str(exc),
1121
1213
  tool_name="app_views_plan",
1122
1214
  exc=exc,
@@ -1148,6 +1240,7 @@ class AiBuilderTools(ToolBase):
1148
1240
  app_title: str = "",
1149
1241
  icon: str = "",
1150
1242
  color: str = "",
1243
+ visibility: JSONObject | None = None,
1151
1244
  create_if_missing: bool = False,
1152
1245
  publish: bool = True,
1153
1246
  add_fields: list[JSONObject],
@@ -1162,6 +1255,7 @@ class AiBuilderTools(ToolBase):
1162
1255
  app_title=app_title,
1163
1256
  icon=icon,
1164
1257
  color=color,
1258
+ visibility=visibility,
1165
1259
  create_if_missing=create_if_missing,
1166
1260
  publish=publish,
1167
1261
  add_fields=add_fields,
@@ -1179,6 +1273,7 @@ class AiBuilderTools(ToolBase):
1179
1273
  app_title=app_title,
1180
1274
  icon=icon,
1181
1275
  color=color,
1276
+ visibility=visibility,
1182
1277
  create_if_missing=create_if_missing,
1183
1278
  publish=publish,
1184
1279
  add_fields=add_fields,
@@ -1197,6 +1292,7 @@ class AiBuilderTools(ToolBase):
1197
1292
  app_title: str = "",
1198
1293
  icon: str = "",
1199
1294
  color: str = "",
1295
+ visibility: JSONObject | None = None,
1200
1296
  create_if_missing: bool = False,
1201
1297
  publish: bool = True,
1202
1298
  add_fields: list[JSONObject],
@@ -1212,6 +1308,7 @@ class AiBuilderTools(ToolBase):
1212
1308
  app_name=effective_app_name,
1213
1309
  icon=icon,
1214
1310
  color=color,
1311
+ visibility=visibility,
1215
1312
  create_if_missing=create_if_missing,
1216
1313
  add_fields=add_fields,
1217
1314
  update_fields=update_fields,
@@ -1245,6 +1342,7 @@ class AiBuilderTools(ToolBase):
1245
1342
  "app_name": str(plan_args.get("app_name") or effective_app_name),
1246
1343
  "icon": str(plan_args.get("icon") or icon),
1247
1344
  "color": str(plan_args.get("color") or color),
1345
+ "visibility": plan_args.get("visibility") or visibility,
1248
1346
  "create_if_missing": bool(plan_args.get("create_if_missing", create_if_missing)),
1249
1347
  "publish": publish,
1250
1348
  "add_fields": plan_args.get("add_fields") or [{"name": "字段名称", "type": "text", "required": False}],
@@ -1259,6 +1357,7 @@ class AiBuilderTools(ToolBase):
1259
1357
  "app_name": str(plan_args.get("app_name") or effective_app_name),
1260
1358
  "icon": str(plan_args.get("icon") or icon or ""),
1261
1359
  "color": str(plan_args.get("color") or color or ""),
1360
+ "visibility": plan_args.get("visibility") or visibility,
1262
1361
  "create_if_missing": bool(plan_args.get("create_if_missing", create_if_missing)),
1263
1362
  "publish": publish,
1264
1363
  "add_fields": [patch.model_dump(mode="json") for patch in parsed_add],
@@ -1273,6 +1372,7 @@ class AiBuilderTools(ToolBase):
1273
1372
  app_name=str(plan_args.get("app_name") or effective_app_name),
1274
1373
  icon=str(plan_args.get("icon") or icon or ""),
1275
1374
  color=str(plan_args.get("color") or color or ""),
1375
+ visibility=VisibilityPatch.model_validate(plan_args.get("visibility")) if plan_args.get("visibility") is not None else None,
1276
1376
  create_if_missing=bool(plan_args.get("create_if_missing", create_if_missing)),
1277
1377
  publish=publish,
1278
1378
  add_fields=parsed_add,
@@ -1528,7 +1628,7 @@ class AiBuilderTools(ToolBase):
1528
1628
  try:
1529
1629
  parsed_views = [ViewUpsertPatch.model_validate(item) for item in plan_args.get("upsert_views") or []]
1530
1630
  except ValidationError as exc:
1531
- return _validation_failure(
1631
+ return _visibility_validation_failure(
1532
1632
  str(exc),
1533
1633
  tool_name="app_views_apply",
1534
1634
  exc=exc,
@@ -1598,7 +1698,7 @@ class AiBuilderTools(ToolBase):
1598
1698
  }
1599
1699
  )
1600
1700
  except ValidationError as exc:
1601
- return _validation_failure(
1701
+ return _visibility_validation_failure(
1602
1702
  str(exc),
1603
1703
  tool_name="app_charts_apply",
1604
1704
  exc=exc,
@@ -1630,6 +1730,7 @@ class AiBuilderTools(ToolBase):
1630
1730
  package_tag_id: int | None = None,
1631
1731
  publish: bool = True,
1632
1732
  sections: list[JSONObject],
1733
+ visibility: JSONObject | None = None,
1633
1734
  auth: JSONObject | None = None,
1634
1735
  icon: str | None = None,
1635
1736
  color: str | None = None,
@@ -1645,6 +1746,7 @@ class AiBuilderTools(ToolBase):
1645
1746
  "package_tag_id": package_tag_id,
1646
1747
  "publish": publish,
1647
1748
  "sections": sections or [],
1749
+ "visibility": visibility,
1648
1750
  "auth": auth,
1649
1751
  "icon": icon,
1650
1752
  "color": color,
@@ -1654,7 +1756,7 @@ class AiBuilderTools(ToolBase):
1654
1756
  }
1655
1757
  )
1656
1758
  except ValidationError as exc:
1657
- return _validation_failure(
1759
+ return _visibility_validation_failure(
1658
1760
  str(exc),
1659
1761
  tool_name="portal_apply",
1660
1762
  exc=exc,
@@ -1862,6 +1964,40 @@ def _validation_failure(
1862
1964
  }
1863
1965
 
1864
1966
 
1967
+ def _visibility_validation_failure(
1968
+ detail: str,
1969
+ *,
1970
+ tool_name: str,
1971
+ exc: ValidationError | None,
1972
+ suggested_next_call: JSONObject | None = None,
1973
+ ) -> JSONObject:
1974
+ result = _validation_failure(
1975
+ detail,
1976
+ tool_name=tool_name,
1977
+ exc=exc,
1978
+ suggested_next_call=suggested_next_call,
1979
+ )
1980
+ lowered = detail.lower()
1981
+ code_by_fragment = [
1982
+ ("visibility and auth cannot be provided together", "VISIBILITY_CONFLICT"),
1983
+ ("specific visibility requires selectors", "VISIBILITY_SELECTOR_REQUIRED"),
1984
+ ("selectors are only allowed when mode=specific", "VISIBILITY_SELECTOR_UNEXPECTED"),
1985
+ ("external_mode=specific requires external_selectors", "EXTERNAL_VISIBILITY_SELECTOR_REQUIRED"),
1986
+ ("external_selectors are only allowed when external_mode=specific", "EXTERNAL_VISIBILITY_SELECTOR_UNEXPECTED"),
1987
+ ("mode=everyone requires external_mode=workspace", "VISIBILITY_EXTERNAL_MODE_CONFLICT"),
1988
+ ("external_selectors are not allowed when mode=everyone", "VISIBILITY_SELECTOR_UNEXPECTED"),
1989
+ ]
1990
+ for fragment, error_code in code_by_fragment:
1991
+ if fragment in lowered:
1992
+ result["error_code"] = error_code
1993
+ result["message"] = fragment
1994
+ details = result.get("details")
1995
+ if isinstance(details, dict):
1996
+ details["visibility_error"] = True
1997
+ break
1998
+ return result
1999
+
2000
+
1865
2001
  def _config_failure(*, tool_name: str, message: str, fix_hint: str) -> JSONObject:
1866
2002
  contract = _BUILDER_TOOL_CONTRACTS.get(tool_name or "")
1867
2003
  return {
@@ -1978,6 +2114,52 @@ def _public_error_message(error_code: str, error: QingflowApiError) -> str:
1978
2114
  return mapping.get(error_code, "requested builder resource is unavailable in the current route")
1979
2115
 
1980
2116
 
2117
+ _VISIBILITY_ALLOWED_VALUES: JSONObject = {
2118
+ "visibility.mode": ["workspace", "everyone", "specific"],
2119
+ "visibility.external_mode": ["not", "workspace", "specific"],
2120
+ "visibility.selectors": [
2121
+ "member_uids",
2122
+ "member_emails",
2123
+ "member_names",
2124
+ "dept_ids",
2125
+ "dept_names",
2126
+ "role_ids",
2127
+ "role_names",
2128
+ "include_sub_departs",
2129
+ ],
2130
+ "visibility.external_selectors": ["member_ids", "member_emails", "dept_ids"],
2131
+ }
2132
+
2133
+
2134
+ _VISIBILITY_EXECUTION_NOTES = [
2135
+ "visibility is the canonical public auth shape for packages, apps, views, charts, and portals",
2136
+ "mode=workspace means internal workspace members; mode=everyone means all users; mode=specific requires selectors",
2137
+ "external_mode defaults to not and controls external-user visibility independently unless mode=everyone",
2138
+ "name selectors must resolve to exactly one member, department, or role; ambiguous names fail instead of guessing",
2139
+ "when updating an existing resource, omit visibility to preserve the current backend auth",
2140
+ ]
2141
+
2142
+
2143
+ _VISIBILITY_WORKSPACE_EXAMPLE: JSONObject = {
2144
+ "mode": "workspace",
2145
+ "selectors": {},
2146
+ "external_mode": "not",
2147
+ "external_selectors": {},
2148
+ }
2149
+
2150
+
2151
+ _VISIBILITY_SPECIFIC_EXAMPLE: JSONObject = {
2152
+ "mode": "specific",
2153
+ "selectors": {
2154
+ "member_emails": ["owner@example.com"],
2155
+ "role_names": ["项目经理"],
2156
+ "include_sub_departs": False,
2157
+ },
2158
+ "external_mode": "not",
2159
+ "external_selectors": {},
2160
+ }
2161
+
2162
+
1981
2163
  _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
1982
2164
  "file_upload_local": {
1983
2165
  "allowed_keys": ["upload_kind", "file_path", "upload_mark", "content_type", "bucket_type", "path_id", "file_related_url"],
@@ -2042,18 +2224,68 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2042
2224
  },
2043
2225
  },
2044
2226
  "package_create": {
2045
- "allowed_keys": ["package_name", "icon", "color"],
2227
+ "allowed_keys": ["package_name", "icon", "color", "visibility"],
2046
2228
  "aliases": {
2047
2229
  "packageName": "package_name",
2048
2230
  "iconName": "icon",
2049
2231
  "iconColor": "color",
2050
2232
  },
2051
- "allowed_values": {},
2233
+ "allowed_values": deepcopy(_VISIBILITY_ALLOWED_VALUES),
2234
+ "execution_notes": [
2235
+ "create a package and optionally set visibility in the same call",
2236
+ *_VISIBILITY_EXECUTION_NOTES,
2237
+ ],
2052
2238
  "minimal_example": {
2053
2239
  "profile": "default",
2054
2240
  "package_name": "项目管理",
2055
2241
  "icon": "files-folder",
2056
2242
  "color": "azure",
2243
+ "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE),
2244
+ },
2245
+ "specific_visibility_example": {
2246
+ "profile": "default",
2247
+ "package_name": "项目管理",
2248
+ "icon": "files-folder",
2249
+ "color": "azure",
2250
+ "visibility": deepcopy(_VISIBILITY_SPECIFIC_EXAMPLE),
2251
+ },
2252
+ },
2253
+ "package_get": {
2254
+ "allowed_keys": ["tag_id"],
2255
+ "aliases": {"tagId": "tag_id"},
2256
+ "allowed_values": {},
2257
+ "execution_notes": [
2258
+ "returns package base info plus normalized visibility",
2259
+ "visibility is normalized from backend MemberAuthInfoVO auth",
2260
+ ],
2261
+ "minimal_example": {
2262
+ "profile": "default",
2263
+ "tag_id": 1001,
2264
+ },
2265
+ },
2266
+ "package_update": {
2267
+ "allowed_keys": ["tag_id", "package_name", "icon", "color", "visibility"],
2268
+ "aliases": {
2269
+ "tagId": "tag_id",
2270
+ "packageName": "package_name",
2271
+ "iconName": "icon",
2272
+ "iconColor": "color",
2273
+ },
2274
+ "allowed_values": deepcopy(_VISIBILITY_ALLOWED_VALUES),
2275
+ "execution_notes": [
2276
+ "update package name, icon, color, and/or visibility",
2277
+ *_VISIBILITY_EXECUTION_NOTES,
2278
+ ],
2279
+ "minimal_example": {
2280
+ "profile": "default",
2281
+ "tag_id": 1001,
2282
+ "package_name": "项目管理",
2283
+ "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE),
2284
+ },
2285
+ "specific_visibility_example": {
2286
+ "profile": "default",
2287
+ "tag_id": 1001,
2288
+ "visibility": deepcopy(_VISIBILITY_SPECIFIC_EXAMPLE),
2057
2289
  },
2058
2290
  },
2059
2291
  "solution_install": {
@@ -2273,7 +2505,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2273
2505
  },
2274
2506
  },
2275
2507
  "app_schema_plan": {
2276
- "allowed_keys": ["app_key", "package_tag_id", "app_name", "icon", "color", "create_if_missing", "add_fields", "update_fields", "remove_fields"],
2508
+ "allowed_keys": ["app_key", "package_tag_id", "app_name", "icon", "color", "visibility", "create_if_missing", "add_fields", "update_fields", "remove_fields"],
2277
2509
  "aliases": {
2278
2510
  "app_title": "app_name",
2279
2511
  "title": "app_name",
@@ -2301,13 +2533,19 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2301
2533
  "field.type": [member.value for member in PublicFieldType],
2302
2534
  "field.relation_mode": [member.value for member in PublicRelationMode],
2303
2535
  "field_type_ids": sorted(FIELD_TYPE_ID_ALIASES.keys()),
2536
+ **deepcopy(_VISIBILITY_ALLOWED_VALUES),
2304
2537
  },
2538
+ "execution_notes": [
2539
+ "create mode may set visibility for the new app; edit mode may update visibility on an existing app",
2540
+ *_VISIBILITY_EXECUTION_NOTES,
2541
+ ],
2305
2542
  "minimal_example": {
2306
2543
  "profile": "default",
2307
2544
  "app_name": "研发项目管理",
2308
2545
  "package_tag_id": 1001,
2309
2546
  "icon": "template",
2310
2547
  "color": "emerald",
2548
+ "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE),
2311
2549
  "create_if_missing": True,
2312
2550
  "add_fields": [{"name": "项目名称", "type": "text"}],
2313
2551
  "update_fields": [],
@@ -2331,7 +2569,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2331
2569
  },
2332
2570
  },
2333
2571
  "app_schema_apply": {
2334
- "allowed_keys": ["app_key", "package_tag_id", "app_name", "icon", "color", "create_if_missing", "publish", "add_fields", "update_fields", "remove_fields"],
2572
+ "allowed_keys": ["app_key", "package_tag_id", "app_name", "icon", "color", "visibility", "create_if_missing", "publish", "add_fields", "update_fields", "remove_fields"],
2335
2573
  "aliases": {
2336
2574
  "app_title": "app_name",
2337
2575
  "title": "app_name",
@@ -2362,11 +2600,14 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2362
2600
  "field.code_block_binding.outputs.target_field.type": list(INTEGRATION_OUTPUT_TARGET_FIELD_TYPES),
2363
2601
  "field.q_linker_binding.outputs.target_field.type": list(INTEGRATION_OUTPUT_TARGET_FIELD_TYPES),
2364
2602
  "field_type_ids": sorted(FIELD_TYPE_ID_ALIASES.keys()),
2603
+ **deepcopy(_VISIBILITY_ALLOWED_VALUES),
2365
2604
  },
2366
2605
  "execution_notes": [
2367
2606
  "use exactly one resource mode",
2368
2607
  "edit mode: app_key, optional app_name to rename the existing app",
2369
2608
  "create mode: package_tag_id + app_name + create_if_missing=true",
2609
+ "create mode defaults new app visibility to workspace/not when visibility is omitted; edit mode preserves current visibility when omitted",
2610
+ *_VISIBILITY_EXECUTION_NOTES,
2370
2611
  "multiple relation fields are backend-risky; read verification.relation_field_limit_verified and warnings before declaring the schema stable",
2371
2612
  "backend 49614 is normalized to MULTIPLE_RELATION_FIELDS_UNSUPPORTED with a workaround message",
2372
2613
  "relation_mode=multiple maps to referenceConfig.optionalDataNum=0",
@@ -2386,6 +2627,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2386
2627
  "package_tag_id": 1001,
2387
2628
  "icon": "template",
2388
2629
  "color": "emerald",
2630
+ "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE),
2389
2631
  "create_if_missing": True,
2390
2632
  "publish": True,
2391
2633
  "add_fields": [{"name": "项目名称", "type": "text"}],
@@ -2637,7 +2879,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2637
2879
  },
2638
2880
  },
2639
2881
  "app_views_plan": {
2640
- "allowed_keys": ["app_key", "upsert_views", "remove_views", "preset", "upsert_views[].view_key", "upsert_views[].buttons"],
2882
+ "allowed_keys": ["app_key", "upsert_views", "remove_views", "preset", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility"],
2641
2883
  "aliases": {
2642
2884
  "fields": "columns",
2643
2885
  "column_names": "columns",
@@ -2666,14 +2908,17 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2666
2908
  "view.filter.operator": [member.value for member in ViewFilterOperator],
2667
2909
  "view.buttons.button_type": ["SYSTEM", "CUSTOM"],
2668
2910
  "view.buttons.config_type": ["TOP", "DETAIL"],
2911
+ **deepcopy(_VISIBILITY_ALLOWED_VALUES),
2669
2912
  },
2670
2913
  "execution_notes": [
2914
+ "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",
2671
2915
  "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",
2916
+ *_VISIBILITY_EXECUTION_NOTES,
2672
2917
  ],
2673
2918
  "minimal_example": {
2674
2919
  "profile": "default",
2675
2920
  "app_key": "APP_KEY",
2676
- "upsert_views": [{"name": "全部数据", "type": "table", "columns": ["项目名称"]}],
2921
+ "upsert_views": [{"name": "全部数据", "type": "table", "columns": ["项目名称"], "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE)}],
2677
2922
  "remove_views": [],
2678
2923
  },
2679
2924
  "gantt_example": {
@@ -2694,7 +2939,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2694
2939
  },
2695
2940
  },
2696
2941
  "app_views_apply": {
2697
- "allowed_keys": ["app_key", "publish", "upsert_views", "remove_views", "upsert_views[].view_key", "upsert_views[].buttons"],
2942
+ "allowed_keys": ["app_key", "publish", "upsert_views", "remove_views", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility"],
2698
2943
  "aliases": {
2699
2944
  "fields": "columns",
2700
2945
  "column_names": "columns",
@@ -2722,6 +2967,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2722
2967
  "view.filter.operator": [member.value for member in ViewFilterOperator],
2723
2968
  "view.buttons.button_type": ["SYSTEM", "CUSTOM"],
2724
2969
  "view.buttons.config_type": ["TOP", "DETAIL"],
2970
+ **deepcopy(_VISIBILITY_ALLOWED_VALUES),
2725
2971
  },
2726
2972
  "execution_notes": [
2727
2973
  "apply may return partial_success when some views land and others fail",
@@ -2729,13 +2975,15 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2729
2975
  "read back app_get_views after any failed or partial view apply",
2730
2976
  "view existence verification and saved-filter verification are separate; treat filters as unverified until verification.view_filters_verified is true",
2731
2977
  "buttons omitted preserves existing button config; buttons=[] clears all buttons; buttons=[...] replaces the full button config",
2978
+ "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",
2732
2979
  "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",
2980
+ *_VISIBILITY_EXECUTION_NOTES,
2733
2981
  ],
2734
2982
  "minimal_example": {
2735
2983
  "profile": "default",
2736
2984
  "app_key": "APP_KEY",
2737
2985
  "publish": True,
2738
- "upsert_views": [{"name": "全部数据", "type": "table", "columns": ["项目名称"]}],
2986
+ "upsert_views": [{"name": "全部数据", "type": "table", "columns": ["项目名称"], "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE)}],
2739
2987
  "remove_views": [],
2740
2988
  },
2741
2989
  "gantt_example": {
@@ -2764,6 +3012,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2764
3012
  "returns builder-side app configuration summary and editability",
2765
3013
  "use this as the default builder discovery read before fields/layout/views/flow/charts detail reads",
2766
3014
  "editability reflects builder permissions, not end-user data visibility",
3015
+ "returns normalized app visibility when backend auth is readable",
2767
3016
  ],
2768
3017
  "minimal_example": {
2769
3018
  "profile": "default",
@@ -2803,6 +3052,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2803
3052
  "execution_notes": [
2804
3053
  "returns compact current view inventory for one app",
2805
3054
  "use this before app_views_apply when you need exact current view keys",
3055
+ "view items include visibility_summary when backend view auth is readable",
2806
3056
  ],
2807
3057
  "minimal_example": {
2808
3058
  "profile": "default",
@@ -2830,6 +3080,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2830
3080
  "returns a compact current chart inventory for one app",
2831
3081
  "use this before app_charts_apply when you need exact current chart_id values",
2832
3082
  "chart summaries do not include full qingbi config payloads",
3083
+ "chart items include visibility_summary when QingBI base info is readable",
2833
3084
  ],
2834
3085
  "minimal_example": {
2835
3086
  "profile": "default",
@@ -2857,6 +3108,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2857
3108
  "returns builder-side portal configuration detail plus a normalized component inventory",
2858
3109
  "chart and view components are returned as refs only; use builder chart_get or builder view_get for configuration detail",
2859
3110
  "being_draft=true reads the current draft view; being_draft=false reads live",
3111
+ "returns normalized portal visibility when backend auth is readable",
2860
3112
  ],
2861
3113
  "minimal_example": {
2862
3114
  "profile": "default",
@@ -2865,7 +3117,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2865
3117
  },
2866
3118
  },
2867
3119
  "app_charts_apply": {
2868
- "allowed_keys": ["app_key", "upsert_charts", "remove_chart_ids", "reorder_chart_ids"],
3120
+ "allowed_keys": ["app_key", "upsert_charts", "remove_chart_ids", "reorder_chart_ids", "upsert_charts[].visibility"],
2869
3121
  "aliases": {
2870
3122
  "chart.id": "chart.chart_id",
2871
3123
  "chart.type": "chart.chart_type",
@@ -2877,17 +3129,20 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2877
3129
  "allowed_values": {
2878
3130
  "chart.chart_type": [member.value for member in PublicChartType],
2879
3131
  "chart.filter.operator": [member.value for member in ViewFilterOperator],
3132
+ **deepcopy(_VISIBILITY_ALLOWED_VALUES),
2880
3133
  },
2881
3134
  "execution_notes": [
2882
3135
  "app_charts_apply is immediate-live and does not publish",
2883
3136
  "chart matching precedence is chart_id first, then exact unique chart name",
2884
3137
  "when chart names are not unique, supply chart_id instead of guessing by name",
2885
3138
  "successful create results must return a real backend chart_id",
3139
+ "upsert_charts[].visibility compiles to QingBI visibleAuth; omit it on updates to preserve the existing visibleAuth",
3140
+ *_VISIBILITY_EXECUTION_NOTES,
2886
3141
  ],
2887
3142
  "minimal_example": {
2888
3143
  "profile": "default",
2889
3144
  "app_key": "APP_KEY",
2890
- "upsert_charts": [{"name": "数据总量", "chart_type": "target", "indicator_field_ids": []}],
3145
+ "upsert_charts": [{"name": "数据总量", "chart_type": "target", "indicator_field_ids": [], "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE)}],
2891
3146
  "remove_chart_ids": [],
2892
3147
  "reorder_chart_ids": [],
2893
3148
  },
@@ -2900,6 +3155,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2900
3155
  "returns one builder-side view definition detail",
2901
3156
  "does not return record data; use user-side view_get or record_list for runtime rows",
2902
3157
  "use this after builder portal_get when a component references a view_ref.view_key",
3158
+ "returns normalized view visibility when backend auth is readable",
2903
3159
  ],
2904
3160
  "minimal_example": {
2905
3161
  "profile": "default",
@@ -2914,6 +3170,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2914
3170
  "returns builder-side chart base info and chart config only",
2915
3171
  "chart_id is required; chart names are not accepted here",
2916
3172
  "does not return chart data; use user-side chart_get for runtime data access",
3173
+ "returns normalized chart visibility from QingBI visibleAuth when readable",
2917
3174
  ],
2918
3175
  "minimal_example": {
2919
3176
  "profile": "default",
@@ -2921,7 +3178,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2921
3178
  },
2922
3179
  },
2923
3180
  "portal_apply": {
2924
- "allowed_keys": ["dash_key", "dash_name", "package_tag_id", "publish", "sections", "auth", "icon", "color", "hide_copyright", "dash_global_config", "config"],
3181
+ "allowed_keys": ["dash_key", "dash_name", "package_tag_id", "publish", "sections", "visibility", "auth", "icon", "color", "hide_copyright", "dash_global_config", "config"],
2925
3182
  "aliases": {
2926
3183
  "sourceType": "source_type",
2927
3184
  "chartRef": "chart_ref",
@@ -2935,7 +3192,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2935
3192
  "viewRef": "view_ref",
2936
3193
  "dashStyleConfigBO": "dash_style_config",
2937
3194
  },
2938
- "allowed_values": {"section.source_type": ["chart", "view", "grid", "filter", "text", "link"]},
3195
+ "allowed_values": {"section.source_type": ["chart", "view", "grid", "filter", "text", "link"], **deepcopy(_VISIBILITY_ALLOWED_VALUES)},
2939
3196
  "execution_notes": [
2940
3197
  "use exactly one resource mode",
2941
3198
  "update mode: dash_key",
@@ -2947,12 +3204,16 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2947
3204
  "chart_ref resolves by chart_id first, then exact unique chart_name",
2948
3205
  "view_ref resolves by view_key first, then exact unique view_name",
2949
3206
  "position.pc/mobile is the canonical portal layout shape",
3207
+ "visibility is the canonical public auth shape; auth is kept only as a deprecated compatibility alias",
3208
+ "passing visibility and auth together is rejected as VISIBILITY_CONFLICT",
3209
+ *_VISIBILITY_EXECUTION_NOTES,
2950
3210
  ],
2951
3211
  "minimal_example": {
2952
3212
  "profile": "default",
2953
3213
  "dash_name": "经营门户",
2954
3214
  "package_tag_id": 1001,
2955
3215
  "publish": True,
3216
+ "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE),
2956
3217
  "sections": [
2957
3218
  {
2958
3219
  "title": "经营概览",