@qingflow-tech/qingflow-cli 1.1.47-beta.5 → 1.1.48

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.
@@ -10,7 +10,6 @@ from .config import DEFAULT_PROFILE
10
10
  from .response_trim import USER_SERVER_METHOD_MAP, trim_error_response, trim_public_response, wrap_trimmed_methods
11
11
  from .session_store import SessionStore
12
12
  from .tools.app_tools import AppTools
13
- from .tools.data_tools import DataTools
14
13
  from .tools.auth_tools import AuthTools
15
14
  from .tools.code_block_tools import CodeBlockTools
16
15
  from .tools.directory_tools import DirectoryTools
@@ -34,15 +33,12 @@ def build_user_server(*, stateless_http: bool = False) -> FastMCP:
34
33
 
35
34
  ## 页面交接
36
35
 
37
- 创建、修改或安装轻流资源后,最终回复主动提供主要结果的可点击页面入口,无需等待用户再次索要。采用“完成结果 + 查看入口”,文案使用“在轻流中查看应用/应用包/视图”,不默认称为“继续编辑”。用户明确要求查看或打开页面时也适用以下规则。
38
- 默认只交付用户请求的主要结果:安装方案默认提供应用包入口;直接创建或修改应用、视图时提供对应入口;用户明确要求多个主要对象时分别提供。使用业务结果中的稳定 `uri`,按 URI 去重,不为全部子资源预签票据。
39
- 主要对象没有可靠 URI 时说明限制,不猜地址,也不替换为不相关页面。部分成功时仅交付已确认成功且可定位的主要结果,保留失败或未验证说明。纯数据问答、中间读取、失败资源和删除操作不自动签发;用户明确要求不提供链接时遵从。
36
+ 用户需要查看结果、继续编辑、处理业务或进入工作区页面时,使用 `get_login_link`;纯数据查询不自动签发链接。
40
37
  目标路径由业务工具或领域 Skill 根据真实前端路由确定,不由票据工具根据业务标识推导。
41
38
  已知目标传可选 `redirect_path`;明确指定页面但路径未知时继续定位或说明限制,不猜测,也不静默改为首页。
42
39
  搜索未命中时继续搜索 `login` 或查看服务工具列表,不擅自打开固定正式站点;鉴权或工具失败时如实说明。
43
- 目标浏览器登录态未知时,默认用资源 URI 的同源 pathname 调用 `get_login_link`,为每个实际展示的入口分别签发,不额外追问登录态;只有已有用户确认同一浏览器、网页域名、账号和工作区已登录且无失效信号的事实时,才直接交付稳定 URI。调用过票据工具、自动化浏览器已登录或之前签发过票据均不能证明用户浏览器已登录,已消费链接也不能复用。
44
- 临近交付时再签发,使用返回的完整 URL。用户未要求打开时仅交付,不提前消费;要求“打开”时才调用浏览器。签发失败时说明免登录链接未生成,并提供标注“需登录”的稳定 URI 作为备用入口,不重做业务操作。机器结果继续保留稳定 URI,临时登录链接仅用于面向用户交付。
45
- 浏览器不可用时提供可点击链接,不假称已经打开,不展示工作区数字 ID 或原始 JSON。
40
+ 临近交付时再签发,使用返回的完整 URL。要求“给我链接”时仅交付,不提前消费;要求“打开”时才调用浏览器。
41
+ 浏览器不可用时提供可点击链接;用“查看记录”“继续编辑”等文案,不展示工作区数字 ID 或原始 JSON。
46
42
  链接约 5 分钟内一次性有效,不能作为长期书签;安全验证及原有业务权限不变。
47
43
 
48
44
  ## App Discovery
@@ -243,7 +239,6 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
243
239
  backend = BackendClient()
244
240
  auth = wrap_trimmed_methods(AuthTools(sessions, backend), USER_SERVER_METHOD_MAP)
245
241
  apps = wrap_trimmed_methods(AppTools(sessions, backend), USER_SERVER_METHOD_MAP)
246
- data_tools = wrap_trimmed_methods(DataTools(sessions, backend), USER_SERVER_METHOD_MAP)
247
242
  workspace = wrap_trimmed_methods(WorkspaceTools(sessions, backend), USER_SERVER_METHOD_MAP)
248
243
  file_tools = wrap_trimmed_methods(FileTools(sessions, backend), USER_SERVER_METHOD_MAP)
249
244
  imports = wrap_trimmed_methods(ImportTools(sessions, backend), USER_SERVER_METHOD_MAP)
@@ -397,8 +392,6 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
397
392
  def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
398
393
  return apps.app_get(profile=profile, app_key=app_key)
399
394
 
400
- data_tools.register(server)
401
-
402
395
  @server.tool()
403
396
  def portal_list(profile: str = DEFAULT_PROFILE) -> dict:
404
397
  return resources.portal_list(profile=profile)
@@ -20,7 +20,6 @@ from ..public_surface import public_builder_contract_tool_names
20
20
  from ..config import DEFAULT_PROFILE
21
21
  from ..errors import QingflowApiError, backend_code_int, backend_code_value_int
22
22
  from ..json_types import JSONObject
23
- from ..resource_uri import try_build_resource_uri
24
23
  from ..builder_facade.models import (
25
24
  AssociatedResourcesApplyRequest,
26
25
  ChartApplyRequest,
@@ -188,26 +187,6 @@ class AiBuilderTools(ToolBase):
188
187
  registry=app_form_schema_registry(),
189
188
  )
190
189
 
191
- def _selected_ws_id(self, profile: str) -> int:
192
- session_profile, _backend_session, _context = self._require_context(profile, require_workspace=True)
193
- assert session_profile.selected_ws_id is not None
194
- return session_profile.selected_ws_id
195
-
196
- def _attach_apply_envelope(self, profile: str, tool_name: str, payload: JSONObject) -> JSONObject:
197
- result = _attach_builder_apply_envelope(tool_name, payload)
198
- eligible = [
199
- item
200
- for item in result.get("resources") or []
201
- if isinstance(item, dict)
202
- and item.get("resource_type") in {"package", "app", "view"}
203
- and str(item.get("status") or "").lower()
204
- not in {"failed", "pending", "planned", "readback_pending", "pending_readback", "unknown", "removed"}
205
- and str(item.get("operation") or "").lower() not in {"failed", "removed", "planned"}
206
- ]
207
- if eligible:
208
- _attach_builder_resource_uris(result, eligible, ws_id=self._selected_ws_id(profile))
209
- return result
210
-
211
190
  def _apply_app_batch(self, *, tool_name: str, profile: str, apps: list[JSONObject], apply_one) -> JSONObject:
212
191
  started_at = time.perf_counter()
213
192
  batch_item_apply_ms = 0
@@ -220,7 +199,7 @@ class AiBuilderTools(ToolBase):
220
199
  child_total_ms=child_total_ms,
221
200
  total_ms=_elapsed_ms(started_at),
222
201
  )
223
- return self._attach_apply_envelope(profile, tool_name, payload)
202
+ return _attach_builder_apply_envelope(tool_name, payload)
224
203
 
225
204
  if not isinstance(apps, list) or not apps:
226
205
  return finish(
@@ -862,30 +841,16 @@ class AiBuilderTools(ToolBase):
862
841
  being_draft: bool = False,
863
842
  ) -> JSONObject:
864
843
  """Read a canonical draft or published AppForm resource."""
865
- result = self._app_form.get(
844
+ return self._app_form.get(
866
845
  profile=profile,
867
846
  app_key=app_key,
868
847
  schema_version=schema_version,
869
848
  being_draft=being_draft,
870
849
  )
871
- if result.get("status") == "success":
872
- _attach_uri(result, result, ws_id=self._selected_ws_id(profile), resource_type="app", app_key=app_key)
873
- return result
874
850
 
875
851
  def app_form_apply(self, *, profile: str, spec: JSONObject) -> JSONObject:
876
852
  """Reconcile one complete declarative AppForm target."""
877
- result = self._app_form.apply(profile=profile, spec=spec)
878
- if result.get("appKey") and (
879
- str(result.get("status") or "") == "success" or result.get("createdAppPreserved") is True
880
- ):
881
- _attach_uri(
882
- result,
883
- result,
884
- ws_id=self._selected_ws_id(profile),
885
- resource_type="app",
886
- app_key=result.get("appKey"),
887
- )
888
- return result
853
+ return self._app_form.apply(profile=profile, spec=spec)
889
854
 
890
855
  def app_form_evaluate_formula(
891
856
  self,
@@ -952,24 +917,12 @@ class AiBuilderTools(ToolBase):
952
917
  def package_list(self, *, profile: str, trial_status: str = "all", query: str = "") -> JSONObject:
953
918
  """执行分组与包相关逻辑。"""
954
919
  normalized_args = {"trial_status": trial_status, "query": query}
955
- result = _safe_tool_call(
920
+ return _safe_tool_call(
956
921
  lambda: self._facade.package_list(profile=profile, trial_status=trial_status, query=query),
957
922
  error_code="PACKAGE_LIST_FAILED",
958
923
  normalized_args=normalized_args,
959
924
  suggested_next_call={"tool_name": "package_list", "arguments": {"profile": profile, "trial_status": trial_status, "query": query}},
960
925
  )
961
- if result.get("status") == "success":
962
- ws_id = self._selected_ws_id(profile)
963
- for item in result.get("items") or []:
964
- if isinstance(item, dict):
965
- _attach_uri(
966
- item,
967
- result,
968
- ws_id=ws_id,
969
- resource_type="package",
970
- package_id=item.get("package_id"),
971
- )
972
- return result
973
926
 
974
927
  def package_resolve(self, *, profile: str, package_name: str) -> JSONObject:
975
928
  """执行分组与包相关逻辑。"""
@@ -1109,23 +1062,12 @@ class AiBuilderTools(ToolBase):
1109
1062
  def package_get(self, *, profile: str, package_id: int | None = None) -> JSONObject:
1110
1063
  """执行分组与包相关逻辑。"""
1111
1064
  normalized_args = {"package_id": package_id}
1112
- result = _publicize_package_fields(_safe_tool_call(
1065
+ return _publicize_package_fields(_safe_tool_call(
1113
1066
  lambda: self._facade.package_get(profile=profile, package_id=package_id),
1114
1067
  error_code="PACKAGE_GET_FAILED",
1115
1068
  normalized_args=normalized_args,
1116
1069
  suggested_next_call={"tool_name": "package_get", "arguments": {"profile": profile, "package_id": package_id}},
1117
1070
  ))
1118
- if result.get("status") == "success":
1119
- ws_id = self._selected_ws_id(profile)
1120
- _attach_uri(
1121
- result,
1122
- result,
1123
- ws_id=ws_id,
1124
- resource_type="package",
1125
- package_id=result.get("package_id"),
1126
- )
1127
- _attach_package_item_uris(result.get("items"), result, ws_id=ws_id)
1128
- return result
1129
1071
 
1130
1072
  def package_apply(
1131
1073
  self,
@@ -1155,7 +1097,7 @@ class AiBuilderTools(ToolBase):
1155
1097
  try:
1156
1098
  visibility_patch = VisibilityPatch.model_validate(visibility)
1157
1099
  except ValidationError as exc:
1158
- return self._attach_apply_envelope(profile,
1100
+ return _attach_builder_apply_envelope(
1159
1101
  "package_apply",
1160
1102
  _visibility_validation_failure(str(exc), tool_name="package_apply", exc=exc),
1161
1103
  )
@@ -1166,7 +1108,7 @@ class AiBuilderTools(ToolBase):
1166
1108
  creating=package_id is None and bool(str(package_name or "").strip()),
1167
1109
  )
1168
1110
  if icon_failure is not None:
1169
- return self._attach_apply_envelope(profile, "package_apply", icon_failure)
1111
+ return _attach_builder_apply_envelope("package_apply", icon_failure)
1170
1112
  normalized_args = {
1171
1113
  "package_id": package_id,
1172
1114
  **({"package_name": package_name} if str(package_name or "").strip() else {}),
@@ -1191,7 +1133,7 @@ class AiBuilderTools(ToolBase):
1191
1133
  normalized_args=normalized_args,
1192
1134
  suggested_next_call={"tool_name": "package_apply", "arguments": {"profile": profile, **normalized_args}},
1193
1135
  ))
1194
- return self._attach_apply_envelope(profile, "package_apply", result)
1136
+ return _attach_builder_apply_envelope("package_apply", result)
1195
1137
 
1196
1138
  def package_update(
1197
1139
  self,
@@ -1245,7 +1187,7 @@ class AiBuilderTools(ToolBase):
1245
1187
  "being_copy_data": being_copy_data,
1246
1188
  "solution_source": solution_source,
1247
1189
  }
1248
- result = _safe_tool_call(
1190
+ return _safe_tool_call(
1249
1191
  lambda: self._facade.solution_install(
1250
1192
  profile=profile,
1251
1193
  solution_key=solution_key,
@@ -1256,7 +1198,6 @@ class AiBuilderTools(ToolBase):
1256
1198
  normalized_args=normalized_args,
1257
1199
  suggested_next_call={"tool_name": "solution_install", "arguments": {"profile": profile, **normalized_args}},
1258
1200
  )
1259
- return self._attach_apply_envelope(profile, "solution_install", result)
1260
1201
 
1261
1202
  def member_search(
1262
1203
  self,
@@ -1453,7 +1394,7 @@ class AiBuilderTools(ToolBase):
1453
1394
  try:
1454
1395
  request = CustomButtonsApplyRequest.model_validate(raw_request)
1455
1396
  except ValidationError as exc:
1456
- return self._attach_apply_envelope(profile, "app_custom_buttons_apply", _validation_failure(
1397
+ return _attach_builder_apply_envelope("app_custom_buttons_apply", _validation_failure(
1457
1398
  str(exc),
1458
1399
  tool_name="app_custom_buttons_apply",
1459
1400
  exc=exc,
@@ -1477,7 +1418,7 @@ class AiBuilderTools(ToolBase):
1477
1418
  },
1478
1419
  ))
1479
1420
  normalized_args = request.model_dump(mode="json")
1480
- return self._attach_apply_envelope(profile, "app_custom_buttons_apply", _safe_tool_call(
1421
+ return _attach_builder_apply_envelope("app_custom_buttons_apply", _safe_tool_call(
1481
1422
  lambda: self._facade.app_custom_buttons_apply(profile=profile, request=request),
1482
1423
  error_code="CUSTOM_BUTTONS_APPLY_FAILED",
1483
1424
  normalized_args=normalized_args,
@@ -1534,7 +1475,7 @@ class AiBuilderTools(ToolBase):
1534
1475
  try:
1535
1476
  request = AssociatedResourcesApplyRequest.model_validate(raw_request)
1536
1477
  except ValidationError as exc:
1537
- return self._attach_apply_envelope(profile, "app_associated_resources_apply", _validation_failure(
1478
+ return _attach_builder_apply_envelope("app_associated_resources_apply", _validation_failure(
1538
1479
  str(exc),
1539
1480
  tool_name="app_associated_resources_apply",
1540
1481
  exc=exc,
@@ -1563,7 +1504,7 @@ class AiBuilderTools(ToolBase):
1563
1504
  },
1564
1505
  ))
1565
1506
  normalized_args = request.model_dump(mode="json", exclude_none=True)
1566
- return self._attach_apply_envelope(profile, "app_associated_resources_apply", _safe_tool_call(
1507
+ return _attach_builder_apply_envelope("app_associated_resources_apply", _safe_tool_call(
1567
1508
  lambda: self._facade.app_associated_resources_apply(profile=profile, request=request),
1568
1509
  error_code="ASSOCIATED_RESOURCES_APPLY_FAILED",
1569
1510
  normalized_args=normalized_args,
@@ -1688,27 +1629,12 @@ class AiBuilderTools(ToolBase):
1688
1629
  read_one=lambda item_app_key: self.app_get(profile=profile, app_key=item_app_key, app_keys=None),
1689
1630
  )
1690
1631
  normalized_args = {"app_key": app_key}
1691
- result = _publicize_package_fields(_safe_tool_call(
1632
+ return _publicize_package_fields(_safe_tool_call(
1692
1633
  lambda: self._facade.app_get(profile=profile, app_key=app_key),
1693
1634
  error_code="APP_GET_FAILED",
1694
1635
  normalized_args=normalized_args,
1695
1636
  suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
1696
1637
  ))
1697
- if result.get("status") == "success":
1698
- ws_id = self._selected_ws_id(profile)
1699
- _attach_uri(result, result, ws_id=ws_id, resource_type="app", app_key=app_key)
1700
- for view in result.get("views") or []:
1701
- if isinstance(view, dict) and (view.get("view_key") or view.get("viewKey")):
1702
- _attach_uri(
1703
- view,
1704
- result,
1705
- ws_id=ws_id,
1706
- resource_type="view",
1707
- app_key=app_key,
1708
- view_kind="custom",
1709
- view_key=view.get("view_key") or view.get("viewKey"),
1710
- )
1711
- return result
1712
1638
 
1713
1639
  def app_read_fields(self, *, profile: str, app_key: str) -> JSONObject:
1714
1640
  """执行应用相关逻辑。"""
@@ -1801,27 +1727,12 @@ class AiBuilderTools(ToolBase):
1801
1727
  read_one=lambda item_app_key: self.app_get_views(profile=profile, app_key=item_app_key, app_keys=None),
1802
1728
  )
1803
1729
  normalized_args = {"app_key": app_key}
1804
- result = _safe_tool_call(
1730
+ return _safe_tool_call(
1805
1731
  lambda: self._facade.app_get_views(profile=profile, app_key=app_key),
1806
1732
  error_code="APP_GET_VIEWS_FAILED",
1807
1733
  normalized_args=normalized_args,
1808
1734
  suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
1809
1735
  )
1810
- if result.get("status") == "success":
1811
- ws_id = self._selected_ws_id(profile)
1812
- _attach_uri(result, result, ws_id=ws_id, resource_type="app", app_key=app_key)
1813
- for view in result.get("views") or []:
1814
- if isinstance(view, dict) and (view.get("view_key") or view.get("viewKey")):
1815
- _attach_uri(
1816
- view,
1817
- result,
1818
- ws_id=ws_id,
1819
- resource_type="view",
1820
- app_key=app_key,
1821
- view_kind="custom",
1822
- view_key=view.get("view_key") or view.get("viewKey"),
1823
- )
1824
- return result
1825
1736
 
1826
1737
  def app_read_flow_summary(self, *, profile: str, app_key: str) -> JSONObject:
1827
1738
  """执行应用相关逻辑。"""
@@ -1960,27 +1871,12 @@ class AiBuilderTools(ToolBase):
1960
1871
  """执行视图相关逻辑。"""
1961
1872
  resolved_view_key = _normalize_builder_view_key(str(view_key or viewgraph_key or "").strip())
1962
1873
  normalized_args = {"view_key": resolved_view_key}
1963
- result = _safe_tool_call(
1874
+ return _safe_tool_call(
1964
1875
  lambda: self._facade.view_get(profile=profile, view_key=resolved_view_key),
1965
1876
  error_code="VIEW_GET_FAILED",
1966
1877
  normalized_args=normalized_args,
1967
1878
  suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, **normalized_args}},
1968
1879
  )
1969
- if result.get("status") == "success":
1970
- config = result.get("config") if isinstance(result.get("config"), dict) else {}
1971
- base_info = result.get("base_info") if isinstance(result.get("base_info"), dict) else {}
1972
- app_key = str(config.get("appKey") or config.get("formKey") or base_info.get("appKey") or base_info.get("formKey") or "").strip()
1973
- if app_key:
1974
- _attach_uri(
1975
- result,
1976
- result,
1977
- ws_id=self._selected_ws_id(profile),
1978
- resource_type="view",
1979
- app_key=app_key,
1980
- view_kind="custom",
1981
- view_key=resolved_view_key,
1982
- )
1983
- return result
1984
1880
 
1985
1881
  def chart_get(
1986
1882
  self,
@@ -2269,7 +2165,7 @@ class AiBuilderTools(ToolBase):
2269
2165
  )
2270
2166
  failure = normalized_apps_payload.get("failure")
2271
2167
  if isinstance(failure, dict):
2272
- return self._attach_apply_envelope(profile, "app_schema_apply", failure)
2168
+ return _attach_builder_apply_envelope("app_schema_apply", failure)
2273
2169
  package_id = normalized_apps_payload.get("package_id") # type: ignore[assignment]
2274
2170
  apps = normalized_apps_payload.get("apps") # type: ignore[assignment]
2275
2171
  input_warnings = list(normalized_apps_payload.get("warnings") or [])
@@ -2285,7 +2181,7 @@ class AiBuilderTools(ToolBase):
2285
2181
  )
2286
2182
  failure = form_result.get("failure")
2287
2183
  if isinstance(failure, dict):
2288
- return self._attach_apply_envelope(profile, "app_schema_apply", failure)
2184
+ return _attach_builder_apply_envelope("app_schema_apply", failure)
2289
2185
  normalized_apps.append(form_result.get("payload") or app_item)
2290
2186
  apps = normalized_apps
2291
2187
  result = self._app_schema_apply_multi(
@@ -2299,7 +2195,7 @@ class AiBuilderTools(ToolBase):
2299
2195
  result_warnings = list(result.get("warnings") or [])
2300
2196
  result_warnings.extend(deepcopy(input_warnings))
2301
2197
  result["warnings"] = result_warnings
2302
- return self._attach_apply_envelope(profile, "app_schema_apply", result)
2198
+ return _attach_builder_apply_envelope("app_schema_apply", result)
2303
2199
  inline_form_layout_sections: list[JSONObject] = []
2304
2200
  if form:
2305
2201
  form_payload: JSONObject = {"form": form}
@@ -2312,7 +2208,7 @@ class AiBuilderTools(ToolBase):
2312
2208
  )
2313
2209
  failure = form_result.get("failure")
2314
2210
  if isinstance(failure, dict):
2315
- return self._attach_apply_envelope(profile, "app_schema_apply", failure)
2211
+ return _attach_builder_apply_envelope("app_schema_apply", failure)
2316
2212
  normalized_payload = form_result.get("payload") if isinstance(form_result.get("payload"), dict) else {}
2317
2213
  add_fields = list(normalized_payload.get("add_fields") or [])
2318
2214
  inline_form_layout_sections = list(form_result.get("layout_sections") or [])
@@ -2350,7 +2246,7 @@ class AiBuilderTools(ToolBase):
2350
2246
  inline_form_layout_sections=inline_form_layout_sections,
2351
2247
  ),
2352
2248
  )
2353
- return self._attach_apply_envelope(profile, "app_schema_apply", result)
2249
+ return _attach_builder_apply_envelope("app_schema_apply", result)
2354
2250
 
2355
2251
  def _app_schema_apply_multi(
2356
2252
  self,
@@ -3629,7 +3525,7 @@ class AiBuilderTools(ToolBase):
3629
3525
  sections=sections,
3630
3526
  ),
3631
3527
  )
3632
- return self._attach_apply_envelope(profile, "app_layout_apply", result)
3528
+ return _attach_builder_apply_envelope("app_layout_apply", result)
3633
3529
 
3634
3530
  def _app_layout_apply_once(self, *, profile: str, app_key: str, mode: str = "merge", publish: bool = True, sections: list[JSONObject]) -> JSONObject:
3635
3531
  """执行内部辅助逻辑。"""
@@ -3694,7 +3590,7 @@ class AiBuilderTools(ToolBase):
3694
3590
  "idempotency_key": idempotency_key,
3695
3591
  "schema_version": schema_version,
3696
3592
  }
3697
- return self._attach_apply_envelope(profile, "app_flow_apply", _safe_tool_call(
3593
+ return _attach_builder_apply_envelope("app_flow_apply", _safe_tool_call(
3698
3594
  lambda: self._facade.flow_patch_nodes(
3699
3595
  profile=profile,
3700
3596
  app_key=app_key,
@@ -3715,7 +3611,7 @@ class AiBuilderTools(ToolBase):
3715
3611
  "idempotency_key": idempotency_key,
3716
3612
  "schema_version": schema_version,
3717
3613
  }
3718
- return self._attach_apply_envelope(profile, "app_flow_apply", _safe_tool_call(
3614
+ return _attach_builder_apply_envelope("app_flow_apply", _safe_tool_call(
3719
3615
  lambda: self._facade.flow_apply(
3720
3616
  profile=profile,
3721
3617
  app_key=app_key,
@@ -3751,9 +3647,9 @@ class AiBuilderTools(ToolBase):
3751
3647
  transitions=effective_transitions,
3752
3648
  ),
3753
3649
  )
3754
- return self._attach_apply_envelope(profile, "app_flow_apply", result)
3650
+ return _attach_builder_apply_envelope("app_flow_apply", result)
3755
3651
  normalized_args = {"app_key": app_key, "publish": publish}
3756
- return self._attach_apply_envelope(profile,
3652
+ return _attach_builder_apply_envelope(
3757
3653
  "app_flow_apply",
3758
3654
  {
3759
3655
  "status": "failed",
@@ -3888,7 +3784,7 @@ class AiBuilderTools(ToolBase):
3888
3784
  patch_views=patch_views or [],
3889
3785
  ),
3890
3786
  )
3891
- return self._attach_apply_envelope(profile, "app_views_apply", result)
3787
+ return _attach_builder_apply_envelope("app_views_apply", result)
3892
3788
 
3893
3789
  def _app_views_apply_view_batch(
3894
3790
  self,
@@ -3899,7 +3795,7 @@ class AiBuilderTools(ToolBase):
3899
3795
  ) -> JSONObject:
3900
3796
  started_at = time.perf_counter()
3901
3797
  if not isinstance(views, list) or not views:
3902
- return self._attach_apply_envelope(profile,
3798
+ return _attach_builder_apply_envelope(
3903
3799
  "app_views_apply",
3904
3800
  _config_failure(
3905
3801
  tool_name="app_views_apply",
@@ -4130,7 +4026,7 @@ class AiBuilderTools(ToolBase):
4130
4026
  view_write_parallel_wall_ms=view_write_parallel_wall_ms,
4131
4027
  total_ms=_elapsed_ms(started_at),
4132
4028
  )
4133
- return self._attach_apply_envelope(profile, "app_views_apply", payload)
4029
+ return _attach_builder_apply_envelope("app_views_apply", payload)
4134
4030
 
4135
4031
  def _app_views_apply_once(
4136
4032
  self,
@@ -4253,7 +4149,7 @@ class AiBuilderTools(ToolBase):
4253
4149
  }
4254
4150
  )
4255
4151
  except ValidationError as exc:
4256
- return self._attach_apply_envelope(profile, "app_charts_apply", _visibility_validation_failure(
4152
+ return _attach_builder_apply_envelope("app_charts_apply", _visibility_validation_failure(
4257
4153
  str(exc),
4258
4154
  tool_name="app_charts_apply",
4259
4155
  exc=exc,
@@ -4269,7 +4165,7 @@ class AiBuilderTools(ToolBase):
4269
4165
  },
4270
4166
  ))
4271
4167
  normalized_args = request.model_dump(mode="json")
4272
- return self._attach_apply_envelope(profile, "app_charts_apply", _safe_tool_call(
4168
+ return _attach_builder_apply_envelope("app_charts_apply", _safe_tool_call(
4273
4169
  lambda: self._facade.chart_apply(profile=profile, request=request),
4274
4170
  error_code="CHART_APPLY_FAILED",
4275
4171
  normalized_args=normalized_args,
@@ -4305,7 +4201,7 @@ class AiBuilderTools(ToolBase):
4305
4201
  "publish": publish,
4306
4202
  "patch_sections": patch_sections,
4307
4203
  }
4308
- return self._attach_apply_envelope(profile, "portal_apply", _publicize_package_fields(_safe_tool_call(
4204
+ return _attach_builder_apply_envelope("portal_apply", _publicize_package_fields(_safe_tool_call(
4309
4205
  lambda: self._facade.portal_patch_sections(
4310
4206
  profile=profile,
4311
4207
  dash_key=dash_key,
@@ -4352,7 +4248,7 @@ class AiBuilderTools(ToolBase):
4352
4248
  try:
4353
4249
  request = PortalApplyRequest.model_validate(request_payload)
4354
4250
  except ValidationError as exc:
4355
- return self._attach_apply_envelope(profile, "portal_apply", _visibility_validation_failure(
4251
+ return _attach_builder_apply_envelope("portal_apply", _visibility_validation_failure(
4356
4252
  str(exc),
4357
4253
  tool_name="portal_apply",
4358
4254
  exc=exc,
@@ -4383,14 +4279,14 @@ class AiBuilderTools(ToolBase):
4383
4279
  creating=not bool(str(request.dash_key or "").strip()),
4384
4280
  )
4385
4281
  if icon_failure is not None:
4386
- return self._attach_apply_envelope(profile, "portal_apply", icon_failure)
4282
+ return _attach_builder_apply_envelope("portal_apply", icon_failure)
4387
4283
  result = _publicize_package_fields(_safe_tool_call(
4388
4284
  lambda: self._facade.portal_apply(profile=profile, request=request),
4389
4285
  error_code="PORTAL_APPLY_FAILED",
4390
4286
  normalized_args=normalized_args,
4391
4287
  suggested_next_call={"tool_name": "portal_apply", "arguments": {"profile": profile, **normalized_args}},
4392
4288
  ))
4393
- return self._attach_apply_envelope(profile, "portal_apply", result)
4289
+ return _attach_builder_apply_envelope("portal_apply", result)
4394
4290
 
4395
4291
  def portal_delete(self, *, profile: str, dash_key: str) -> JSONObject:
4396
4292
  """执行门户删除逻辑。"""
@@ -4401,7 +4297,7 @@ class AiBuilderTools(ToolBase):
4401
4297
  normalized_args=normalized_args,
4402
4298
  suggested_next_call={"tool_name": "portal_delete", "arguments": {"profile": profile, **normalized_args}},
4403
4299
  ))
4404
- return self._attach_apply_envelope(profile, "portal_delete", result)
4300
+ return _attach_builder_apply_envelope("portal_delete", result)
4405
4301
 
4406
4302
  def app_publish_verify(
4407
4303
  self,
@@ -4440,7 +4336,7 @@ class AiBuilderTools(ToolBase):
4440
4336
  expected_package_tag_id=expected_package_id,
4441
4337
  ),
4442
4338
  ))
4443
- return self._attach_apply_envelope(profile, "app_publish_verify", result)
4339
+ return _attach_builder_apply_envelope("app_publish_verify", result)
4444
4340
 
4445
4341
  def _retry_after_self_lock_release(self, *, profile: str, result: JSONObject, retry_call) -> JSONObject:
4446
4342
  """执行内部辅助逻辑。"""
@@ -5806,68 +5702,6 @@ def _publicize_package_fields(value):
5806
5702
  return public
5807
5703
 
5808
5704
 
5809
- def _append_uri_warning(payload: JSONObject, warning: JSONObject | None) -> None:
5810
- if not warning:
5811
- return
5812
- warnings = payload.setdefault("warnings", [])
5813
- if isinstance(warnings, list) and not any(
5814
- isinstance(item, dict) and item.get("code") == warning.get("code") for item in warnings
5815
- ):
5816
- warnings.append(warning)
5817
-
5818
-
5819
- def _attach_uri(target: JSONObject, payload: JSONObject, *, ws_id: int, **identity: Any) -> None:
5820
- uri, warning = try_build_resource_uri(ws_id=ws_id, **identity)
5821
- if uri:
5822
- target["uri"] = uri
5823
- _append_uri_warning(payload, warning)
5824
-
5825
-
5826
- def _attach_builder_resource_uris(payload: JSONObject, resources: list[JSONObject], *, ws_id: int) -> None:
5827
- for resource in resources:
5828
- resource_type = str(resource.get("resource_type") or "")
5829
- ids = resource.get("ids") if isinstance(resource.get("ids"), dict) else {}
5830
- if resource_type == "package":
5831
- _attach_uri(
5832
- resource,
5833
- payload,
5834
- ws_id=ws_id,
5835
- resource_type="package",
5836
- package_id=resource.get("id") or ids.get("package_id"),
5837
- )
5838
- elif resource_type == "app":
5839
- _attach_uri(
5840
- resource,
5841
- payload,
5842
- ws_id=ws_id,
5843
- resource_type="app",
5844
- app_key=resource.get("key") or ids.get("app_key"),
5845
- )
5846
- elif resource_type == "view":
5847
- parent = resource.get("parent") if isinstance(resource.get("parent"), dict) else {}
5848
- _attach_uri(
5849
- resource,
5850
- payload,
5851
- ws_id=ws_id,
5852
- resource_type="view",
5853
- app_key=ids.get("app_key") or (parent.get("key") if parent.get("resource_type") == "app" else None),
5854
- view_kind="custom",
5855
- view_key=resource.get("key") or ids.get("view_key"),
5856
- )
5857
-
5858
-
5859
- def _attach_package_item_uris(items: Any, payload: JSONObject, *, ws_id: int) -> None:
5860
- if not isinstance(items, list):
5861
- return
5862
- for item in items:
5863
- if not isinstance(item, dict):
5864
- continue
5865
- if item.get("type") == "app" and item.get("app_key"):
5866
- _attach_uri(item, payload, ws_id=ws_id, resource_type="app", app_key=item.get("app_key"))
5867
- if item.get("type") == "group":
5868
- _attach_package_item_uris(item.get("items"), payload, ws_id=ws_id)
5869
-
5870
-
5871
5705
  def _builder_contract_with_apply_output(tool_name: str, contract: JSONObject) -> JSONObject:
5872
5706
  public = deepcopy(contract)
5873
5707
  if tool_name not in BUILDER_APPLY_TOOL_NAMES:
@@ -5880,7 +5714,7 @@ def _builder_contract_with_apply_output(tool_name: str, contract: JSONObject) ->
5880
5714
  public["output_contract"] = {
5881
5715
  "schema_version": BUILDER_APPLY_SCHEMA_VERSION,
5882
5716
  "preferred_ui_fields": ["operation", "summary", "resources"],
5883
- "resource_fields": ["resource_type", "operation", "status", "id", "key", "name", "ids", "parent", "uri", "icon_config", "error_code", "message"],
5717
+ "resource_fields": ["resource_type", "operation", "status", "id", "key", "name", "ids", "parent", "icon_config", "error_code", "message"],
5884
5718
  "json_paths": _builder_apply_json_paths(),
5885
5719
  "legacy_fields_preserved": True,
5886
5720
  }
@@ -5932,7 +5766,6 @@ def _builder_apply_json_paths() -> JSONObject:
5932
5766
  "resource_keys": "$.resources[].key",
5933
5767
  "resource_ids": "$.resources[].id",
5934
5768
  "resource_statuses": "$.resources[].status",
5935
- "resource_uris": "$.resources[].uri",
5936
5769
  }
5937
5770
 
5938
5771
 
@@ -6022,9 +5855,7 @@ def _builder_verification_truthy(value: object) -> bool:
6022
5855
 
6023
5856
  def _builder_apply_resources(tool_name: str, payload: JSONObject) -> list[JSONObject]:
6024
5857
  resources: list[JSONObject]
6025
- if tool_name == "solution_install":
6026
- resources = [deepcopy(item) for item in payload.get("resources") or [] if isinstance(item, dict)]
6027
- elif tool_name == "package_apply":
5858
+ if tool_name == "package_apply":
6028
5859
  resources = _builder_package_resources(payload)
6029
5860
  elif tool_name == "app_schema_apply":
6030
5861
  resources = _builder_schema_resources(payload)