@josephyan/qingflow-cli 0.2.0-beta.62 → 0.2.0-beta.64
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 +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/builder_facade/models.py +225 -0
- package/src/qingflow_mcp/builder_facade/service.py +1416 -35
- package/src/qingflow_mcp/cli/commands/builder.py +58 -1
- package/src/qingflow_mcp/server_app_builder.py +25 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +244 -2
- package/src/qingflow_mcp/tools/custom_button_tools.py +177 -0
- package/src/qingflow_mcp/tools/import_tools.py +217 -90
|
@@ -7,7 +7,7 @@ from .common import load_list_arg, load_object_arg, require_list_arg
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
10
|
-
parser = subparsers.add_parser("builder", help="稳定 builder 命令")
|
|
10
|
+
parser = subparsers.add_parser("builder", aliases=["build"], help="稳定 builder 命令")
|
|
11
11
|
builder_subparsers = parser.add_subparsers(dest="builder_command", required=True)
|
|
12
12
|
|
|
13
13
|
package = builder_subparsers.add_parser("package", help="应用包")
|
|
@@ -25,6 +25,34 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
25
25
|
app_resolve.add_argument("--package-tag-id", type=int)
|
|
26
26
|
app_resolve.set_defaults(handler=_handle_app_resolve, format_hint="builder_summary")
|
|
27
27
|
|
|
28
|
+
button = builder_subparsers.add_parser("button", help="自定义按钮")
|
|
29
|
+
button_subparsers = button.add_subparsers(dest="builder_button_command", required=True)
|
|
30
|
+
|
|
31
|
+
button_list = button_subparsers.add_parser("list", help="列出自定义按钮")
|
|
32
|
+
button_list.add_argument("--app-key", required=True)
|
|
33
|
+
button_list.set_defaults(handler=_handle_button_list, format_hint="builder_summary")
|
|
34
|
+
|
|
35
|
+
button_get = button_subparsers.add_parser("get", help="读取自定义按钮")
|
|
36
|
+
button_get.add_argument("--app-key", required=True)
|
|
37
|
+
button_get.add_argument("--button-id", type=int, required=True)
|
|
38
|
+
button_get.set_defaults(handler=_handle_button_get, format_hint="builder_summary")
|
|
39
|
+
|
|
40
|
+
button_create = button_subparsers.add_parser("create", help="创建自定义按钮")
|
|
41
|
+
button_create.add_argument("--app-key", required=True)
|
|
42
|
+
button_create.add_argument("--payload-file", required=True)
|
|
43
|
+
button_create.set_defaults(handler=_handle_button_create, format_hint="builder_summary")
|
|
44
|
+
|
|
45
|
+
button_update = button_subparsers.add_parser("update", help="更新自定义按钮")
|
|
46
|
+
button_update.add_argument("--app-key", required=True)
|
|
47
|
+
button_update.add_argument("--button-id", type=int, required=True)
|
|
48
|
+
button_update.add_argument("--payload-file", required=True)
|
|
49
|
+
button_update.set_defaults(handler=_handle_button_update, format_hint="builder_summary")
|
|
50
|
+
|
|
51
|
+
button_delete = button_subparsers.add_parser("delete", help="删除自定义按钮")
|
|
52
|
+
button_delete.add_argument("--app-key", required=True)
|
|
53
|
+
button_delete.add_argument("--button-id", type=int, required=True)
|
|
54
|
+
button_delete.set_defaults(handler=_handle_button_delete, format_hint="builder_summary")
|
|
55
|
+
|
|
28
56
|
for name, help_text, handler in [
|
|
29
57
|
("read-summary", "读取应用摘要", _handle_app_read_summary),
|
|
30
58
|
("read-fields", "读取字段摘要", _handle_app_read_fields),
|
|
@@ -102,6 +130,35 @@ def _handle_app_resolve(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
102
130
|
)
|
|
103
131
|
|
|
104
132
|
|
|
133
|
+
def _handle_button_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
134
|
+
return context.builder.app_custom_button_list(profile=args.profile, app_key=args.app_key)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _handle_button_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
138
|
+
return context.builder.app_custom_button_get(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _handle_button_create(args: argparse.Namespace, context: CliContext) -> dict:
|
|
142
|
+
return context.builder.app_custom_button_create(
|
|
143
|
+
profile=args.profile,
|
|
144
|
+
app_key=args.app_key,
|
|
145
|
+
payload=load_object_arg(args.payload_file, option_name="--payload-file"),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _handle_button_update(args: argparse.Namespace, context: CliContext) -> dict:
|
|
150
|
+
return context.builder.app_custom_button_update(
|
|
151
|
+
profile=args.profile,
|
|
152
|
+
app_key=args.app_key,
|
|
153
|
+
button_id=args.button_id,
|
|
154
|
+
payload=load_object_arg(args.payload_file, option_name="--payload-file"),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _handle_button_delete(args: argparse.Namespace, context: CliContext) -> dict:
|
|
159
|
+
return context.builder.app_custom_button_delete(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
|
|
160
|
+
|
|
161
|
+
|
|
105
162
|
def _handle_app_read_summary(args: argparse.Namespace, context: CliContext) -> dict:
|
|
106
163
|
return context.builder.app_read_summary(profile=args.profile, app_key=args.app_key)
|
|
107
164
|
|
|
@@ -216,6 +216,31 @@ def build_builder_server() -> FastMCP:
|
|
|
216
216
|
) -> dict:
|
|
217
217
|
return ai_builder.app_resolve(profile=profile, app_key=app_key, app_name=app_name, package_tag_id=package_tag_id)
|
|
218
218
|
|
|
219
|
+
@server.tool()
|
|
220
|
+
def app_custom_button_list(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
221
|
+
return ai_builder.app_custom_button_list(profile=profile, app_key=app_key)
|
|
222
|
+
|
|
223
|
+
@server.tool()
|
|
224
|
+
def app_custom_button_get(profile: str = DEFAULT_PROFILE, app_key: str = "", button_id: int = 0) -> dict:
|
|
225
|
+
return ai_builder.app_custom_button_get(profile=profile, app_key=app_key, button_id=button_id)
|
|
226
|
+
|
|
227
|
+
@server.tool()
|
|
228
|
+
def app_custom_button_create(profile: str = DEFAULT_PROFILE, app_key: str = "", payload: dict | None = None) -> dict:
|
|
229
|
+
return ai_builder.app_custom_button_create(profile=profile, app_key=app_key, payload=payload or {})
|
|
230
|
+
|
|
231
|
+
@server.tool()
|
|
232
|
+
def app_custom_button_update(
|
|
233
|
+
profile: str = DEFAULT_PROFILE,
|
|
234
|
+
app_key: str = "",
|
|
235
|
+
button_id: int = 0,
|
|
236
|
+
payload: dict | None = None,
|
|
237
|
+
) -> dict:
|
|
238
|
+
return ai_builder.app_custom_button_update(profile=profile, app_key=app_key, button_id=button_id, payload=payload or {})
|
|
239
|
+
|
|
240
|
+
@server.tool()
|
|
241
|
+
def app_custom_button_delete(profile: str = DEFAULT_PROFILE, app_key: str = "", button_id: int = 0) -> dict:
|
|
242
|
+
return ai_builder.app_custom_button_delete(profile=profile, app_key=app_key, button_id=button_id)
|
|
243
|
+
|
|
219
244
|
@server.tool()
|
|
220
245
|
def app_read_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
221
246
|
return ai_builder.app_read_summary(profile=profile, app_key=app_key)
|
|
@@ -11,6 +11,7 @@ from ..errors import QingflowApiError
|
|
|
11
11
|
from ..json_types import JSONObject
|
|
12
12
|
from ..builder_facade.models import (
|
|
13
13
|
ChartApplyRequest,
|
|
14
|
+
CustomButtonPatch,
|
|
14
15
|
FIELD_TYPE_ID_ALIASES,
|
|
15
16
|
FieldPatch,
|
|
16
17
|
FieldRemovePatch,
|
|
@@ -24,6 +25,7 @@ from ..builder_facade.models import (
|
|
|
24
25
|
LayoutPreset,
|
|
25
26
|
LayoutSectionPatch,
|
|
26
27
|
PortalApplyRequest,
|
|
28
|
+
PublicButtonTriggerAction,
|
|
27
29
|
PublicFieldType,
|
|
28
30
|
PublicRelationMode,
|
|
29
31
|
PublicChartType,
|
|
@@ -37,6 +39,7 @@ from ..builder_facade.models import (
|
|
|
37
39
|
from ..builder_facade.service import AiBuilderFacade
|
|
38
40
|
from .app_tools import AppTools
|
|
39
41
|
from .base import ToolBase
|
|
42
|
+
from .custom_button_tools import CustomButtonTools
|
|
40
43
|
from .directory_tools import DirectoryTools
|
|
41
44
|
from .package_tools import PackageTools
|
|
42
45
|
from .portal_tools import PortalTools
|
|
@@ -54,6 +57,7 @@ class AiBuilderTools(ToolBase):
|
|
|
54
57
|
super().__init__(sessions, backend)
|
|
55
58
|
self._facade = AiBuilderFacade(
|
|
56
59
|
apps=AppTools(sessions, backend),
|
|
60
|
+
buttons=CustomButtonTools(sessions, backend),
|
|
57
61
|
packages=PackageTools(sessions, backend),
|
|
58
62
|
views=ViewTools(sessions, backend),
|
|
59
63
|
workflows=WorkflowTools(sessions, backend),
|
|
@@ -156,6 +160,35 @@ class AiBuilderTools(ToolBase):
|
|
|
156
160
|
) -> JSONObject:
|
|
157
161
|
return self.app_resolve(profile=profile, app_key=app_key, app_name=app_name, package_tag_id=package_tag_id)
|
|
158
162
|
|
|
163
|
+
@mcp.tool()
|
|
164
|
+
def app_custom_button_list(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
165
|
+
return self.app_custom_button_list(profile=profile, app_key=app_key)
|
|
166
|
+
|
|
167
|
+
@mcp.tool()
|
|
168
|
+
def app_custom_button_get(profile: str = DEFAULT_PROFILE, app_key: str = "", button_id: int = 0) -> JSONObject:
|
|
169
|
+
return self.app_custom_button_get(profile=profile, app_key=app_key, button_id=button_id)
|
|
170
|
+
|
|
171
|
+
@mcp.tool()
|
|
172
|
+
def app_custom_button_create(
|
|
173
|
+
profile: str = DEFAULT_PROFILE,
|
|
174
|
+
app_key: str = "",
|
|
175
|
+
payload: JSONObject | None = None,
|
|
176
|
+
) -> JSONObject:
|
|
177
|
+
return self.app_custom_button_create(profile=profile, app_key=app_key, payload=payload or {})
|
|
178
|
+
|
|
179
|
+
@mcp.tool()
|
|
180
|
+
def app_custom_button_update(
|
|
181
|
+
profile: str = DEFAULT_PROFILE,
|
|
182
|
+
app_key: str = "",
|
|
183
|
+
button_id: int = 0,
|
|
184
|
+
payload: JSONObject | None = None,
|
|
185
|
+
) -> JSONObject:
|
|
186
|
+
return self.app_custom_button_update(profile=profile, app_key=app_key, button_id=button_id, payload=payload or {})
|
|
187
|
+
|
|
188
|
+
@mcp.tool()
|
|
189
|
+
def app_custom_button_delete(profile: str = DEFAULT_PROFILE, app_key: str = "", button_id: int = 0) -> JSONObject:
|
|
190
|
+
return self.app_custom_button_delete(profile=profile, app_key=app_key, button_id=button_id)
|
|
191
|
+
|
|
159
192
|
@mcp.tool()
|
|
160
193
|
def app_read_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
161
194
|
return self.app_read_summary(profile=profile, app_key=app_key)
|
|
@@ -518,6 +551,98 @@ class AiBuilderTools(ToolBase):
|
|
|
518
551
|
suggested_next_call={"tool_name": "app_resolve", "arguments": {"profile": profile, **normalized_args}},
|
|
519
552
|
)
|
|
520
553
|
|
|
554
|
+
def app_custom_button_list(self, *, profile: str, app_key: str) -> JSONObject:
|
|
555
|
+
normalized_args = {"app_key": app_key}
|
|
556
|
+
return _safe_tool_call(
|
|
557
|
+
lambda: self._facade.app_custom_button_list(profile=profile, app_key=app_key),
|
|
558
|
+
error_code="CUSTOM_BUTTON_LIST_FAILED",
|
|
559
|
+
normalized_args=normalized_args,
|
|
560
|
+
suggested_next_call={"tool_name": "app_custom_button_list", "arguments": {"profile": profile, **normalized_args}},
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
def app_custom_button_get(self, *, profile: str, app_key: str, button_id: int) -> JSONObject:
|
|
564
|
+
normalized_args = {"app_key": app_key, "button_id": button_id}
|
|
565
|
+
return _safe_tool_call(
|
|
566
|
+
lambda: self._facade.app_custom_button_get(profile=profile, app_key=app_key, button_id=button_id),
|
|
567
|
+
error_code="CUSTOM_BUTTON_GET_FAILED",
|
|
568
|
+
normalized_args=normalized_args,
|
|
569
|
+
suggested_next_call={"tool_name": "app_custom_button_get", "arguments": {"profile": profile, **normalized_args}},
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
def app_custom_button_create(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
573
|
+
try:
|
|
574
|
+
request = CustomButtonPatch.model_validate(payload)
|
|
575
|
+
except ValidationError as exc:
|
|
576
|
+
return _validation_failure(
|
|
577
|
+
str(exc),
|
|
578
|
+
tool_name="app_custom_button_create",
|
|
579
|
+
exc=exc,
|
|
580
|
+
suggested_next_call={
|
|
581
|
+
"tool_name": "app_custom_button_create",
|
|
582
|
+
"arguments": {
|
|
583
|
+
"profile": profile,
|
|
584
|
+
"app_key": app_key,
|
|
585
|
+
"payload": {
|
|
586
|
+
"button_text": "新增记录",
|
|
587
|
+
"background_color": "#FFFFFF",
|
|
588
|
+
"text_color": "#494F57",
|
|
589
|
+
"button_icon": "ex-add-outlined",
|
|
590
|
+
"trigger_action": "addData",
|
|
591
|
+
"trigger_add_data_config": {"related_app_key": "TARGET_APP_KEY", "que_relation": []},
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
)
|
|
596
|
+
normalized_args = {"app_key": app_key, "payload": request.model_dump(mode="json")}
|
|
597
|
+
return _safe_tool_call(
|
|
598
|
+
lambda: self._facade.app_custom_button_create(profile=profile, app_key=app_key, payload=request),
|
|
599
|
+
error_code="CUSTOM_BUTTON_CREATE_FAILED",
|
|
600
|
+
normalized_args=normalized_args,
|
|
601
|
+
suggested_next_call={"tool_name": "app_custom_button_create", "arguments": {"profile": profile, **normalized_args}},
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
def app_custom_button_update(self, *, profile: str, app_key: str, button_id: int, payload: JSONObject) -> JSONObject:
|
|
605
|
+
try:
|
|
606
|
+
request = CustomButtonPatch.model_validate(payload)
|
|
607
|
+
except ValidationError as exc:
|
|
608
|
+
return _validation_failure(
|
|
609
|
+
str(exc),
|
|
610
|
+
tool_name="app_custom_button_update",
|
|
611
|
+
exc=exc,
|
|
612
|
+
suggested_next_call={
|
|
613
|
+
"tool_name": "app_custom_button_update",
|
|
614
|
+
"arguments": {
|
|
615
|
+
"profile": profile,
|
|
616
|
+
"app_key": app_key,
|
|
617
|
+
"button_id": button_id,
|
|
618
|
+
"payload": {
|
|
619
|
+
"button_text": "新增记录",
|
|
620
|
+
"background_color": "#FFFFFF",
|
|
621
|
+
"text_color": "#494F57",
|
|
622
|
+
"button_icon": "ex-add-outlined",
|
|
623
|
+
"trigger_action": "link",
|
|
624
|
+
"trigger_link_url": "https://example.com",
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
)
|
|
629
|
+
normalized_args = {"app_key": app_key, "button_id": button_id, "payload": request.model_dump(mode="json")}
|
|
630
|
+
return _safe_tool_call(
|
|
631
|
+
lambda: self._facade.app_custom_button_update(profile=profile, app_key=app_key, button_id=button_id, payload=request),
|
|
632
|
+
error_code="CUSTOM_BUTTON_UPDATE_FAILED",
|
|
633
|
+
normalized_args=normalized_args,
|
|
634
|
+
suggested_next_call={"tool_name": "app_custom_button_update", "arguments": {"profile": profile, **normalized_args}},
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
def app_custom_button_delete(self, *, profile: str, app_key: str, button_id: int) -> JSONObject:
|
|
638
|
+
normalized_args = {"app_key": app_key, "button_id": button_id}
|
|
639
|
+
return _safe_tool_call(
|
|
640
|
+
lambda: self._facade.app_custom_button_delete(profile=profile, app_key=app_key, button_id=button_id),
|
|
641
|
+
error_code="CUSTOM_BUTTON_DELETE_FAILED",
|
|
642
|
+
normalized_args=normalized_args,
|
|
643
|
+
suggested_next_call={"tool_name": "app_custom_button_delete", "arguments": {"profile": profile, **normalized_args}},
|
|
644
|
+
)
|
|
645
|
+
|
|
521
646
|
def app_read_summary(self, *, profile: str, app_key: str) -> JSONObject:
|
|
522
647
|
normalized_args = {"app_key": app_key}
|
|
523
648
|
return _safe_tool_call(
|
|
@@ -1531,6 +1656,11 @@ def _public_error_message(error_code: str, error: QingflowApiError) -> str:
|
|
|
1531
1656
|
"PACKAGE_RESOLVE_FAILED": "package resolution is unavailable in the current route",
|
|
1532
1657
|
"PACKAGE_ATTACH_FAILED": "package attachment could not be verified in the current route",
|
|
1533
1658
|
"APP_RESOLVE_FAILED": "app resolution is unavailable in the current route",
|
|
1659
|
+
"CUSTOM_BUTTON_LIST_FAILED": "custom button list is unavailable in the current route",
|
|
1660
|
+
"CUSTOM_BUTTON_GET_FAILED": "custom button detail is unavailable in the current route",
|
|
1661
|
+
"CUSTOM_BUTTON_CREATE_FAILED": "custom button create could not complete because the route or readback is unavailable",
|
|
1662
|
+
"CUSTOM_BUTTON_UPDATE_FAILED": "custom button update could not complete because the route or readback is unavailable",
|
|
1663
|
+
"CUSTOM_BUTTON_DELETE_FAILED": "custom button delete could not complete because the route is unavailable",
|
|
1534
1664
|
"APP_READ_FAILED": "app base or schema is unavailable in the current route",
|
|
1535
1665
|
"FIELDS_READ_FAILED": "app fields are unavailable in the current route",
|
|
1536
1666
|
"LAYOUT_READ_FAILED": "layout resource is unavailable for this app in the current route",
|
|
@@ -1588,6 +1718,97 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
1588
1718
|
"role_icon": "ex-user-outlined",
|
|
1589
1719
|
},
|
|
1590
1720
|
},
|
|
1721
|
+
"app_custom_button_list": {
|
|
1722
|
+
"allowed_keys": ["app_key"],
|
|
1723
|
+
"aliases": {},
|
|
1724
|
+
"allowed_values": {},
|
|
1725
|
+
"minimal_example": {
|
|
1726
|
+
"profile": "default",
|
|
1727
|
+
"app_key": "APP_KEY",
|
|
1728
|
+
},
|
|
1729
|
+
},
|
|
1730
|
+
"app_custom_button_get": {
|
|
1731
|
+
"allowed_keys": ["app_key", "button_id"],
|
|
1732
|
+
"aliases": {"buttonId": "button_id"},
|
|
1733
|
+
"allowed_values": {},
|
|
1734
|
+
"minimal_example": {
|
|
1735
|
+
"profile": "default",
|
|
1736
|
+
"app_key": "APP_KEY",
|
|
1737
|
+
"button_id": 1001,
|
|
1738
|
+
},
|
|
1739
|
+
},
|
|
1740
|
+
"app_custom_button_create": {
|
|
1741
|
+
"allowed_keys": ["app_key", "payload"],
|
|
1742
|
+
"aliases": {
|
|
1743
|
+
"payload.buttonText": "payload.button_text",
|
|
1744
|
+
"payload.backgroundColor": "payload.background_color",
|
|
1745
|
+
"payload.textColor": "payload.text_color",
|
|
1746
|
+
"payload.buttonIcon": "payload.button_icon",
|
|
1747
|
+
"payload.triggerAction": "payload.trigger_action",
|
|
1748
|
+
"payload.triggerLinkUrl": "payload.trigger_link_url",
|
|
1749
|
+
"payload.triggerAddDataConfig": "payload.trigger_add_data_config",
|
|
1750
|
+
"payload.externalQrobotConfig": "payload.external_qrobot_config",
|
|
1751
|
+
"payload.customButtonExternalQRobotRelationVO": "payload.external_qrobot_config",
|
|
1752
|
+
"payload.triggerWingsConfig": "payload.trigger_wings_config",
|
|
1753
|
+
},
|
|
1754
|
+
"allowed_values": {
|
|
1755
|
+
"payload.trigger_action": [member.value for member in PublicButtonTriggerAction],
|
|
1756
|
+
},
|
|
1757
|
+
"minimal_example": {
|
|
1758
|
+
"profile": "default",
|
|
1759
|
+
"app_key": "APP_KEY",
|
|
1760
|
+
"payload": {
|
|
1761
|
+
"button_text": "新增记录",
|
|
1762
|
+
"background_color": "#FFFFFF",
|
|
1763
|
+
"text_color": "#494F57",
|
|
1764
|
+
"button_icon": "ex-add-outlined",
|
|
1765
|
+
"trigger_action": "link",
|
|
1766
|
+
"trigger_link_url": "https://example.com",
|
|
1767
|
+
},
|
|
1768
|
+
},
|
|
1769
|
+
},
|
|
1770
|
+
"app_custom_button_update": {
|
|
1771
|
+
"allowed_keys": ["app_key", "button_id", "payload"],
|
|
1772
|
+
"aliases": {
|
|
1773
|
+
"buttonId": "button_id",
|
|
1774
|
+
"payload.buttonText": "payload.button_text",
|
|
1775
|
+
"payload.backgroundColor": "payload.background_color",
|
|
1776
|
+
"payload.textColor": "payload.text_color",
|
|
1777
|
+
"payload.buttonIcon": "payload.button_icon",
|
|
1778
|
+
"payload.triggerAction": "payload.trigger_action",
|
|
1779
|
+
"payload.triggerLinkUrl": "payload.trigger_link_url",
|
|
1780
|
+
"payload.triggerAddDataConfig": "payload.trigger_add_data_config",
|
|
1781
|
+
"payload.externalQrobotConfig": "payload.external_qrobot_config",
|
|
1782
|
+
"payload.customButtonExternalQRobotRelationVO": "payload.external_qrobot_config",
|
|
1783
|
+
"payload.triggerWingsConfig": "payload.trigger_wings_config",
|
|
1784
|
+
},
|
|
1785
|
+
"allowed_values": {
|
|
1786
|
+
"payload.trigger_action": [member.value for member in PublicButtonTriggerAction],
|
|
1787
|
+
},
|
|
1788
|
+
"minimal_example": {
|
|
1789
|
+
"profile": "default",
|
|
1790
|
+
"app_key": "APP_KEY",
|
|
1791
|
+
"button_id": 1001,
|
|
1792
|
+
"payload": {
|
|
1793
|
+
"button_text": "查看详情",
|
|
1794
|
+
"background_color": "#FFFFFF",
|
|
1795
|
+
"text_color": "#494F57",
|
|
1796
|
+
"button_icon": "ex-link-outlined",
|
|
1797
|
+
"trigger_action": "link",
|
|
1798
|
+
"trigger_link_url": "https://example.com/detail",
|
|
1799
|
+
},
|
|
1800
|
+
},
|
|
1801
|
+
},
|
|
1802
|
+
"app_custom_button_delete": {
|
|
1803
|
+
"allowed_keys": ["app_key", "button_id"],
|
|
1804
|
+
"aliases": {"buttonId": "button_id"},
|
|
1805
|
+
"allowed_values": {},
|
|
1806
|
+
"minimal_example": {
|
|
1807
|
+
"profile": "default",
|
|
1808
|
+
"app_key": "APP_KEY",
|
|
1809
|
+
"button_id": 1001,
|
|
1810
|
+
},
|
|
1811
|
+
},
|
|
1591
1812
|
"app_schema_plan": {
|
|
1592
1813
|
"allowed_keys": ["app_key", "package_tag_id", "app_name", "create_if_missing", "add_fields", "update_fields", "remove_fields"],
|
|
1593
1814
|
"aliases": {
|
|
@@ -1825,7 +2046,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
1825
2046
|
},
|
|
1826
2047
|
},
|
|
1827
2048
|
"app_views_plan": {
|
|
1828
|
-
"allowed_keys": ["app_key", "upsert_views", "remove_views", "preset", "upsert_views[].view_key"],
|
|
2049
|
+
"allowed_keys": ["app_key", "upsert_views", "remove_views", "preset", "upsert_views[].view_key", "upsert_views[].buttons"],
|
|
1829
2050
|
"aliases": {
|
|
1830
2051
|
"fields": "columns",
|
|
1831
2052
|
"column_names": "columns",
|
|
@@ -1839,11 +2060,21 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
1839
2060
|
"startField": "start_field",
|
|
1840
2061
|
"endField": "end_field",
|
|
1841
2062
|
"titleField": "title_field",
|
|
2063
|
+
"buttons[].buttonType": "buttons[].button_type",
|
|
2064
|
+
"buttons[].configType": "buttons[].config_type",
|
|
2065
|
+
"buttons[].buttonId": "buttons[].button_id",
|
|
2066
|
+
"buttons[].beingMain": "buttons[].being_main",
|
|
2067
|
+
"buttons[].buttonLimit": "buttons[].button_limit",
|
|
2068
|
+
"buttons[].buttonFormula": "buttons[].button_formula",
|
|
2069
|
+
"buttons[].buttonFormulaType": "buttons[].button_formula_type",
|
|
2070
|
+
"buttons[].printTpls": "buttons[].print_tpls",
|
|
1842
2071
|
},
|
|
1843
2072
|
"allowed_values": {
|
|
1844
2073
|
"preset": [member.value for member in ViewsPreset],
|
|
1845
2074
|
"view.type": [member.value for member in PublicViewType],
|
|
1846
2075
|
"view.filter.operator": [member.value for member in ViewFilterOperator],
|
|
2076
|
+
"view.buttons.button_type": ["SYSTEM", "CUSTOM"],
|
|
2077
|
+
"view.buttons.config_type": ["TOP", "DETAIL"],
|
|
1847
2078
|
},
|
|
1848
2079
|
"minimal_example": {
|
|
1849
2080
|
"profile": "default",
|
|
@@ -1869,7 +2100,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
1869
2100
|
},
|
|
1870
2101
|
},
|
|
1871
2102
|
"app_views_apply": {
|
|
1872
|
-
"allowed_keys": ["app_key", "publish", "upsert_views", "remove_views", "upsert_views[].view_key"],
|
|
2103
|
+
"allowed_keys": ["app_key", "publish", "upsert_views", "remove_views", "upsert_views[].view_key", "upsert_views[].buttons"],
|
|
1873
2104
|
"aliases": {
|
|
1874
2105
|
"fields": "columns",
|
|
1875
2106
|
"column_names": "columns",
|
|
@@ -1883,16 +2114,27 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
1883
2114
|
"startField": "start_field",
|
|
1884
2115
|
"endField": "end_field",
|
|
1885
2116
|
"titleField": "title_field",
|
|
2117
|
+
"buttons[].buttonType": "buttons[].button_type",
|
|
2118
|
+
"buttons[].configType": "buttons[].config_type",
|
|
2119
|
+
"buttons[].buttonId": "buttons[].button_id",
|
|
2120
|
+
"buttons[].beingMain": "buttons[].being_main",
|
|
2121
|
+
"buttons[].buttonLimit": "buttons[].button_limit",
|
|
2122
|
+
"buttons[].buttonFormula": "buttons[].button_formula",
|
|
2123
|
+
"buttons[].buttonFormulaType": "buttons[].button_formula_type",
|
|
2124
|
+
"buttons[].printTpls": "buttons[].print_tpls",
|
|
1886
2125
|
},
|
|
1887
2126
|
"allowed_values": {
|
|
1888
2127
|
"view.type": [member.value for member in PublicViewType],
|
|
1889
2128
|
"view.filter.operator": [member.value for member in ViewFilterOperator],
|
|
2129
|
+
"view.buttons.button_type": ["SYSTEM", "CUSTOM"],
|
|
2130
|
+
"view.buttons.config_type": ["TOP", "DETAIL"],
|
|
1890
2131
|
},
|
|
1891
2132
|
"execution_notes": [
|
|
1892
2133
|
"apply may return partial_success when some views land and others fail",
|
|
1893
2134
|
"when duplicate view names exist, supply view_key to target the exact view",
|
|
1894
2135
|
"read back app_read_views_summary after any failed or partial view apply",
|
|
1895
2136
|
"view existence verification and saved-filter verification are separate; treat filters as unverified until verification.view_filters_verified is true",
|
|
2137
|
+
"buttons omitted preserves existing button config; buttons=[] clears all buttons; buttons=[...] replaces the full button config",
|
|
1896
2138
|
],
|
|
1897
2139
|
"minimal_example": {
|
|
1898
2140
|
"profile": "default",
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
|
|
5
|
+
from ..config import DEFAULT_PROFILE
|
|
6
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
7
|
+
from ..json_types import JSONObject
|
|
8
|
+
from .base import ToolBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CustomButtonTools(ToolBase):
|
|
12
|
+
def custom_button_list(
|
|
13
|
+
self,
|
|
14
|
+
*,
|
|
15
|
+
profile: str,
|
|
16
|
+
app_key: str,
|
|
17
|
+
being_draft: bool = True,
|
|
18
|
+
include_raw: bool = False,
|
|
19
|
+
) -> JSONObject:
|
|
20
|
+
self._require_app_key(app_key)
|
|
21
|
+
|
|
22
|
+
def runner(session_profile, context):
|
|
23
|
+
result = self.backend.request(
|
|
24
|
+
"GET",
|
|
25
|
+
context,
|
|
26
|
+
f"/app/{app_key}/customButton",
|
|
27
|
+
params={"beingDraft": being_draft},
|
|
28
|
+
)
|
|
29
|
+
items = []
|
|
30
|
+
raw_items = result.get("result") if isinstance(result, dict) and isinstance(result.get("result"), list) else []
|
|
31
|
+
for item in raw_items:
|
|
32
|
+
if not isinstance(item, dict):
|
|
33
|
+
continue
|
|
34
|
+
items.append(self._compact_button_base_info(item))
|
|
35
|
+
response = {
|
|
36
|
+
"profile": profile,
|
|
37
|
+
"ws_id": session_profile.selected_ws_id,
|
|
38
|
+
"app_key": app_key,
|
|
39
|
+
"being_draft": being_draft,
|
|
40
|
+
"items": result if include_raw else items,
|
|
41
|
+
"count": len(items),
|
|
42
|
+
"compact": not include_raw,
|
|
43
|
+
}
|
|
44
|
+
if include_raw:
|
|
45
|
+
response["summary"] = items
|
|
46
|
+
return response
|
|
47
|
+
|
|
48
|
+
return self._run(profile, runner)
|
|
49
|
+
|
|
50
|
+
def custom_button_get(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
profile: str,
|
|
54
|
+
app_key: str,
|
|
55
|
+
button_id: int,
|
|
56
|
+
being_draft: bool = True,
|
|
57
|
+
include_raw: bool = False,
|
|
58
|
+
) -> JSONObject:
|
|
59
|
+
self._require_app_key(app_key)
|
|
60
|
+
self._require_button_id(button_id)
|
|
61
|
+
|
|
62
|
+
def runner(session_profile, context):
|
|
63
|
+
params = {"beingDraft": being_draft}
|
|
64
|
+
result = self.backend.request("GET", context, f"/app/{app_key}/customButton/{button_id}", params=params)
|
|
65
|
+
response = {
|
|
66
|
+
"profile": profile,
|
|
67
|
+
"ws_id": session_profile.selected_ws_id,
|
|
68
|
+
"app_key": app_key,
|
|
69
|
+
"button_id": button_id,
|
|
70
|
+
"being_draft": being_draft,
|
|
71
|
+
"result": result if include_raw else self._compact_button_detail(result if isinstance(result, dict) else {}),
|
|
72
|
+
"compact": not include_raw,
|
|
73
|
+
}
|
|
74
|
+
if include_raw:
|
|
75
|
+
response["summary"] = self._compact_button_detail(result if isinstance(result, dict) else {})
|
|
76
|
+
return response
|
|
77
|
+
|
|
78
|
+
return self._run(profile, runner)
|
|
79
|
+
|
|
80
|
+
def custom_button_create(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
81
|
+
self._require_app_key(app_key)
|
|
82
|
+
body = self._require_dict(payload)
|
|
83
|
+
|
|
84
|
+
def runner(session_profile, context):
|
|
85
|
+
result = self.backend.request("POST", context, f"/app/{app_key}/customButton", json_body=deepcopy(body))
|
|
86
|
+
return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result}
|
|
87
|
+
|
|
88
|
+
return self._run(profile, runner)
|
|
89
|
+
|
|
90
|
+
def custom_button_update(self, *, profile: str, app_key: str, button_id: int, payload: JSONObject) -> JSONObject:
|
|
91
|
+
self._require_app_key(app_key)
|
|
92
|
+
self._require_button_id(button_id)
|
|
93
|
+
body = self._require_dict(payload)
|
|
94
|
+
|
|
95
|
+
def runner(session_profile, context):
|
|
96
|
+
result = self.backend.request(
|
|
97
|
+
"POST",
|
|
98
|
+
context,
|
|
99
|
+
f"/app/{app_key}/customButton/{button_id}",
|
|
100
|
+
json_body=deepcopy(body),
|
|
101
|
+
)
|
|
102
|
+
return self._attach_human_review_notice(
|
|
103
|
+
{
|
|
104
|
+
"profile": profile,
|
|
105
|
+
"ws_id": session_profile.selected_ws_id,
|
|
106
|
+
"app_key": app_key,
|
|
107
|
+
"button_id": button_id,
|
|
108
|
+
"result": result,
|
|
109
|
+
},
|
|
110
|
+
operation="update",
|
|
111
|
+
target="custom button configuration",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return self._run(profile, runner)
|
|
115
|
+
|
|
116
|
+
def custom_button_delete(self, *, profile: str, app_key: str, button_id: int) -> JSONObject:
|
|
117
|
+
self._require_app_key(app_key)
|
|
118
|
+
self._require_button_id(button_id)
|
|
119
|
+
|
|
120
|
+
def runner(session_profile, context):
|
|
121
|
+
result = self.backend.request("DELETE", context, f"/app/{app_key}/customButton/{button_id}")
|
|
122
|
+
return self._attach_human_review_notice(
|
|
123
|
+
{
|
|
124
|
+
"profile": profile,
|
|
125
|
+
"ws_id": session_profile.selected_ws_id,
|
|
126
|
+
"app_key": app_key,
|
|
127
|
+
"button_id": button_id,
|
|
128
|
+
"result": result,
|
|
129
|
+
},
|
|
130
|
+
operation="delete",
|
|
131
|
+
target="custom button configuration",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return self._run(profile, runner)
|
|
135
|
+
|
|
136
|
+
def _require_app_key(self, app_key: str) -> None:
|
|
137
|
+
if not str(app_key or "").strip():
|
|
138
|
+
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
139
|
+
|
|
140
|
+
def _require_button_id(self, button_id: int) -> None:
|
|
141
|
+
if not isinstance(button_id, int) or isinstance(button_id, bool) or button_id <= 0:
|
|
142
|
+
raise_tool_error(QingflowApiError.config_error("button_id must be a positive integer"))
|
|
143
|
+
|
|
144
|
+
def _compact_button_base_info(self, item: dict[str, object]) -> JSONObject:
|
|
145
|
+
creator = item.get("creatorUserInfo") if isinstance(item.get("creatorUserInfo"), dict) else {}
|
|
146
|
+
return {
|
|
147
|
+
"button_id": item.get("buttonId"),
|
|
148
|
+
"button_text": item.get("buttonText"),
|
|
149
|
+
"button_icon": item.get("buttonIcon"),
|
|
150
|
+
"background_color": item.get("backgroundColor"),
|
|
151
|
+
"text_color": item.get("textColor"),
|
|
152
|
+
"creator_user_info": {
|
|
153
|
+
"uid": creator.get("uid"),
|
|
154
|
+
"name": creator.get("name"),
|
|
155
|
+
"email": creator.get("email"),
|
|
156
|
+
}
|
|
157
|
+
if creator
|
|
158
|
+
else None,
|
|
159
|
+
"used_in_chart_count": item.get("userInChartCount"),
|
|
160
|
+
"being_effective_external_qrobot": item.get("beingEffectiveExternalQRobot"),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def _compact_button_detail(self, item: dict[str, object]) -> JSONObject:
|
|
164
|
+
return {
|
|
165
|
+
"button_id": item.get("buttonId"),
|
|
166
|
+
"button_text": item.get("buttonText"),
|
|
167
|
+
"button_icon": item.get("buttonIcon"),
|
|
168
|
+
"background_color": item.get("backgroundColor"),
|
|
169
|
+
"text_color": item.get("textColor"),
|
|
170
|
+
"trigger_action": item.get("triggerAction"),
|
|
171
|
+
"trigger_link_url": item.get("triggerLinkUrl"),
|
|
172
|
+
"trigger_add_data_config": deepcopy(item.get("triggerAddDataConfig")) if isinstance(item.get("triggerAddDataConfig"), dict) else None,
|
|
173
|
+
"external_qrobot_config": deepcopy(item.get("customButtonExternalQRobotRelationVO"))
|
|
174
|
+
if isinstance(item.get("customButtonExternalQRobotRelationVO"), dict)
|
|
175
|
+
else None,
|
|
176
|
+
"trigger_wings_config": deepcopy(item.get("triggerWingsConfig")) if isinstance(item.get("triggerWingsConfig"), dict) else None,
|
|
177
|
+
}
|