@qingflow-tech/qingflow-cli 1.1.48 → 1.1.50-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/npm/lib/runtime.mjs +29 -13
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +92 -410
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +4 -4
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +3 -137
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +6 -4
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +14 -14
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +20 -19
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +18 -14
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +7 -12
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +2 -1
- package/skills/qingflow-cli/reference/builder/workflow/03-flow-patterns.md +13 -5
- package/skills/qingflow-cli/reference/builder/workflow/07-stage4-validate-spec.md +5 -3
- package/skills/qingflow-cli/reference/builder/workflow/08-stage5-apply-verify.md +1 -1
- package/skills/qingflow-cli/reference/builder/workflow/11-troubleshooting.md +1 -1
- package/skills/qingflow-cli/reference/builder/workflow/workflow-schema.json +1665 -1738
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +76 -95
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_EXPLORATION_REPORT.md +6 -4
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_FIELD_DATA_TYPES.md +13 -13
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +20 -19
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +18 -14
- package/skills/qingflow-cli/reference/data/README.md +32 -0
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_SPECIALIZED_WORKFLOW.md +50 -0
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +2 -1
- package/skills/qingflow-cli/reference/record/analysis/README.md +21 -113
- package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +10 -10
- package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +9 -9
- package/skills/qingflow-cli/reference/record/analysis/business-context.md +1 -1
- package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +10 -10
- package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +52 -80
- package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +2 -2
- package/skills/qingflow-cli/scripts/workflow/validate_flow_spec.py +48 -46
- package/skills/qingflow-common/SKILL.md +10 -17
- package/src/qingflow_mcp/backend_client.py +142 -12
- package/src/qingflow_mcp/builder_facade/flow_validation.py +569 -0
- package/src/qingflow_mcp/builder_facade/service.py +762 -6
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +154 -2
- package/src/qingflow_mcp/cli/commands/__init__.py +2 -1
- package/src/qingflow_mcp/cli/commands/auth.py +1 -1
- package/src/qingflow_mcp/cli/commands/builder.py +57 -41
- package/src/qingflow_mcp/cli/commands/data.py +134 -0
- package/src/qingflow_mcp/cli/commands/record.py +1 -1
- package/src/qingflow_mcp/cli/commands/task.py +10 -12
- package/src/qingflow_mcp/cli/commands/workspace.py +20 -7
- package/src/qingflow_mcp/cli/context.py +4 -1
- package/src/qingflow_mcp/cli/formatters.py +177 -21
- package/src/qingflow_mcp/cli/main.py +4 -1
- package/src/qingflow_mcp/cli/pagination.py +44 -0
- package/src/qingflow_mcp/config.py +60 -0
- package/src/qingflow_mcp/public_surface.py +9 -0
- package/src/qingflow_mcp/resource_uri.py +241 -0
- package/src/qingflow_mcp/response_trim.py +56 -5
- package/src/qingflow_mcp/server.py +2 -0
- package/src/qingflow_mcp/server_app_builder.py +57 -2
- package/src/qingflow_mcp/server_app_user.py +10 -3
- package/src/qingflow_mcp/tools/ai_builder_tools.py +374 -41
- package/src/qingflow_mcp/tools/app_tools.py +52 -13
- package/src/qingflow_mcp/tools/csv_export.py +168 -0
- package/src/qingflow_mcp/tools/data_tools.py +284 -0
- package/src/qingflow_mcp/tools/login_link_tools.py +34 -6
- package/src/qingflow_mcp/tools/record_tools.py +85 -61
- package/src/qingflow_mcp/tools/resource_read_tools.py +13 -0
- package/src/qingflow_mcp/tools/task_context_tools.py +21 -4
- package/src/qingflow_mcp/tools/workspace_tools.py +9 -12
|
@@ -20,6 +20,7 @@ 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
|
|
23
24
|
from ..builder_facade.models import (
|
|
24
25
|
AssociatedResourcesApplyRequest,
|
|
25
26
|
ChartApplyRequest,
|
|
@@ -187,6 +188,26 @@ class AiBuilderTools(ToolBase):
|
|
|
187
188
|
registry=app_form_schema_registry(),
|
|
188
189
|
)
|
|
189
190
|
|
|
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
|
+
|
|
190
211
|
def _apply_app_batch(self, *, tool_name: str, profile: str, apps: list[JSONObject], apply_one) -> JSONObject:
|
|
191
212
|
started_at = time.perf_counter()
|
|
192
213
|
batch_item_apply_ms = 0
|
|
@@ -199,7 +220,7 @@ class AiBuilderTools(ToolBase):
|
|
|
199
220
|
child_total_ms=child_total_ms,
|
|
200
221
|
total_ms=_elapsed_ms(started_at),
|
|
201
222
|
)
|
|
202
|
-
return
|
|
223
|
+
return self._attach_apply_envelope(profile, tool_name, payload)
|
|
203
224
|
|
|
204
225
|
if not isinstance(apps, list) or not apps:
|
|
205
226
|
return finish(
|
|
@@ -677,6 +698,41 @@ class AiBuilderTools(ToolBase):
|
|
|
677
698
|
def app_flow_get_schema(profile: str = DEFAULT_PROFILE, schema_version: str = "") -> JSONObject:
|
|
678
699
|
return self.app_flow_get_schema(profile=profile, schema_version=schema_version or None)
|
|
679
700
|
|
|
701
|
+
@mcp.tool()
|
|
702
|
+
def app_flow_validate(
|
|
703
|
+
profile: str = DEFAULT_PROFILE,
|
|
704
|
+
spec: JSONObject | None = None,
|
|
705
|
+
schema_version: str = "",
|
|
706
|
+
) -> JSONObject:
|
|
707
|
+
return self.app_flow_validate(
|
|
708
|
+
profile=profile,
|
|
709
|
+
spec=spec or {},
|
|
710
|
+
schema_version=schema_version or None,
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
@mcp.tool()
|
|
714
|
+
def app_flow_list_versions(
|
|
715
|
+
profile: str = DEFAULT_PROFILE,
|
|
716
|
+
app_key: str = "",
|
|
717
|
+
) -> JSONObject:
|
|
718
|
+
return self.app_flow_list_versions(profile=profile, app_key=app_key)
|
|
719
|
+
|
|
720
|
+
@mcp.tool()
|
|
721
|
+
def app_flow_restore_version(
|
|
722
|
+
profile: str = DEFAULT_PROFILE,
|
|
723
|
+
app_key: str = "",
|
|
724
|
+
version_id: str = "",
|
|
725
|
+
idempotency_key: str = "",
|
|
726
|
+
schema_version: str = "",
|
|
727
|
+
) -> JSONObject:
|
|
728
|
+
return self.app_flow_restore_version(
|
|
729
|
+
profile=profile,
|
|
730
|
+
app_key=app_key,
|
|
731
|
+
version_id=version_id,
|
|
732
|
+
idempotency_key=idempotency_key or None,
|
|
733
|
+
schema_version=schema_version or None,
|
|
734
|
+
)
|
|
735
|
+
|
|
680
736
|
@mcp.tool()
|
|
681
737
|
def app_flow_apply(
|
|
682
738
|
profile: str = DEFAULT_PROFILE,
|
|
@@ -686,7 +742,10 @@ class AiBuilderTools(ToolBase):
|
|
|
686
742
|
idempotency_key: str = "",
|
|
687
743
|
schema_version: str = "",
|
|
688
744
|
patch_nodes: list[JSONObject] | None = None,
|
|
745
|
+
confirm_ticket: str = "",
|
|
746
|
+
base_version: int | None = None,
|
|
689
747
|
) -> JSONObject:
|
|
748
|
+
"""执行应用相关逻辑。"""
|
|
690
749
|
return self.app_flow_apply(
|
|
691
750
|
profile=profile,
|
|
692
751
|
app_key=app_key,
|
|
@@ -695,6 +754,8 @@ class AiBuilderTools(ToolBase):
|
|
|
695
754
|
idempotency_key=idempotency_key or None,
|
|
696
755
|
schema_version=schema_version or None,
|
|
697
756
|
patch_nodes=patch_nodes,
|
|
757
|
+
confirm_ticket=confirm_ticket or None,
|
|
758
|
+
base_version=base_version,
|
|
698
759
|
)
|
|
699
760
|
|
|
700
761
|
@mcp.tool()
|
|
@@ -841,16 +902,30 @@ class AiBuilderTools(ToolBase):
|
|
|
841
902
|
being_draft: bool = False,
|
|
842
903
|
) -> JSONObject:
|
|
843
904
|
"""Read a canonical draft or published AppForm resource."""
|
|
844
|
-
|
|
905
|
+
result = self._app_form.get(
|
|
845
906
|
profile=profile,
|
|
846
907
|
app_key=app_key,
|
|
847
908
|
schema_version=schema_version,
|
|
848
909
|
being_draft=being_draft,
|
|
849
910
|
)
|
|
911
|
+
if result.get("status") == "success":
|
|
912
|
+
_attach_uri(result, result, ws_id=self._selected_ws_id(profile), resource_type="app", app_key=app_key)
|
|
913
|
+
return result
|
|
850
914
|
|
|
851
915
|
def app_form_apply(self, *, profile: str, spec: JSONObject) -> JSONObject:
|
|
852
916
|
"""Reconcile one complete declarative AppForm target."""
|
|
853
|
-
|
|
917
|
+
result = self._app_form.apply(profile=profile, spec=spec)
|
|
918
|
+
if result.get("appKey") and (
|
|
919
|
+
str(result.get("status") or "") == "success" or result.get("createdAppPreserved") is True
|
|
920
|
+
):
|
|
921
|
+
_attach_uri(
|
|
922
|
+
result,
|
|
923
|
+
result,
|
|
924
|
+
ws_id=self._selected_ws_id(profile),
|
|
925
|
+
resource_type="app",
|
|
926
|
+
app_key=result.get("appKey"),
|
|
927
|
+
)
|
|
928
|
+
return result
|
|
854
929
|
|
|
855
930
|
def app_form_evaluate_formula(
|
|
856
931
|
self,
|
|
@@ -917,12 +992,24 @@ class AiBuilderTools(ToolBase):
|
|
|
917
992
|
def package_list(self, *, profile: str, trial_status: str = "all", query: str = "") -> JSONObject:
|
|
918
993
|
"""执行分组与包相关逻辑。"""
|
|
919
994
|
normalized_args = {"trial_status": trial_status, "query": query}
|
|
920
|
-
|
|
995
|
+
result = _safe_tool_call(
|
|
921
996
|
lambda: self._facade.package_list(profile=profile, trial_status=trial_status, query=query),
|
|
922
997
|
error_code="PACKAGE_LIST_FAILED",
|
|
923
998
|
normalized_args=normalized_args,
|
|
924
999
|
suggested_next_call={"tool_name": "package_list", "arguments": {"profile": profile, "trial_status": trial_status, "query": query}},
|
|
925
1000
|
)
|
|
1001
|
+
if result.get("status") == "success":
|
|
1002
|
+
ws_id = self._selected_ws_id(profile)
|
|
1003
|
+
for item in result.get("items") or []:
|
|
1004
|
+
if isinstance(item, dict):
|
|
1005
|
+
_attach_uri(
|
|
1006
|
+
item,
|
|
1007
|
+
result,
|
|
1008
|
+
ws_id=ws_id,
|
|
1009
|
+
resource_type="package",
|
|
1010
|
+
package_id=item.get("package_id"),
|
|
1011
|
+
)
|
|
1012
|
+
return result
|
|
926
1013
|
|
|
927
1014
|
def package_resolve(self, *, profile: str, package_name: str) -> JSONObject:
|
|
928
1015
|
"""执行分组与包相关逻辑。"""
|
|
@@ -1062,12 +1149,23 @@ class AiBuilderTools(ToolBase):
|
|
|
1062
1149
|
def package_get(self, *, profile: str, package_id: int | None = None) -> JSONObject:
|
|
1063
1150
|
"""执行分组与包相关逻辑。"""
|
|
1064
1151
|
normalized_args = {"package_id": package_id}
|
|
1065
|
-
|
|
1152
|
+
result = _publicize_package_fields(_safe_tool_call(
|
|
1066
1153
|
lambda: self._facade.package_get(profile=profile, package_id=package_id),
|
|
1067
1154
|
error_code="PACKAGE_GET_FAILED",
|
|
1068
1155
|
normalized_args=normalized_args,
|
|
1069
1156
|
suggested_next_call={"tool_name": "package_get", "arguments": {"profile": profile, "package_id": package_id}},
|
|
1070
1157
|
))
|
|
1158
|
+
if result.get("status") == "success":
|
|
1159
|
+
ws_id = self._selected_ws_id(profile)
|
|
1160
|
+
_attach_uri(
|
|
1161
|
+
result,
|
|
1162
|
+
result,
|
|
1163
|
+
ws_id=ws_id,
|
|
1164
|
+
resource_type="package",
|
|
1165
|
+
package_id=result.get("package_id"),
|
|
1166
|
+
)
|
|
1167
|
+
_attach_package_item_uris(result.get("items"), result, ws_id=ws_id)
|
|
1168
|
+
return result
|
|
1071
1169
|
|
|
1072
1170
|
def package_apply(
|
|
1073
1171
|
self,
|
|
@@ -1097,7 +1195,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1097
1195
|
try:
|
|
1098
1196
|
visibility_patch = VisibilityPatch.model_validate(visibility)
|
|
1099
1197
|
except ValidationError as exc:
|
|
1100
|
-
return
|
|
1198
|
+
return self._attach_apply_envelope(profile,
|
|
1101
1199
|
"package_apply",
|
|
1102
1200
|
_visibility_validation_failure(str(exc), tool_name="package_apply", exc=exc),
|
|
1103
1201
|
)
|
|
@@ -1108,7 +1206,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1108
1206
|
creating=package_id is None and bool(str(package_name or "").strip()),
|
|
1109
1207
|
)
|
|
1110
1208
|
if icon_failure is not None:
|
|
1111
|
-
return
|
|
1209
|
+
return self._attach_apply_envelope(profile, "package_apply", icon_failure)
|
|
1112
1210
|
normalized_args = {
|
|
1113
1211
|
"package_id": package_id,
|
|
1114
1212
|
**({"package_name": package_name} if str(package_name or "").strip() else {}),
|
|
@@ -1133,7 +1231,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1133
1231
|
normalized_args=normalized_args,
|
|
1134
1232
|
suggested_next_call={"tool_name": "package_apply", "arguments": {"profile": profile, **normalized_args}},
|
|
1135
1233
|
))
|
|
1136
|
-
return
|
|
1234
|
+
return self._attach_apply_envelope(profile, "package_apply", result)
|
|
1137
1235
|
|
|
1138
1236
|
def package_update(
|
|
1139
1237
|
self,
|
|
@@ -1187,7 +1285,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1187
1285
|
"being_copy_data": being_copy_data,
|
|
1188
1286
|
"solution_source": solution_source,
|
|
1189
1287
|
}
|
|
1190
|
-
|
|
1288
|
+
result = _safe_tool_call(
|
|
1191
1289
|
lambda: self._facade.solution_install(
|
|
1192
1290
|
profile=profile,
|
|
1193
1291
|
solution_key=solution_key,
|
|
@@ -1198,6 +1296,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1198
1296
|
normalized_args=normalized_args,
|
|
1199
1297
|
suggested_next_call={"tool_name": "solution_install", "arguments": {"profile": profile, **normalized_args}},
|
|
1200
1298
|
)
|
|
1299
|
+
return self._attach_apply_envelope(profile, "solution_install", result)
|
|
1201
1300
|
|
|
1202
1301
|
def member_search(
|
|
1203
1302
|
self,
|
|
@@ -1394,7 +1493,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1394
1493
|
try:
|
|
1395
1494
|
request = CustomButtonsApplyRequest.model_validate(raw_request)
|
|
1396
1495
|
except ValidationError as exc:
|
|
1397
|
-
return
|
|
1496
|
+
return self._attach_apply_envelope(profile, "app_custom_buttons_apply", _validation_failure(
|
|
1398
1497
|
str(exc),
|
|
1399
1498
|
tool_name="app_custom_buttons_apply",
|
|
1400
1499
|
exc=exc,
|
|
@@ -1418,7 +1517,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1418
1517
|
},
|
|
1419
1518
|
))
|
|
1420
1519
|
normalized_args = request.model_dump(mode="json")
|
|
1421
|
-
return
|
|
1520
|
+
return self._attach_apply_envelope(profile, "app_custom_buttons_apply", _safe_tool_call(
|
|
1422
1521
|
lambda: self._facade.app_custom_buttons_apply(profile=profile, request=request),
|
|
1423
1522
|
error_code="CUSTOM_BUTTONS_APPLY_FAILED",
|
|
1424
1523
|
normalized_args=normalized_args,
|
|
@@ -1475,7 +1574,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1475
1574
|
try:
|
|
1476
1575
|
request = AssociatedResourcesApplyRequest.model_validate(raw_request)
|
|
1477
1576
|
except ValidationError as exc:
|
|
1478
|
-
return
|
|
1577
|
+
return self._attach_apply_envelope(profile, "app_associated_resources_apply", _validation_failure(
|
|
1479
1578
|
str(exc),
|
|
1480
1579
|
tool_name="app_associated_resources_apply",
|
|
1481
1580
|
exc=exc,
|
|
@@ -1504,7 +1603,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1504
1603
|
},
|
|
1505
1604
|
))
|
|
1506
1605
|
normalized_args = request.model_dump(mode="json", exclude_none=True)
|
|
1507
|
-
return
|
|
1606
|
+
return self._attach_apply_envelope(profile, "app_associated_resources_apply", _safe_tool_call(
|
|
1508
1607
|
lambda: self._facade.app_associated_resources_apply(profile=profile, request=request),
|
|
1509
1608
|
error_code="ASSOCIATED_RESOURCES_APPLY_FAILED",
|
|
1510
1609
|
normalized_args=normalized_args,
|
|
@@ -1629,12 +1728,27 @@ class AiBuilderTools(ToolBase):
|
|
|
1629
1728
|
read_one=lambda item_app_key: self.app_get(profile=profile, app_key=item_app_key, app_keys=None),
|
|
1630
1729
|
)
|
|
1631
1730
|
normalized_args = {"app_key": app_key}
|
|
1632
|
-
|
|
1731
|
+
result = _publicize_package_fields(_safe_tool_call(
|
|
1633
1732
|
lambda: self._facade.app_get(profile=profile, app_key=app_key),
|
|
1634
1733
|
error_code="APP_GET_FAILED",
|
|
1635
1734
|
normalized_args=normalized_args,
|
|
1636
1735
|
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1637
1736
|
))
|
|
1737
|
+
if result.get("status") == "success":
|
|
1738
|
+
ws_id = self._selected_ws_id(profile)
|
|
1739
|
+
_attach_uri(result, result, ws_id=ws_id, resource_type="app", app_key=app_key)
|
|
1740
|
+
for view in result.get("views") or []:
|
|
1741
|
+
if isinstance(view, dict) and (view.get("view_key") or view.get("viewKey")):
|
|
1742
|
+
_attach_uri(
|
|
1743
|
+
view,
|
|
1744
|
+
result,
|
|
1745
|
+
ws_id=ws_id,
|
|
1746
|
+
resource_type="view",
|
|
1747
|
+
app_key=app_key,
|
|
1748
|
+
view_kind="custom",
|
|
1749
|
+
view_key=view.get("view_key") or view.get("viewKey"),
|
|
1750
|
+
)
|
|
1751
|
+
return result
|
|
1638
1752
|
|
|
1639
1753
|
def app_read_fields(self, *, profile: str, app_key: str) -> JSONObject:
|
|
1640
1754
|
"""执行应用相关逻辑。"""
|
|
@@ -1727,12 +1841,27 @@ class AiBuilderTools(ToolBase):
|
|
|
1727
1841
|
read_one=lambda item_app_key: self.app_get_views(profile=profile, app_key=item_app_key, app_keys=None),
|
|
1728
1842
|
)
|
|
1729
1843
|
normalized_args = {"app_key": app_key}
|
|
1730
|
-
|
|
1844
|
+
result = _safe_tool_call(
|
|
1731
1845
|
lambda: self._facade.app_get_views(profile=profile, app_key=app_key),
|
|
1732
1846
|
error_code="APP_GET_VIEWS_FAILED",
|
|
1733
1847
|
normalized_args=normalized_args,
|
|
1734
1848
|
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1735
1849
|
)
|
|
1850
|
+
if result.get("status") == "success":
|
|
1851
|
+
ws_id = self._selected_ws_id(profile)
|
|
1852
|
+
_attach_uri(result, result, ws_id=ws_id, resource_type="app", app_key=app_key)
|
|
1853
|
+
for view in result.get("views") or []:
|
|
1854
|
+
if isinstance(view, dict) and (view.get("view_key") or view.get("viewKey")):
|
|
1855
|
+
_attach_uri(
|
|
1856
|
+
view,
|
|
1857
|
+
result,
|
|
1858
|
+
ws_id=ws_id,
|
|
1859
|
+
resource_type="view",
|
|
1860
|
+
app_key=app_key,
|
|
1861
|
+
view_kind="custom",
|
|
1862
|
+
view_key=view.get("view_key") or view.get("viewKey"),
|
|
1863
|
+
)
|
|
1864
|
+
return result
|
|
1736
1865
|
|
|
1737
1866
|
def app_read_flow_summary(self, *, profile: str, app_key: str) -> JSONObject:
|
|
1738
1867
|
"""执行应用相关逻辑。"""
|
|
@@ -1779,6 +1908,50 @@ class AiBuilderTools(ToolBase):
|
|
|
1779
1908
|
suggested_next_call={"tool_name": "app_flow_get_schema", "arguments": {"profile": profile, **normalized_args}},
|
|
1780
1909
|
)
|
|
1781
1910
|
|
|
1911
|
+
def app_flow_validate(self, *, profile: str, spec: JSONObject, schema_version: str | None = None) -> JSONObject:
|
|
1912
|
+
"""离线校验 WorkflowSpec(apply 前预检,不写后端)。"""
|
|
1913
|
+
normalized_args = {"spec": spec, "schema_version": schema_version}
|
|
1914
|
+
return _safe_tool_call(
|
|
1915
|
+
lambda: self._facade.flow_validate(profile=profile, spec=spec, schema_version=schema_version),
|
|
1916
|
+
error_code="APP_FLOW_VALIDATE_FAILED",
|
|
1917
|
+
normalized_args=normalized_args,
|
|
1918
|
+
suggested_next_call={"tool_name": "app_flow_get_schema", "arguments": {"profile": profile}},
|
|
1919
|
+
)
|
|
1920
|
+
|
|
1921
|
+
def app_flow_list_versions(self, *, profile: str, app_key: str) -> JSONObject:
|
|
1922
|
+
"""列出某应用的 WorkflowSpec 版本快照(version_id/snapshot_type/时间)。"""
|
|
1923
|
+
normalized_args = {"app_key": app_key}
|
|
1924
|
+
return _safe_tool_call(
|
|
1925
|
+
lambda: self._facade.flow_versions(profile=profile, app_key=app_key),
|
|
1926
|
+
error_code="APP_FLOW_LIST_VERSIONS_FAILED",
|
|
1927
|
+
normalized_args=normalized_args,
|
|
1928
|
+
suggested_next_call={"tool_name": "app_flow_list_versions", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1929
|
+
)
|
|
1930
|
+
|
|
1931
|
+
def app_flow_restore_version(
|
|
1932
|
+
self,
|
|
1933
|
+
*,
|
|
1934
|
+
profile: str,
|
|
1935
|
+
app_key: str,
|
|
1936
|
+
version_id: str,
|
|
1937
|
+
idempotency_key: str | None = None,
|
|
1938
|
+
schema_version: str | None = None,
|
|
1939
|
+
) -> JSONObject:
|
|
1940
|
+
"""回滚到指定版本快照,并回读当前 spec 与目标版本比对确认。"""
|
|
1941
|
+
normalized_args = {"app_key": app_key, "version_id": version_id}
|
|
1942
|
+
return _safe_tool_call(
|
|
1943
|
+
lambda: self._facade.flow_restore_version(
|
|
1944
|
+
profile=profile,
|
|
1945
|
+
app_key=app_key,
|
|
1946
|
+
version_id=version_id,
|
|
1947
|
+
idempotency_key=idempotency_key,
|
|
1948
|
+
schema_version=schema_version,
|
|
1949
|
+
),
|
|
1950
|
+
error_code="APP_FLOW_RESTORE_VERSION_FAILED",
|
|
1951
|
+
normalized_args=normalized_args,
|
|
1952
|
+
suggested_next_call={"tool_name": "app_flow_list_versions", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1953
|
+
)
|
|
1954
|
+
|
|
1782
1955
|
def app_read_charts_summary(self, *, profile: str, app_key: str) -> JSONObject:
|
|
1783
1956
|
"""执行应用相关逻辑。"""
|
|
1784
1957
|
normalized_args = {"app_key": app_key}
|
|
@@ -1871,12 +2044,27 @@ class AiBuilderTools(ToolBase):
|
|
|
1871
2044
|
"""执行视图相关逻辑。"""
|
|
1872
2045
|
resolved_view_key = _normalize_builder_view_key(str(view_key or viewgraph_key or "").strip())
|
|
1873
2046
|
normalized_args = {"view_key": resolved_view_key}
|
|
1874
|
-
|
|
2047
|
+
result = _safe_tool_call(
|
|
1875
2048
|
lambda: self._facade.view_get(profile=profile, view_key=resolved_view_key),
|
|
1876
2049
|
error_code="VIEW_GET_FAILED",
|
|
1877
2050
|
normalized_args=normalized_args,
|
|
1878
2051
|
suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, **normalized_args}},
|
|
1879
2052
|
)
|
|
2053
|
+
if result.get("status") == "success":
|
|
2054
|
+
config = result.get("config") if isinstance(result.get("config"), dict) else {}
|
|
2055
|
+
base_info = result.get("base_info") if isinstance(result.get("base_info"), dict) else {}
|
|
2056
|
+
app_key = str(config.get("appKey") or config.get("formKey") or base_info.get("appKey") or base_info.get("formKey") or "").strip()
|
|
2057
|
+
if app_key:
|
|
2058
|
+
_attach_uri(
|
|
2059
|
+
result,
|
|
2060
|
+
result,
|
|
2061
|
+
ws_id=self._selected_ws_id(profile),
|
|
2062
|
+
resource_type="view",
|
|
2063
|
+
app_key=app_key,
|
|
2064
|
+
view_kind="custom",
|
|
2065
|
+
view_key=resolved_view_key,
|
|
2066
|
+
)
|
|
2067
|
+
return result
|
|
1880
2068
|
|
|
1881
2069
|
def chart_get(
|
|
1882
2070
|
self,
|
|
@@ -2165,7 +2353,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2165
2353
|
)
|
|
2166
2354
|
failure = normalized_apps_payload.get("failure")
|
|
2167
2355
|
if isinstance(failure, dict):
|
|
2168
|
-
return
|
|
2356
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", failure)
|
|
2169
2357
|
package_id = normalized_apps_payload.get("package_id") # type: ignore[assignment]
|
|
2170
2358
|
apps = normalized_apps_payload.get("apps") # type: ignore[assignment]
|
|
2171
2359
|
input_warnings = list(normalized_apps_payload.get("warnings") or [])
|
|
@@ -2181,7 +2369,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2181
2369
|
)
|
|
2182
2370
|
failure = form_result.get("failure")
|
|
2183
2371
|
if isinstance(failure, dict):
|
|
2184
|
-
return
|
|
2372
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", failure)
|
|
2185
2373
|
normalized_apps.append(form_result.get("payload") or app_item)
|
|
2186
2374
|
apps = normalized_apps
|
|
2187
2375
|
result = self._app_schema_apply_multi(
|
|
@@ -2195,7 +2383,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2195
2383
|
result_warnings = list(result.get("warnings") or [])
|
|
2196
2384
|
result_warnings.extend(deepcopy(input_warnings))
|
|
2197
2385
|
result["warnings"] = result_warnings
|
|
2198
|
-
return
|
|
2386
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", result)
|
|
2199
2387
|
inline_form_layout_sections: list[JSONObject] = []
|
|
2200
2388
|
if form:
|
|
2201
2389
|
form_payload: JSONObject = {"form": form}
|
|
@@ -2208,7 +2396,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2208
2396
|
)
|
|
2209
2397
|
failure = form_result.get("failure")
|
|
2210
2398
|
if isinstance(failure, dict):
|
|
2211
|
-
return
|
|
2399
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", failure)
|
|
2212
2400
|
normalized_payload = form_result.get("payload") if isinstance(form_result.get("payload"), dict) else {}
|
|
2213
2401
|
add_fields = list(normalized_payload.get("add_fields") or [])
|
|
2214
2402
|
inline_form_layout_sections = list(form_result.get("layout_sections") or [])
|
|
@@ -2246,7 +2434,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2246
2434
|
inline_form_layout_sections=inline_form_layout_sections,
|
|
2247
2435
|
),
|
|
2248
2436
|
)
|
|
2249
|
-
return
|
|
2437
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", result)
|
|
2250
2438
|
|
|
2251
2439
|
def _app_schema_apply_multi(
|
|
2252
2440
|
self,
|
|
@@ -3525,7 +3713,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3525
3713
|
sections=sections,
|
|
3526
3714
|
),
|
|
3527
3715
|
)
|
|
3528
|
-
return
|
|
3716
|
+
return self._attach_apply_envelope(profile, "app_layout_apply", result)
|
|
3529
3717
|
|
|
3530
3718
|
def _app_layout_apply_once(self, *, profile: str, app_key: str, mode: str = "merge", publish: bool = True, sections: list[JSONObject]) -> JSONObject:
|
|
3531
3719
|
"""执行内部辅助逻辑。"""
|
|
@@ -3580,6 +3768,8 @@ class AiBuilderTools(ToolBase):
|
|
|
3580
3768
|
idempotency_key: str | None = None,
|
|
3581
3769
|
schema_version: str | None = None,
|
|
3582
3770
|
patch_nodes: list[JSONObject] | None = None,
|
|
3771
|
+
confirm_ticket: str | None = None,
|
|
3772
|
+
base_version: int | None = None,
|
|
3583
3773
|
) -> JSONObject:
|
|
3584
3774
|
"""执行应用相关逻辑。"""
|
|
3585
3775
|
if patch_nodes is not None:
|
|
@@ -3589,8 +3779,9 @@ class AiBuilderTools(ToolBase):
|
|
|
3589
3779
|
"patch_nodes": patch_nodes,
|
|
3590
3780
|
"idempotency_key": idempotency_key,
|
|
3591
3781
|
"schema_version": schema_version,
|
|
3782
|
+
"base_version": base_version,
|
|
3592
3783
|
}
|
|
3593
|
-
return
|
|
3784
|
+
return self._attach_apply_envelope(profile, "app_flow_apply", _safe_tool_call(
|
|
3594
3785
|
lambda: self._facade.flow_patch_nodes(
|
|
3595
3786
|
profile=profile,
|
|
3596
3787
|
app_key=app_key,
|
|
@@ -3610,8 +3801,10 @@ class AiBuilderTools(ToolBase):
|
|
|
3610
3801
|
"spec": spec,
|
|
3611
3802
|
"idempotency_key": idempotency_key,
|
|
3612
3803
|
"schema_version": schema_version,
|
|
3804
|
+
"confirm_ticket": confirm_ticket,
|
|
3805
|
+
"base_version": base_version,
|
|
3613
3806
|
}
|
|
3614
|
-
return
|
|
3807
|
+
return self._attach_apply_envelope(profile, "app_flow_apply", _safe_tool_call(
|
|
3615
3808
|
lambda: self._facade.flow_apply(
|
|
3616
3809
|
profile=profile,
|
|
3617
3810
|
app_key=app_key,
|
|
@@ -3619,6 +3812,8 @@ class AiBuilderTools(ToolBase):
|
|
|
3619
3812
|
publish=publish,
|
|
3620
3813
|
idempotency_key=idempotency_key,
|
|
3621
3814
|
schema_version=schema_version,
|
|
3815
|
+
confirm_ticket=confirm_ticket,
|
|
3816
|
+
base_version=base_version,
|
|
3622
3817
|
),
|
|
3623
3818
|
error_code="FLOW_SPEC_APPLY_FAILED",
|
|
3624
3819
|
normalized_args=normalized_args,
|
|
@@ -3647,9 +3842,9 @@ class AiBuilderTools(ToolBase):
|
|
|
3647
3842
|
transitions=effective_transitions,
|
|
3648
3843
|
),
|
|
3649
3844
|
)
|
|
3650
|
-
return
|
|
3845
|
+
return self._attach_apply_envelope(profile, "app_flow_apply", result)
|
|
3651
3846
|
normalized_args = {"app_key": app_key, "publish": publish}
|
|
3652
|
-
return
|
|
3847
|
+
return self._attach_apply_envelope(profile,
|
|
3653
3848
|
"app_flow_apply",
|
|
3654
3849
|
{
|
|
3655
3850
|
"status": "failed",
|
|
@@ -3784,7 +3979,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3784
3979
|
patch_views=patch_views or [],
|
|
3785
3980
|
),
|
|
3786
3981
|
)
|
|
3787
|
-
return
|
|
3982
|
+
return self._attach_apply_envelope(profile, "app_views_apply", result)
|
|
3788
3983
|
|
|
3789
3984
|
def _app_views_apply_view_batch(
|
|
3790
3985
|
self,
|
|
@@ -3795,7 +3990,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3795
3990
|
) -> JSONObject:
|
|
3796
3991
|
started_at = time.perf_counter()
|
|
3797
3992
|
if not isinstance(views, list) or not views:
|
|
3798
|
-
return
|
|
3993
|
+
return self._attach_apply_envelope(profile,
|
|
3799
3994
|
"app_views_apply",
|
|
3800
3995
|
_config_failure(
|
|
3801
3996
|
tool_name="app_views_apply",
|
|
@@ -4026,7 +4221,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4026
4221
|
view_write_parallel_wall_ms=view_write_parallel_wall_ms,
|
|
4027
4222
|
total_ms=_elapsed_ms(started_at),
|
|
4028
4223
|
)
|
|
4029
|
-
return
|
|
4224
|
+
return self._attach_apply_envelope(profile, "app_views_apply", payload)
|
|
4030
4225
|
|
|
4031
4226
|
def _app_views_apply_once(
|
|
4032
4227
|
self,
|
|
@@ -4149,7 +4344,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4149
4344
|
}
|
|
4150
4345
|
)
|
|
4151
4346
|
except ValidationError as exc:
|
|
4152
|
-
return
|
|
4347
|
+
return self._attach_apply_envelope(profile, "app_charts_apply", _visibility_validation_failure(
|
|
4153
4348
|
str(exc),
|
|
4154
4349
|
tool_name="app_charts_apply",
|
|
4155
4350
|
exc=exc,
|
|
@@ -4165,7 +4360,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4165
4360
|
},
|
|
4166
4361
|
))
|
|
4167
4362
|
normalized_args = request.model_dump(mode="json")
|
|
4168
|
-
return
|
|
4363
|
+
return self._attach_apply_envelope(profile, "app_charts_apply", _safe_tool_call(
|
|
4169
4364
|
lambda: self._facade.chart_apply(profile=profile, request=request),
|
|
4170
4365
|
error_code="CHART_APPLY_FAILED",
|
|
4171
4366
|
normalized_args=normalized_args,
|
|
@@ -4201,7 +4396,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4201
4396
|
"publish": publish,
|
|
4202
4397
|
"patch_sections": patch_sections,
|
|
4203
4398
|
}
|
|
4204
|
-
return
|
|
4399
|
+
return self._attach_apply_envelope(profile, "portal_apply", _publicize_package_fields(_safe_tool_call(
|
|
4205
4400
|
lambda: self._facade.portal_patch_sections(
|
|
4206
4401
|
profile=profile,
|
|
4207
4402
|
dash_key=dash_key,
|
|
@@ -4248,7 +4443,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4248
4443
|
try:
|
|
4249
4444
|
request = PortalApplyRequest.model_validate(request_payload)
|
|
4250
4445
|
except ValidationError as exc:
|
|
4251
|
-
return
|
|
4446
|
+
return self._attach_apply_envelope(profile, "portal_apply", _visibility_validation_failure(
|
|
4252
4447
|
str(exc),
|
|
4253
4448
|
tool_name="portal_apply",
|
|
4254
4449
|
exc=exc,
|
|
@@ -4279,14 +4474,14 @@ class AiBuilderTools(ToolBase):
|
|
|
4279
4474
|
creating=not bool(str(request.dash_key or "").strip()),
|
|
4280
4475
|
)
|
|
4281
4476
|
if icon_failure is not None:
|
|
4282
|
-
return
|
|
4477
|
+
return self._attach_apply_envelope(profile, "portal_apply", icon_failure)
|
|
4283
4478
|
result = _publicize_package_fields(_safe_tool_call(
|
|
4284
4479
|
lambda: self._facade.portal_apply(profile=profile, request=request),
|
|
4285
4480
|
error_code="PORTAL_APPLY_FAILED",
|
|
4286
4481
|
normalized_args=normalized_args,
|
|
4287
4482
|
suggested_next_call={"tool_name": "portal_apply", "arguments": {"profile": profile, **normalized_args}},
|
|
4288
4483
|
))
|
|
4289
|
-
return
|
|
4484
|
+
return self._attach_apply_envelope(profile, "portal_apply", result)
|
|
4290
4485
|
|
|
4291
4486
|
def portal_delete(self, *, profile: str, dash_key: str) -> JSONObject:
|
|
4292
4487
|
"""执行门户删除逻辑。"""
|
|
@@ -4297,7 +4492,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4297
4492
|
normalized_args=normalized_args,
|
|
4298
4493
|
suggested_next_call={"tool_name": "portal_delete", "arguments": {"profile": profile, **normalized_args}},
|
|
4299
4494
|
))
|
|
4300
|
-
return
|
|
4495
|
+
return self._attach_apply_envelope(profile, "portal_delete", result)
|
|
4301
4496
|
|
|
4302
4497
|
def app_publish_verify(
|
|
4303
4498
|
self,
|
|
@@ -4336,7 +4531,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4336
4531
|
expected_package_tag_id=expected_package_id,
|
|
4337
4532
|
),
|
|
4338
4533
|
))
|
|
4339
|
-
return
|
|
4534
|
+
return self._attach_apply_envelope(profile, "app_publish_verify", result)
|
|
4340
4535
|
|
|
4341
4536
|
def _retry_after_self_lock_release(self, *, profile: str, result: JSONObject, retry_call) -> JSONObject:
|
|
4342
4537
|
"""执行内部辅助逻辑。"""
|
|
@@ -5613,7 +5808,7 @@ def _safe_tool_call(
|
|
|
5613
5808
|
result = call()
|
|
5614
5809
|
except (QingflowApiError, RuntimeError) as error:
|
|
5615
5810
|
api_error = _coerce_api_error(error)
|
|
5616
|
-
|
|
5811
|
+
# #162 D5:404 不再抹除 http_status,错误信封保留真实传输层事实
|
|
5617
5812
|
result = {
|
|
5618
5813
|
"status": "failed",
|
|
5619
5814
|
"error_code": error_code,
|
|
@@ -5632,7 +5827,7 @@ def _safe_tool_call(
|
|
|
5632
5827
|
"suggested_next_call": suggested_next_call,
|
|
5633
5828
|
"request_id": api_error.request_id,
|
|
5634
5829
|
"backend_code": api_error.backend_code,
|
|
5635
|
-
"http_status":
|
|
5830
|
+
"http_status": api_error.http_status,
|
|
5636
5831
|
"noop": False,
|
|
5637
5832
|
"verification": {},
|
|
5638
5833
|
}
|
|
@@ -5702,6 +5897,68 @@ def _publicize_package_fields(value):
|
|
|
5702
5897
|
return public
|
|
5703
5898
|
|
|
5704
5899
|
|
|
5900
|
+
def _append_uri_warning(payload: JSONObject, warning: JSONObject | None) -> None:
|
|
5901
|
+
if not warning:
|
|
5902
|
+
return
|
|
5903
|
+
warnings = payload.setdefault("warnings", [])
|
|
5904
|
+
if isinstance(warnings, list) and not any(
|
|
5905
|
+
isinstance(item, dict) and item.get("code") == warning.get("code") for item in warnings
|
|
5906
|
+
):
|
|
5907
|
+
warnings.append(warning)
|
|
5908
|
+
|
|
5909
|
+
|
|
5910
|
+
def _attach_uri(target: JSONObject, payload: JSONObject, *, ws_id: int, **identity: Any) -> None:
|
|
5911
|
+
uri, warning = try_build_resource_uri(ws_id=ws_id, **identity)
|
|
5912
|
+
if uri:
|
|
5913
|
+
target["uri"] = uri
|
|
5914
|
+
_append_uri_warning(payload, warning)
|
|
5915
|
+
|
|
5916
|
+
|
|
5917
|
+
def _attach_builder_resource_uris(payload: JSONObject, resources: list[JSONObject], *, ws_id: int) -> None:
|
|
5918
|
+
for resource in resources:
|
|
5919
|
+
resource_type = str(resource.get("resource_type") or "")
|
|
5920
|
+
ids = resource.get("ids") if isinstance(resource.get("ids"), dict) else {}
|
|
5921
|
+
if resource_type == "package":
|
|
5922
|
+
_attach_uri(
|
|
5923
|
+
resource,
|
|
5924
|
+
payload,
|
|
5925
|
+
ws_id=ws_id,
|
|
5926
|
+
resource_type="package",
|
|
5927
|
+
package_id=resource.get("id") or ids.get("package_id"),
|
|
5928
|
+
)
|
|
5929
|
+
elif resource_type == "app":
|
|
5930
|
+
_attach_uri(
|
|
5931
|
+
resource,
|
|
5932
|
+
payload,
|
|
5933
|
+
ws_id=ws_id,
|
|
5934
|
+
resource_type="app",
|
|
5935
|
+
app_key=resource.get("key") or ids.get("app_key"),
|
|
5936
|
+
)
|
|
5937
|
+
elif resource_type == "view":
|
|
5938
|
+
parent = resource.get("parent") if isinstance(resource.get("parent"), dict) else {}
|
|
5939
|
+
_attach_uri(
|
|
5940
|
+
resource,
|
|
5941
|
+
payload,
|
|
5942
|
+
ws_id=ws_id,
|
|
5943
|
+
resource_type="view",
|
|
5944
|
+
app_key=ids.get("app_key") or (parent.get("key") if parent.get("resource_type") == "app" else None),
|
|
5945
|
+
view_kind="custom",
|
|
5946
|
+
view_key=resource.get("key") or ids.get("view_key"),
|
|
5947
|
+
)
|
|
5948
|
+
|
|
5949
|
+
|
|
5950
|
+
def _attach_package_item_uris(items: Any, payload: JSONObject, *, ws_id: int) -> None:
|
|
5951
|
+
if not isinstance(items, list):
|
|
5952
|
+
return
|
|
5953
|
+
for item in items:
|
|
5954
|
+
if not isinstance(item, dict):
|
|
5955
|
+
continue
|
|
5956
|
+
if item.get("type") == "app" and item.get("app_key"):
|
|
5957
|
+
_attach_uri(item, payload, ws_id=ws_id, resource_type="app", app_key=item.get("app_key"))
|
|
5958
|
+
if item.get("type") == "group":
|
|
5959
|
+
_attach_package_item_uris(item.get("items"), payload, ws_id=ws_id)
|
|
5960
|
+
|
|
5961
|
+
|
|
5705
5962
|
def _builder_contract_with_apply_output(tool_name: str, contract: JSONObject) -> JSONObject:
|
|
5706
5963
|
public = deepcopy(contract)
|
|
5707
5964
|
if tool_name not in BUILDER_APPLY_TOOL_NAMES:
|
|
@@ -5714,7 +5971,7 @@ def _builder_contract_with_apply_output(tool_name: str, contract: JSONObject) ->
|
|
|
5714
5971
|
public["output_contract"] = {
|
|
5715
5972
|
"schema_version": BUILDER_APPLY_SCHEMA_VERSION,
|
|
5716
5973
|
"preferred_ui_fields": ["operation", "summary", "resources"],
|
|
5717
|
-
"resource_fields": ["resource_type", "operation", "status", "id", "key", "name", "ids", "parent", "icon_config", "error_code", "message"],
|
|
5974
|
+
"resource_fields": ["resource_type", "operation", "status", "id", "key", "name", "ids", "parent", "uri", "icon_config", "error_code", "message"],
|
|
5718
5975
|
"json_paths": _builder_apply_json_paths(),
|
|
5719
5976
|
"legacy_fields_preserved": True,
|
|
5720
5977
|
}
|
|
@@ -5766,6 +6023,7 @@ def _builder_apply_json_paths() -> JSONObject:
|
|
|
5766
6023
|
"resource_keys": "$.resources[].key",
|
|
5767
6024
|
"resource_ids": "$.resources[].id",
|
|
5768
6025
|
"resource_statuses": "$.resources[].status",
|
|
6026
|
+
"resource_uris": "$.resources[].uri",
|
|
5769
6027
|
}
|
|
5770
6028
|
|
|
5771
6029
|
|
|
@@ -5855,7 +6113,9 @@ def _builder_verification_truthy(value: object) -> bool:
|
|
|
5855
6113
|
|
|
5856
6114
|
def _builder_apply_resources(tool_name: str, payload: JSONObject) -> list[JSONObject]:
|
|
5857
6115
|
resources: list[JSONObject]
|
|
5858
|
-
if tool_name == "
|
|
6116
|
+
if tool_name == "solution_install":
|
|
6117
|
+
resources = [deepcopy(item) for item in payload.get("resources") or [] if isinstance(item, dict)]
|
|
6118
|
+
elif tool_name == "package_apply":
|
|
5859
6119
|
resources = _builder_package_resources(payload)
|
|
5860
6120
|
elif tool_name == "app_schema_apply":
|
|
5861
6121
|
resources = _builder_schema_resources(payload)
|
|
@@ -6620,6 +6880,10 @@ def _public_error_message(error_code: str, error: QingflowApiError) -> str:
|
|
|
6620
6880
|
return "app is currently locked by another active editor session"
|
|
6621
6881
|
if error.http_status != 404:
|
|
6622
6882
|
return error.message
|
|
6883
|
+
# 404 优先保留后端原始 message(agent 排障依据);仅当原始 message 为空时
|
|
6884
|
+
# 才落多路由环境泛化兑底(#162 D5:不再默认泛化丢失真实原因)。
|
|
6885
|
+
if error.message:
|
|
6886
|
+
return f"{error.message} (HTTP 404)"
|
|
6623
6887
|
mapping = {
|
|
6624
6888
|
"PACKAGE_LIST_FAILED": "package list is unavailable in the current route",
|
|
6625
6889
|
"PACKAGE_RESOLVE_FAILED": "package resolution is unavailable in the current route",
|
|
@@ -7811,6 +8075,75 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
7811
8075
|
"patch_nodes": [{"id": "approve_1", "set": {"name": "主管审批"}}],
|
|
7812
8076
|
},
|
|
7813
8077
|
},
|
|
8078
|
+
"app_flow_validate": {
|
|
8079
|
+
"allowed_keys": [
|
|
8080
|
+
"spec",
|
|
8081
|
+
"schema_version",
|
|
8082
|
+
],
|
|
8083
|
+
"aliases": {
|
|
8084
|
+
"schemaVersion": "schema_version",
|
|
8085
|
+
},
|
|
8086
|
+
"allowed_values": {},
|
|
8087
|
+
"dependency_hints": [
|
|
8088
|
+
"run app_flow_validate on the drafted WorkflowSpec before app_flow_apply to catch schema/structure issues without writing",
|
|
8089
|
+
],
|
|
8090
|
+
"execution_notes": [
|
|
8091
|
+
"offline precheck: fetches the WorkflowSpec JSON Schema and runs schema + structural + semantic lint checks",
|
|
8092
|
+
"never writes: the only network call is the schema GET; publish=false is a draft write, not a precheck",
|
|
8093
|
+
"issues[] blocks apply; warnings[] (e.g. rejection-edge cycles) are advisory only",
|
|
8094
|
+
"server-side field-reference semantics are validated at apply time; a clean validate does not guarantee apply success",
|
|
8095
|
+
],
|
|
8096
|
+
"minimal_example": {
|
|
8097
|
+
"profile": "default",
|
|
8098
|
+
"spec": {"schemaVersion": "1", "nodes": [], "edges": []},
|
|
8099
|
+
},
|
|
8100
|
+
},
|
|
8101
|
+
"app_flow_list_versions": {
|
|
8102
|
+
"allowed_keys": [
|
|
8103
|
+
"app_key",
|
|
8104
|
+
],
|
|
8105
|
+
"aliases": {},
|
|
8106
|
+
"allowed_values": {},
|
|
8107
|
+
"dependency_hints": [
|
|
8108
|
+
"use app_flow_list_versions to discover version_id values before app_flow_restore_version or app_flow_get(version_id=...)",
|
|
8109
|
+
],
|
|
8110
|
+
"execution_notes": [
|
|
8111
|
+
"returns items[] with version_id, snapshot_type, created_at_ms/created_at_iso, operator_name, spec_hash per version snapshot",
|
|
8112
|
+
"raw backend entries ride along in details.versions of the full envelope for troubleshooting",
|
|
8113
|
+
],
|
|
8114
|
+
"minimal_example": {
|
|
8115
|
+
"profile": "default",
|
|
8116
|
+
"app_key": "APP_KEY",
|
|
8117
|
+
},
|
|
8118
|
+
},
|
|
8119
|
+
"app_flow_restore_version": {
|
|
8120
|
+
"allowed_keys": [
|
|
8121
|
+
"app_key",
|
|
8122
|
+
"version_id",
|
|
8123
|
+
"idempotency_key",
|
|
8124
|
+
"schema_version",
|
|
8125
|
+
],
|
|
8126
|
+
"aliases": {
|
|
8127
|
+
"versionId": "version_id",
|
|
8128
|
+
"idempotencyKey": "idempotency_key",
|
|
8129
|
+
"schemaVersion": "schema_version",
|
|
8130
|
+
},
|
|
8131
|
+
"allowed_values": {},
|
|
8132
|
+
"dependency_hints": [
|
|
8133
|
+
"resolve version_id via app_flow_list_versions, optionally inspect it with app_flow_get(version_id=...), then restore",
|
|
8134
|
+
],
|
|
8135
|
+
"execution_notes": [
|
|
8136
|
+
"draft-domain write: restores the draft workflow spec to the target version snapshot and does not auto-publish",
|
|
8137
|
+
"maintenance gate applies: restore to a version with more nodes than the threshold is blocked (FLOW_EDIT_UNDER_MAINTENANCE)",
|
|
8138
|
+
"readback verification compares the restored current spec with the target version; mismatch yields partial_success with FLOW_RESTORE_VERIFY_FAILED",
|
|
8139
|
+
"verify the outcome with app_flow_get afterwards when verification reports partial_success",
|
|
8140
|
+
],
|
|
8141
|
+
"minimal_example": {
|
|
8142
|
+
"profile": "default",
|
|
8143
|
+
"app_key": "APP_KEY",
|
|
8144
|
+
"version_id": "v_1765000000_ab12cd34",
|
|
8145
|
+
},
|
|
8146
|
+
},
|
|
7814
8147
|
"app_views_plan": {
|
|
7815
8148
|
"allowed_keys": [
|
|
7816
8149
|
"app_key",
|