@qingflow-tech/qingflow-cli 1.1.47 → 1.1.48-beta.2
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 +91 -396
- 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/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_LEGACY_RECORD_RETRIEVAL_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-common/SKILL.md +13 -4
- package/src/qingflow_mcp/builder_facade/service.py +119 -1
- package/src/qingflow_mcp/cli/commands/__init__.py +2 -1
- package/src/qingflow_mcp/cli/commands/auth.py +8 -0
- 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/context.py +6 -0
- package/src/qingflow_mcp/cli/formatters.py +163 -3
- package/src/qingflow_mcp/cli/main.py +4 -1
- package/src/qingflow_mcp/config.py +39 -0
- package/src/qingflow_mcp/public_surface.py +7 -0
- package/src/qingflow_mcp/resource_uri.py +241 -0
- package/src/qingflow_mcp/response_trim.py +10 -2
- package/src/qingflow_mcp/server.py +4 -0
- package/src/qingflow_mcp/server_app_builder.py +16 -0
- package/src/qingflow_mcp/server_app_user.py +24 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +208 -39
- 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 +271 -0
- package/src/qingflow_mcp/tools/login_link_tools.py +142 -0
- package/src/qingflow_mcp/tools/record_tools.py +11 -46
- package/src/qingflow_mcp/tools/resource_read_tools.py +13 -0
|
@@ -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(
|
|
@@ -841,16 +862,30 @@ class AiBuilderTools(ToolBase):
|
|
|
841
862
|
being_draft: bool = False,
|
|
842
863
|
) -> JSONObject:
|
|
843
864
|
"""Read a canonical draft or published AppForm resource."""
|
|
844
|
-
|
|
865
|
+
result = self._app_form.get(
|
|
845
866
|
profile=profile,
|
|
846
867
|
app_key=app_key,
|
|
847
868
|
schema_version=schema_version,
|
|
848
869
|
being_draft=being_draft,
|
|
849
870
|
)
|
|
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
|
|
850
874
|
|
|
851
875
|
def app_form_apply(self, *, profile: str, spec: JSONObject) -> JSONObject:
|
|
852
876
|
"""Reconcile one complete declarative AppForm target."""
|
|
853
|
-
|
|
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
|
|
854
889
|
|
|
855
890
|
def app_form_evaluate_formula(
|
|
856
891
|
self,
|
|
@@ -917,12 +952,24 @@ class AiBuilderTools(ToolBase):
|
|
|
917
952
|
def package_list(self, *, profile: str, trial_status: str = "all", query: str = "") -> JSONObject:
|
|
918
953
|
"""执行分组与包相关逻辑。"""
|
|
919
954
|
normalized_args = {"trial_status": trial_status, "query": query}
|
|
920
|
-
|
|
955
|
+
result = _safe_tool_call(
|
|
921
956
|
lambda: self._facade.package_list(profile=profile, trial_status=trial_status, query=query),
|
|
922
957
|
error_code="PACKAGE_LIST_FAILED",
|
|
923
958
|
normalized_args=normalized_args,
|
|
924
959
|
suggested_next_call={"tool_name": "package_list", "arguments": {"profile": profile, "trial_status": trial_status, "query": query}},
|
|
925
960
|
)
|
|
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
|
|
926
973
|
|
|
927
974
|
def package_resolve(self, *, profile: str, package_name: str) -> JSONObject:
|
|
928
975
|
"""执行分组与包相关逻辑。"""
|
|
@@ -1062,12 +1109,23 @@ class AiBuilderTools(ToolBase):
|
|
|
1062
1109
|
def package_get(self, *, profile: str, package_id: int | None = None) -> JSONObject:
|
|
1063
1110
|
"""执行分组与包相关逻辑。"""
|
|
1064
1111
|
normalized_args = {"package_id": package_id}
|
|
1065
|
-
|
|
1112
|
+
result = _publicize_package_fields(_safe_tool_call(
|
|
1066
1113
|
lambda: self._facade.package_get(profile=profile, package_id=package_id),
|
|
1067
1114
|
error_code="PACKAGE_GET_FAILED",
|
|
1068
1115
|
normalized_args=normalized_args,
|
|
1069
1116
|
suggested_next_call={"tool_name": "package_get", "arguments": {"profile": profile, "package_id": package_id}},
|
|
1070
1117
|
))
|
|
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
|
|
1071
1129
|
|
|
1072
1130
|
def package_apply(
|
|
1073
1131
|
self,
|
|
@@ -1097,7 +1155,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1097
1155
|
try:
|
|
1098
1156
|
visibility_patch = VisibilityPatch.model_validate(visibility)
|
|
1099
1157
|
except ValidationError as exc:
|
|
1100
|
-
return
|
|
1158
|
+
return self._attach_apply_envelope(profile,
|
|
1101
1159
|
"package_apply",
|
|
1102
1160
|
_visibility_validation_failure(str(exc), tool_name="package_apply", exc=exc),
|
|
1103
1161
|
)
|
|
@@ -1108,7 +1166,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1108
1166
|
creating=package_id is None and bool(str(package_name or "").strip()),
|
|
1109
1167
|
)
|
|
1110
1168
|
if icon_failure is not None:
|
|
1111
|
-
return
|
|
1169
|
+
return self._attach_apply_envelope(profile, "package_apply", icon_failure)
|
|
1112
1170
|
normalized_args = {
|
|
1113
1171
|
"package_id": package_id,
|
|
1114
1172
|
**({"package_name": package_name} if str(package_name or "").strip() else {}),
|
|
@@ -1133,7 +1191,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1133
1191
|
normalized_args=normalized_args,
|
|
1134
1192
|
suggested_next_call={"tool_name": "package_apply", "arguments": {"profile": profile, **normalized_args}},
|
|
1135
1193
|
))
|
|
1136
|
-
return
|
|
1194
|
+
return self._attach_apply_envelope(profile, "package_apply", result)
|
|
1137
1195
|
|
|
1138
1196
|
def package_update(
|
|
1139
1197
|
self,
|
|
@@ -1187,7 +1245,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1187
1245
|
"being_copy_data": being_copy_data,
|
|
1188
1246
|
"solution_source": solution_source,
|
|
1189
1247
|
}
|
|
1190
|
-
|
|
1248
|
+
result = _safe_tool_call(
|
|
1191
1249
|
lambda: self._facade.solution_install(
|
|
1192
1250
|
profile=profile,
|
|
1193
1251
|
solution_key=solution_key,
|
|
@@ -1198,6 +1256,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1198
1256
|
normalized_args=normalized_args,
|
|
1199
1257
|
suggested_next_call={"tool_name": "solution_install", "arguments": {"profile": profile, **normalized_args}},
|
|
1200
1258
|
)
|
|
1259
|
+
return self._attach_apply_envelope(profile, "solution_install", result)
|
|
1201
1260
|
|
|
1202
1261
|
def member_search(
|
|
1203
1262
|
self,
|
|
@@ -1394,7 +1453,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1394
1453
|
try:
|
|
1395
1454
|
request = CustomButtonsApplyRequest.model_validate(raw_request)
|
|
1396
1455
|
except ValidationError as exc:
|
|
1397
|
-
return
|
|
1456
|
+
return self._attach_apply_envelope(profile, "app_custom_buttons_apply", _validation_failure(
|
|
1398
1457
|
str(exc),
|
|
1399
1458
|
tool_name="app_custom_buttons_apply",
|
|
1400
1459
|
exc=exc,
|
|
@@ -1418,7 +1477,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1418
1477
|
},
|
|
1419
1478
|
))
|
|
1420
1479
|
normalized_args = request.model_dump(mode="json")
|
|
1421
|
-
return
|
|
1480
|
+
return self._attach_apply_envelope(profile, "app_custom_buttons_apply", _safe_tool_call(
|
|
1422
1481
|
lambda: self._facade.app_custom_buttons_apply(profile=profile, request=request),
|
|
1423
1482
|
error_code="CUSTOM_BUTTONS_APPLY_FAILED",
|
|
1424
1483
|
normalized_args=normalized_args,
|
|
@@ -1475,7 +1534,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1475
1534
|
try:
|
|
1476
1535
|
request = AssociatedResourcesApplyRequest.model_validate(raw_request)
|
|
1477
1536
|
except ValidationError as exc:
|
|
1478
|
-
return
|
|
1537
|
+
return self._attach_apply_envelope(profile, "app_associated_resources_apply", _validation_failure(
|
|
1479
1538
|
str(exc),
|
|
1480
1539
|
tool_name="app_associated_resources_apply",
|
|
1481
1540
|
exc=exc,
|
|
@@ -1504,7 +1563,7 @@ class AiBuilderTools(ToolBase):
|
|
|
1504
1563
|
},
|
|
1505
1564
|
))
|
|
1506
1565
|
normalized_args = request.model_dump(mode="json", exclude_none=True)
|
|
1507
|
-
return
|
|
1566
|
+
return self._attach_apply_envelope(profile, "app_associated_resources_apply", _safe_tool_call(
|
|
1508
1567
|
lambda: self._facade.app_associated_resources_apply(profile=profile, request=request),
|
|
1509
1568
|
error_code="ASSOCIATED_RESOURCES_APPLY_FAILED",
|
|
1510
1569
|
normalized_args=normalized_args,
|
|
@@ -1629,12 +1688,27 @@ class AiBuilderTools(ToolBase):
|
|
|
1629
1688
|
read_one=lambda item_app_key: self.app_get(profile=profile, app_key=item_app_key, app_keys=None),
|
|
1630
1689
|
)
|
|
1631
1690
|
normalized_args = {"app_key": app_key}
|
|
1632
|
-
|
|
1691
|
+
result = _publicize_package_fields(_safe_tool_call(
|
|
1633
1692
|
lambda: self._facade.app_get(profile=profile, app_key=app_key),
|
|
1634
1693
|
error_code="APP_GET_FAILED",
|
|
1635
1694
|
normalized_args=normalized_args,
|
|
1636
1695
|
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1637
1696
|
))
|
|
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
|
|
1638
1712
|
|
|
1639
1713
|
def app_read_fields(self, *, profile: str, app_key: str) -> JSONObject:
|
|
1640
1714
|
"""执行应用相关逻辑。"""
|
|
@@ -1727,12 +1801,27 @@ class AiBuilderTools(ToolBase):
|
|
|
1727
1801
|
read_one=lambda item_app_key: self.app_get_views(profile=profile, app_key=item_app_key, app_keys=None),
|
|
1728
1802
|
)
|
|
1729
1803
|
normalized_args = {"app_key": app_key}
|
|
1730
|
-
|
|
1804
|
+
result = _safe_tool_call(
|
|
1731
1805
|
lambda: self._facade.app_get_views(profile=profile, app_key=app_key),
|
|
1732
1806
|
error_code="APP_GET_VIEWS_FAILED",
|
|
1733
1807
|
normalized_args=normalized_args,
|
|
1734
1808
|
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1735
1809
|
)
|
|
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
|
|
1736
1825
|
|
|
1737
1826
|
def app_read_flow_summary(self, *, profile: str, app_key: str) -> JSONObject:
|
|
1738
1827
|
"""执行应用相关逻辑。"""
|
|
@@ -1871,12 +1960,27 @@ class AiBuilderTools(ToolBase):
|
|
|
1871
1960
|
"""执行视图相关逻辑。"""
|
|
1872
1961
|
resolved_view_key = _normalize_builder_view_key(str(view_key or viewgraph_key or "").strip())
|
|
1873
1962
|
normalized_args = {"view_key": resolved_view_key}
|
|
1874
|
-
|
|
1963
|
+
result = _safe_tool_call(
|
|
1875
1964
|
lambda: self._facade.view_get(profile=profile, view_key=resolved_view_key),
|
|
1876
1965
|
error_code="VIEW_GET_FAILED",
|
|
1877
1966
|
normalized_args=normalized_args,
|
|
1878
1967
|
suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, **normalized_args}},
|
|
1879
1968
|
)
|
|
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
|
|
1880
1984
|
|
|
1881
1985
|
def chart_get(
|
|
1882
1986
|
self,
|
|
@@ -2165,7 +2269,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2165
2269
|
)
|
|
2166
2270
|
failure = normalized_apps_payload.get("failure")
|
|
2167
2271
|
if isinstance(failure, dict):
|
|
2168
|
-
return
|
|
2272
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", failure)
|
|
2169
2273
|
package_id = normalized_apps_payload.get("package_id") # type: ignore[assignment]
|
|
2170
2274
|
apps = normalized_apps_payload.get("apps") # type: ignore[assignment]
|
|
2171
2275
|
input_warnings = list(normalized_apps_payload.get("warnings") or [])
|
|
@@ -2181,7 +2285,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2181
2285
|
)
|
|
2182
2286
|
failure = form_result.get("failure")
|
|
2183
2287
|
if isinstance(failure, dict):
|
|
2184
|
-
return
|
|
2288
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", failure)
|
|
2185
2289
|
normalized_apps.append(form_result.get("payload") or app_item)
|
|
2186
2290
|
apps = normalized_apps
|
|
2187
2291
|
result = self._app_schema_apply_multi(
|
|
@@ -2195,7 +2299,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2195
2299
|
result_warnings = list(result.get("warnings") or [])
|
|
2196
2300
|
result_warnings.extend(deepcopy(input_warnings))
|
|
2197
2301
|
result["warnings"] = result_warnings
|
|
2198
|
-
return
|
|
2302
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", result)
|
|
2199
2303
|
inline_form_layout_sections: list[JSONObject] = []
|
|
2200
2304
|
if form:
|
|
2201
2305
|
form_payload: JSONObject = {"form": form}
|
|
@@ -2208,7 +2312,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2208
2312
|
)
|
|
2209
2313
|
failure = form_result.get("failure")
|
|
2210
2314
|
if isinstance(failure, dict):
|
|
2211
|
-
return
|
|
2315
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", failure)
|
|
2212
2316
|
normalized_payload = form_result.get("payload") if isinstance(form_result.get("payload"), dict) else {}
|
|
2213
2317
|
add_fields = list(normalized_payload.get("add_fields") or [])
|
|
2214
2318
|
inline_form_layout_sections = list(form_result.get("layout_sections") or [])
|
|
@@ -2246,7 +2350,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2246
2350
|
inline_form_layout_sections=inline_form_layout_sections,
|
|
2247
2351
|
),
|
|
2248
2352
|
)
|
|
2249
|
-
return
|
|
2353
|
+
return self._attach_apply_envelope(profile, "app_schema_apply", result)
|
|
2250
2354
|
|
|
2251
2355
|
def _app_schema_apply_multi(
|
|
2252
2356
|
self,
|
|
@@ -3525,7 +3629,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3525
3629
|
sections=sections,
|
|
3526
3630
|
),
|
|
3527
3631
|
)
|
|
3528
|
-
return
|
|
3632
|
+
return self._attach_apply_envelope(profile, "app_layout_apply", result)
|
|
3529
3633
|
|
|
3530
3634
|
def _app_layout_apply_once(self, *, profile: str, app_key: str, mode: str = "merge", publish: bool = True, sections: list[JSONObject]) -> JSONObject:
|
|
3531
3635
|
"""执行内部辅助逻辑。"""
|
|
@@ -3590,7 +3694,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3590
3694
|
"idempotency_key": idempotency_key,
|
|
3591
3695
|
"schema_version": schema_version,
|
|
3592
3696
|
}
|
|
3593
|
-
return
|
|
3697
|
+
return self._attach_apply_envelope(profile, "app_flow_apply", _safe_tool_call(
|
|
3594
3698
|
lambda: self._facade.flow_patch_nodes(
|
|
3595
3699
|
profile=profile,
|
|
3596
3700
|
app_key=app_key,
|
|
@@ -3611,7 +3715,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3611
3715
|
"idempotency_key": idempotency_key,
|
|
3612
3716
|
"schema_version": schema_version,
|
|
3613
3717
|
}
|
|
3614
|
-
return
|
|
3718
|
+
return self._attach_apply_envelope(profile, "app_flow_apply", _safe_tool_call(
|
|
3615
3719
|
lambda: self._facade.flow_apply(
|
|
3616
3720
|
profile=profile,
|
|
3617
3721
|
app_key=app_key,
|
|
@@ -3647,9 +3751,9 @@ class AiBuilderTools(ToolBase):
|
|
|
3647
3751
|
transitions=effective_transitions,
|
|
3648
3752
|
),
|
|
3649
3753
|
)
|
|
3650
|
-
return
|
|
3754
|
+
return self._attach_apply_envelope(profile, "app_flow_apply", result)
|
|
3651
3755
|
normalized_args = {"app_key": app_key, "publish": publish}
|
|
3652
|
-
return
|
|
3756
|
+
return self._attach_apply_envelope(profile,
|
|
3653
3757
|
"app_flow_apply",
|
|
3654
3758
|
{
|
|
3655
3759
|
"status": "failed",
|
|
@@ -3784,7 +3888,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3784
3888
|
patch_views=patch_views or [],
|
|
3785
3889
|
),
|
|
3786
3890
|
)
|
|
3787
|
-
return
|
|
3891
|
+
return self._attach_apply_envelope(profile, "app_views_apply", result)
|
|
3788
3892
|
|
|
3789
3893
|
def _app_views_apply_view_batch(
|
|
3790
3894
|
self,
|
|
@@ -3795,7 +3899,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3795
3899
|
) -> JSONObject:
|
|
3796
3900
|
started_at = time.perf_counter()
|
|
3797
3901
|
if not isinstance(views, list) or not views:
|
|
3798
|
-
return
|
|
3902
|
+
return self._attach_apply_envelope(profile,
|
|
3799
3903
|
"app_views_apply",
|
|
3800
3904
|
_config_failure(
|
|
3801
3905
|
tool_name="app_views_apply",
|
|
@@ -4026,7 +4130,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4026
4130
|
view_write_parallel_wall_ms=view_write_parallel_wall_ms,
|
|
4027
4131
|
total_ms=_elapsed_ms(started_at),
|
|
4028
4132
|
)
|
|
4029
|
-
return
|
|
4133
|
+
return self._attach_apply_envelope(profile, "app_views_apply", payload)
|
|
4030
4134
|
|
|
4031
4135
|
def _app_views_apply_once(
|
|
4032
4136
|
self,
|
|
@@ -4149,7 +4253,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4149
4253
|
}
|
|
4150
4254
|
)
|
|
4151
4255
|
except ValidationError as exc:
|
|
4152
|
-
return
|
|
4256
|
+
return self._attach_apply_envelope(profile, "app_charts_apply", _visibility_validation_failure(
|
|
4153
4257
|
str(exc),
|
|
4154
4258
|
tool_name="app_charts_apply",
|
|
4155
4259
|
exc=exc,
|
|
@@ -4165,7 +4269,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4165
4269
|
},
|
|
4166
4270
|
))
|
|
4167
4271
|
normalized_args = request.model_dump(mode="json")
|
|
4168
|
-
return
|
|
4272
|
+
return self._attach_apply_envelope(profile, "app_charts_apply", _safe_tool_call(
|
|
4169
4273
|
lambda: self._facade.chart_apply(profile=profile, request=request),
|
|
4170
4274
|
error_code="CHART_APPLY_FAILED",
|
|
4171
4275
|
normalized_args=normalized_args,
|
|
@@ -4201,7 +4305,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4201
4305
|
"publish": publish,
|
|
4202
4306
|
"patch_sections": patch_sections,
|
|
4203
4307
|
}
|
|
4204
|
-
return
|
|
4308
|
+
return self._attach_apply_envelope(profile, "portal_apply", _publicize_package_fields(_safe_tool_call(
|
|
4205
4309
|
lambda: self._facade.portal_patch_sections(
|
|
4206
4310
|
profile=profile,
|
|
4207
4311
|
dash_key=dash_key,
|
|
@@ -4248,7 +4352,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4248
4352
|
try:
|
|
4249
4353
|
request = PortalApplyRequest.model_validate(request_payload)
|
|
4250
4354
|
except ValidationError as exc:
|
|
4251
|
-
return
|
|
4355
|
+
return self._attach_apply_envelope(profile, "portal_apply", _visibility_validation_failure(
|
|
4252
4356
|
str(exc),
|
|
4253
4357
|
tool_name="portal_apply",
|
|
4254
4358
|
exc=exc,
|
|
@@ -4279,14 +4383,14 @@ class AiBuilderTools(ToolBase):
|
|
|
4279
4383
|
creating=not bool(str(request.dash_key or "").strip()),
|
|
4280
4384
|
)
|
|
4281
4385
|
if icon_failure is not None:
|
|
4282
|
-
return
|
|
4386
|
+
return self._attach_apply_envelope(profile, "portal_apply", icon_failure)
|
|
4283
4387
|
result = _publicize_package_fields(_safe_tool_call(
|
|
4284
4388
|
lambda: self._facade.portal_apply(profile=profile, request=request),
|
|
4285
4389
|
error_code="PORTAL_APPLY_FAILED",
|
|
4286
4390
|
normalized_args=normalized_args,
|
|
4287
4391
|
suggested_next_call={"tool_name": "portal_apply", "arguments": {"profile": profile, **normalized_args}},
|
|
4288
4392
|
))
|
|
4289
|
-
return
|
|
4393
|
+
return self._attach_apply_envelope(profile, "portal_apply", result)
|
|
4290
4394
|
|
|
4291
4395
|
def portal_delete(self, *, profile: str, dash_key: str) -> JSONObject:
|
|
4292
4396
|
"""执行门户删除逻辑。"""
|
|
@@ -4297,7 +4401,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4297
4401
|
normalized_args=normalized_args,
|
|
4298
4402
|
suggested_next_call={"tool_name": "portal_delete", "arguments": {"profile": profile, **normalized_args}},
|
|
4299
4403
|
))
|
|
4300
|
-
return
|
|
4404
|
+
return self._attach_apply_envelope(profile, "portal_delete", result)
|
|
4301
4405
|
|
|
4302
4406
|
def app_publish_verify(
|
|
4303
4407
|
self,
|
|
@@ -4336,7 +4440,7 @@ class AiBuilderTools(ToolBase):
|
|
|
4336
4440
|
expected_package_tag_id=expected_package_id,
|
|
4337
4441
|
),
|
|
4338
4442
|
))
|
|
4339
|
-
return
|
|
4443
|
+
return self._attach_apply_envelope(profile, "app_publish_verify", result)
|
|
4340
4444
|
|
|
4341
4445
|
def _retry_after_self_lock_release(self, *, profile: str, result: JSONObject, retry_call) -> JSONObject:
|
|
4342
4446
|
"""执行内部辅助逻辑。"""
|
|
@@ -5702,6 +5806,68 @@ def _publicize_package_fields(value):
|
|
|
5702
5806
|
return public
|
|
5703
5807
|
|
|
5704
5808
|
|
|
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
|
+
|
|
5705
5871
|
def _builder_contract_with_apply_output(tool_name: str, contract: JSONObject) -> JSONObject:
|
|
5706
5872
|
public = deepcopy(contract)
|
|
5707
5873
|
if tool_name not in BUILDER_APPLY_TOOL_NAMES:
|
|
@@ -5714,7 +5880,7 @@ def _builder_contract_with_apply_output(tool_name: str, contract: JSONObject) ->
|
|
|
5714
5880
|
public["output_contract"] = {
|
|
5715
5881
|
"schema_version": BUILDER_APPLY_SCHEMA_VERSION,
|
|
5716
5882
|
"preferred_ui_fields": ["operation", "summary", "resources"],
|
|
5717
|
-
"resource_fields": ["resource_type", "operation", "status", "id", "key", "name", "ids", "parent", "icon_config", "error_code", "message"],
|
|
5883
|
+
"resource_fields": ["resource_type", "operation", "status", "id", "key", "name", "ids", "parent", "uri", "icon_config", "error_code", "message"],
|
|
5718
5884
|
"json_paths": _builder_apply_json_paths(),
|
|
5719
5885
|
"legacy_fields_preserved": True,
|
|
5720
5886
|
}
|
|
@@ -5766,6 +5932,7 @@ def _builder_apply_json_paths() -> JSONObject:
|
|
|
5766
5932
|
"resource_keys": "$.resources[].key",
|
|
5767
5933
|
"resource_ids": "$.resources[].id",
|
|
5768
5934
|
"resource_statuses": "$.resources[].status",
|
|
5935
|
+
"resource_uris": "$.resources[].uri",
|
|
5769
5936
|
}
|
|
5770
5937
|
|
|
5771
5938
|
|
|
@@ -5855,7 +6022,9 @@ def _builder_verification_truthy(value: object) -> bool:
|
|
|
5855
6022
|
|
|
5856
6023
|
def _builder_apply_resources(tool_name: str, payload: JSONObject) -> list[JSONObject]:
|
|
5857
6024
|
resources: list[JSONObject]
|
|
5858
|
-
if tool_name == "
|
|
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":
|
|
5859
6028
|
resources = _builder_package_resources(payload)
|
|
5860
6029
|
elif tool_name == "app_schema_apply":
|
|
5861
6030
|
resources = _builder_schema_resources(payload)
|
|
@@ -10,6 +10,7 @@ from ..config import DEFAULT_PROFILE
|
|
|
10
10
|
from ..errors import QingflowApiError, backend_code_int, is_auth_like_error, raise_tool_error
|
|
11
11
|
from ..json_types import JSONObject
|
|
12
12
|
from ..list_type_labels import SYSTEM_VIEW_DEFINITIONS, get_app_publish_status_label
|
|
13
|
+
from ..resource_uri import try_build_resource_uri
|
|
13
14
|
from ..solution.compiler.icon_utils import workspace_icon_config
|
|
14
15
|
from .base import ToolBase
|
|
15
16
|
|
|
@@ -46,6 +47,15 @@ class AppTools(ToolBase):
|
|
|
46
47
|
unfiltered_count = len(items)
|
|
47
48
|
if normalized_query:
|
|
48
49
|
items = self._filter_visible_apps(items, normalized_query)
|
|
50
|
+
for item in items:
|
|
51
|
+
uri, warning = try_build_resource_uri(
|
|
52
|
+
ws_id=session_profile.selected_ws_id,
|
|
53
|
+
resource_type="app",
|
|
54
|
+
app_key=item.get("app_key"),
|
|
55
|
+
)
|
|
56
|
+
if uri:
|
|
57
|
+
item["uri"] = uri
|
|
58
|
+
_append_unique_warning(warnings, warning)
|
|
49
59
|
response = {
|
|
50
60
|
"profile": profile,
|
|
51
61
|
"ws_id": session_profile.selected_ws_id,
|
|
@@ -200,9 +210,10 @@ class AppTools(ToolBase):
|
|
|
200
210
|
if not _is_optional_app_metadata_error(exc):
|
|
201
211
|
raise
|
|
202
212
|
visible_app = self._resolve_visible_app(context, app_key)
|
|
203
|
-
if visible_app is
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
if visible_app is None:
|
|
214
|
+
raise
|
|
215
|
+
app_name = str(visible_app.get("app_name") or visible_app.get("title") or app_key).strip() or app_key
|
|
216
|
+
app_icon = str(visible_app.get("app_icon") or "").strip() or None
|
|
206
217
|
warnings.append(
|
|
207
218
|
_with_api_error_fields(
|
|
208
219
|
{
|
|
@@ -224,22 +235,45 @@ class AppTools(ToolBase):
|
|
|
224
235
|
accessible_views.extend(custom_views)
|
|
225
236
|
import_capability, import_warnings = _derive_import_capability(base_info)
|
|
226
237
|
warnings.extend(import_warnings)
|
|
238
|
+
app_uri, uri_warning = try_build_resource_uri(
|
|
239
|
+
ws_id=session_profile.selected_ws_id,
|
|
240
|
+
resource_type="app",
|
|
241
|
+
app_key=app_key,
|
|
242
|
+
)
|
|
243
|
+
_append_unique_warning(warnings, uri_warning)
|
|
244
|
+
for view in accessible_views:
|
|
245
|
+
view_id = str(view.get("view_id") or "")
|
|
246
|
+
view_kind, separator, view_key = view_id.partition(":")
|
|
247
|
+
if not separator:
|
|
248
|
+
continue
|
|
249
|
+
uri, warning = try_build_resource_uri(
|
|
250
|
+
ws_id=session_profile.selected_ws_id,
|
|
251
|
+
resource_type="view",
|
|
252
|
+
app_key=app_key,
|
|
253
|
+
view_kind=view_kind,
|
|
254
|
+
view_key=view_key,
|
|
255
|
+
)
|
|
256
|
+
if uri:
|
|
257
|
+
view["uri"] = uri
|
|
258
|
+
_append_unique_warning(warnings, warning)
|
|
259
|
+
data: JSONObject = {
|
|
260
|
+
"app_key": app_key,
|
|
261
|
+
"app_name": app_name,
|
|
262
|
+
"app_icon": app_icon,
|
|
263
|
+
"icon_config": workspace_icon_config(app_icon),
|
|
264
|
+
**({"uri": app_uri} if app_uri else {}),
|
|
265
|
+
**({"visible_app": visible_app} if base_info is None and visible_app is not None else {}),
|
|
266
|
+
"can_create": can_create,
|
|
267
|
+
"import_capability": import_capability,
|
|
268
|
+
"accessible_views": accessible_views,
|
|
269
|
+
}
|
|
227
270
|
return {
|
|
228
271
|
"profile": profile,
|
|
229
272
|
"ws_id": session_profile.selected_ws_id,
|
|
230
273
|
"ok": True,
|
|
231
274
|
"request_route": {"base_url": context.base_url, "qf_version": context.qf_version},
|
|
232
275
|
"warnings": warnings,
|
|
233
|
-
"data":
|
|
234
|
-
"app_key": app_key,
|
|
235
|
-
"app_name": app_name,
|
|
236
|
-
"app_icon": app_icon,
|
|
237
|
-
"icon_config": workspace_icon_config(app_icon),
|
|
238
|
-
**({"visible_app": visible_app} if base_info is None and visible_app is not None else {}),
|
|
239
|
-
"can_create": can_create,
|
|
240
|
-
"import_capability": import_capability,
|
|
241
|
-
"accessible_views": accessible_views,
|
|
242
|
-
},
|
|
276
|
+
"data": data,
|
|
243
277
|
}
|
|
244
278
|
|
|
245
279
|
return self._run(profile, runner, tool_name='应用详情')
|
|
@@ -1048,6 +1082,11 @@ def _derive_editability(base_info: Any) -> tuple[JSONObject, list[JSONObject]]:
|
|
|
1048
1082
|
}, warnings
|
|
1049
1083
|
|
|
1050
1084
|
|
|
1085
|
+
def _append_unique_warning(warnings: list[JSONObject], warning: JSONObject | None) -> None:
|
|
1086
|
+
if warning and not any(item.get("code") == warning.get("code") for item in warnings):
|
|
1087
|
+
warnings.append(warning)
|
|
1088
|
+
|
|
1089
|
+
|
|
1051
1090
|
def _coerce_positive_int(value: Any) -> int | None:
|
|
1052
1091
|
try:
|
|
1053
1092
|
number = int(value)
|