@josephyan/qingflow-app-builder-mcp 0.2.0-beta.7 → 0.2.0-beta.71

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.
Files changed (70) hide show
  1. package/README.md +5 -3
  2. package/docs/local-agent-install.md +21 -5
  3. package/npm/bin/qingflow-app-builder-mcp.mjs +1 -1
  4. package/npm/lib/runtime.mjs +168 -12
  5. package/package.json +1 -1
  6. package/pyproject.toml +4 -1
  7. package/skills/qingflow-app-builder/SKILL.md +155 -22
  8. package/skills/qingflow-app-builder/references/create-app.md +51 -21
  9. package/skills/qingflow-app-builder/references/environments.md +1 -1
  10. package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
  11. package/skills/qingflow-app-builder/references/gotchas.md +28 -1
  12. package/skills/qingflow-app-builder/references/solution-playbooks.md +14 -12
  13. package/skills/qingflow-app-builder/references/tool-selection.md +47 -19
  14. package/skills/qingflow-app-builder/references/update-flow.md +112 -25
  15. package/skills/qingflow-app-builder/references/update-layout.md +11 -24
  16. package/skills/qingflow-app-builder/references/update-schema.md +1 -23
  17. package/skills/qingflow-app-builder/references/update-views.md +87 -21
  18. package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
  19. package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
  20. package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
  21. package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
  22. package/src/qingflow_mcp/__init__.py +1 -1
  23. package/src/qingflow_mcp/backend_client.py +210 -0
  24. package/src/qingflow_mcp/builder_facade/models.py +1252 -3
  25. package/src/qingflow_mcp/builder_facade/service.py +11367 -2389
  26. package/src/qingflow_mcp/cli/__init__.py +1 -0
  27. package/src/qingflow_mcp/cli/commands/__init__.py +15 -0
  28. package/src/qingflow_mcp/cli/commands/app.py +40 -0
  29. package/src/qingflow_mcp/cli/commands/auth.py +78 -0
  30. package/src/qingflow_mcp/cli/commands/builder.py +515 -0
  31. package/src/qingflow_mcp/cli/commands/common.py +62 -0
  32. package/src/qingflow_mcp/cli/commands/imports.py +96 -0
  33. package/src/qingflow_mcp/cli/commands/record.py +304 -0
  34. package/src/qingflow_mcp/cli/commands/task.py +89 -0
  35. package/src/qingflow_mcp/cli/commands/workspace.py +33 -0
  36. package/src/qingflow_mcp/cli/context.py +48 -0
  37. package/src/qingflow_mcp/cli/formatters.py +355 -0
  38. package/src/qingflow_mcp/cli/json_io.py +50 -0
  39. package/src/qingflow_mcp/cli/main.py +149 -0
  40. package/src/qingflow_mcp/config.py +39 -0
  41. package/src/qingflow_mcp/import_store.py +121 -0
  42. package/src/qingflow_mcp/list_type_labels.py +24 -0
  43. package/src/qingflow_mcp/response_trim.py +668 -0
  44. package/src/qingflow_mcp/server.py +160 -18
  45. package/src/qingflow_mcp/server_app_builder.py +275 -68
  46. package/src/qingflow_mcp/server_app_user.py +219 -191
  47. package/src/qingflow_mcp/session_store.py +41 -1
  48. package/src/qingflow_mcp/solution/compiler/form_compiler.py +43 -4
  49. package/src/qingflow_mcp/solution/compiler/icon_utils.py +119 -45
  50. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +41 -2
  51. package/src/qingflow_mcp/solution/executor.py +107 -11
  52. package/src/qingflow_mcp/solution/spec_models.py +2 -0
  53. package/src/qingflow_mcp/tools/ai_builder_tools.py +2032 -127
  54. package/src/qingflow_mcp/tools/app_tools.py +419 -12
  55. package/src/qingflow_mcp/tools/approval_tools.py +571 -72
  56. package/src/qingflow_mcp/tools/auth_tools.py +398 -2
  57. package/src/qingflow_mcp/tools/code_block_tools.py +756 -0
  58. package/src/qingflow_mcp/tools/custom_button_tools.py +179 -0
  59. package/src/qingflow_mcp/tools/directory_tools.py +203 -31
  60. package/src/qingflow_mcp/tools/feedback_tools.py +230 -0
  61. package/src/qingflow_mcp/tools/file_tools.py +1 -0
  62. package/src/qingflow_mcp/tools/import_tools.py +2150 -0
  63. package/src/qingflow_mcp/tools/package_tools.py +18 -4
  64. package/src/qingflow_mcp/tools/portal_tools.py +31 -0
  65. package/src/qingflow_mcp/tools/qingbi_report_tools.py +109 -7
  66. package/src/qingflow_mcp/tools/record_tools.py +9894 -1104
  67. package/src/qingflow_mcp/tools/solution_tools.py +115 -3
  68. package/src/qingflow_mcp/tools/task_context_tools.py +2040 -0
  69. package/src/qingflow_mcp/tools/task_tools.py +376 -225
  70. package/src/qingflow_mcp/tools/workspace_tools.py +163 -19
@@ -0,0 +1 @@
1
+ __all__: list[str] = []
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from . import app, auth, builder, imports, record, task, workspace
6
+
7
+
8
+ def register_all_commands(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
9
+ auth.register(subparsers)
10
+ workspace.register(subparsers)
11
+ app.register(subparsers)
12
+ record.register(subparsers)
13
+ imports.register(subparsers)
14
+ task.register(subparsers)
15
+ builder.register(subparsers)
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from ..context import CliContext
6
+
7
+
8
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
9
+ parser = subparsers.add_parser("app", help="应用发现")
10
+ app_subparsers = parser.add_subparsers(dest="app_command", required=True)
11
+
12
+ list_parser = app_subparsers.add_parser("list", help="列出可见应用")
13
+ list_parser.set_defaults(handler=_handle_list, format_hint="app_list")
14
+
15
+ search = app_subparsers.add_parser("search", help="搜索应用")
16
+ search.add_argument("--keyword", default="")
17
+ search.add_argument("--page", type=int, default=1)
18
+ search.add_argument("--page-size", type=int, default=50)
19
+ search.set_defaults(handler=_handle_search, format_hint="app_search")
20
+
21
+ get = app_subparsers.add_parser("get", help="读取应用可访问视图与导入能力")
22
+ get.add_argument("--app-key", required=True)
23
+ get.set_defaults(handler=_handle_get, format_hint="app_get")
24
+
25
+
26
+ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
27
+ return context.app.app_list(profile=args.profile)
28
+
29
+
30
+ def _handle_search(args: argparse.Namespace, context: CliContext) -> dict:
31
+ return context.app.app_search(
32
+ profile=args.profile,
33
+ keyword=args.keyword,
34
+ page_num=args.page,
35
+ page_size=args.page_size,
36
+ )
37
+
38
+
39
+ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
40
+ return context.app.app_get(profile=args.profile, app_key=args.app_key)
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import getpass
5
+ import sys
6
+
7
+ from ...errors import QingflowApiError
8
+ from ..context import CliContext
9
+ from .common import read_secret_arg
10
+
11
+
12
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
13
+ parser = subparsers.add_parser("auth", help="认证与会话")
14
+ auth_subparsers = parser.add_subparsers(dest="auth_command", required=True)
15
+
16
+ login = auth_subparsers.add_parser("login", help="邮箱密码登录")
17
+ login.add_argument("--base-url")
18
+ login.add_argument("--qf-version")
19
+ login.add_argument("--email", required=True)
20
+ login.add_argument("--password")
21
+ login.add_argument("--password-stdin", action="store_true")
22
+ login.add_argument("--persist", action=argparse.BooleanOptionalAction, default=True)
23
+ login.set_defaults(handler=_handle_login, format_hint="auth_whoami")
24
+
25
+ use_token = auth_subparsers.add_parser("use-token", help="直接注入 token")
26
+ use_token.add_argument("--base-url")
27
+ use_token.add_argument("--qf-version")
28
+ use_token.add_argument("--token")
29
+ use_token.add_argument("--token-stdin", action="store_true")
30
+ use_token.add_argument("--ws-id", type=int)
31
+ use_token.add_argument("--persist", action=argparse.BooleanOptionalAction, default=False)
32
+ use_token.set_defaults(handler=_handle_use_token, format_hint="auth_whoami")
33
+
34
+ whoami = auth_subparsers.add_parser("whoami", help="查看当前登录态")
35
+ whoami.set_defaults(handler=_handle_whoami, format_hint="auth_whoami")
36
+
37
+ logout = auth_subparsers.add_parser("logout", help="退出登录")
38
+ logout.add_argument("--forget-persisted", action="store_true")
39
+ logout.set_defaults(handler=_handle_logout, format_hint="")
40
+
41
+
42
+ def _handle_login(args: argparse.Namespace, context: CliContext) -> dict:
43
+ password = args.password
44
+ if args.password_stdin:
45
+ password = read_secret_arg(args.password, stdin_enabled=True, label="password")
46
+ elif not password:
47
+ if sys.stdin.isatty():
48
+ password = getpass.getpass("Password: ")
49
+ else:
50
+ raise QingflowApiError.config_error("password is required; use --password or --password-stdin")
51
+ return context.auth.auth_login(
52
+ profile=args.profile,
53
+ base_url=args.base_url,
54
+ qf_version=args.qf_version,
55
+ email=args.email,
56
+ password=password,
57
+ persist=bool(args.persist),
58
+ )
59
+
60
+
61
+ def _handle_use_token(args: argparse.Namespace, context: CliContext) -> dict:
62
+ token = read_secret_arg(args.token, stdin_enabled=bool(args.token_stdin), label="token")
63
+ return context.auth.auth_use_token(
64
+ profile=args.profile,
65
+ base_url=args.base_url,
66
+ qf_version=args.qf_version,
67
+ token=token,
68
+ ws_id=args.ws_id,
69
+ persist=bool(args.persist),
70
+ )
71
+
72
+
73
+ def _handle_whoami(args: argparse.Namespace, context: CliContext) -> dict:
74
+ return context.auth.auth_whoami(profile=args.profile)
75
+
76
+
77
+ def _handle_logout(args: argparse.Namespace, context: CliContext) -> dict:
78
+ return context.auth.auth_logout(profile=args.profile, forget_persisted=bool(args.forget_persisted))
@@ -0,0 +1,515 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from ..context import CliContext
6
+ from .common import load_list_arg, load_object_arg, raise_config_error, require_list_arg
7
+
8
+
9
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
10
+ parser = subparsers.add_parser("builder", aliases=["build"], help="稳定 builder 命令")
11
+ builder_subparsers = parser.add_subparsers(dest="builder_command", required=True)
12
+
13
+ contract = builder_subparsers.add_parser("contract", help="读取 builder tool 合约")
14
+ contract.add_argument("--tool-name", required=True)
15
+ contract.set_defaults(handler=_handle_contract, format_hint="builder_summary")
16
+
17
+ member = builder_subparsers.add_parser("member", help="成员目录")
18
+ member_subparsers = member.add_subparsers(dest="builder_member_command", required=True)
19
+ member_search = member_subparsers.add_parser("search", help="搜索成员")
20
+ member_search.add_argument("--query", default="")
21
+ member_search.add_argument("--page-num", type=int, default=1)
22
+ member_search.add_argument("--page-size", type=int, default=20)
23
+ member_search.add_argument("--contain-disable", action=argparse.BooleanOptionalAction, default=False)
24
+ member_search.set_defaults(handler=_handle_member_search, format_hint="builder_summary")
25
+
26
+ role = builder_subparsers.add_parser("role", help="角色目录")
27
+ role_subparsers = role.add_subparsers(dest="builder_role_command", required=True)
28
+ role_search = role_subparsers.add_parser("search", help="搜索角色")
29
+ role_search.add_argument("--keyword", default="")
30
+ role_search.add_argument("--page-num", type=int, default=1)
31
+ role_search.add_argument("--page-size", type=int, default=20)
32
+ role_search.set_defaults(handler=_handle_role_search, format_hint="builder_summary")
33
+
34
+ role_create = role_subparsers.add_parser("create", help="创建角色")
35
+ role_create.add_argument("--role-name", required=True)
36
+ role_create.add_argument("--member-uids-file")
37
+ role_create.add_argument("--member-emails-file")
38
+ role_create.add_argument("--member-names-file")
39
+ role_create.add_argument("--role-icon", default="ex-user-outlined")
40
+ role_create.set_defaults(handler=_handle_role_create, format_hint="builder_summary")
41
+
42
+ package = builder_subparsers.add_parser("package", help="应用包")
43
+ package_subparsers = package.add_subparsers(dest="builder_package_command", required=True)
44
+ package_list = package_subparsers.add_parser("list", help="列出应用包")
45
+ package_list.add_argument("--trial-status", default="all")
46
+ package_list.set_defaults(handler=_handle_package_list, format_hint="builder_summary")
47
+
48
+ package_resolve = package_subparsers.add_parser("resolve", help="解析应用包")
49
+ package_resolve.add_argument("--package-name", required=True)
50
+ package_resolve.set_defaults(handler=_handle_package_resolve, format_hint="builder_summary")
51
+
52
+ package_create = package_subparsers.add_parser("create", help="创建应用包")
53
+ package_create.add_argument("--package-name", required=True)
54
+ package_create.add_argument("--icon")
55
+ package_create.add_argument("--color")
56
+ package_create.set_defaults(handler=_handle_package_create, format_hint="builder_summary")
57
+
58
+ package_attach_app = package_subparsers.add_parser("attach-app", help="将应用挂载到应用包")
59
+ package_attach_app.add_argument("--tag-id", type=int, required=True)
60
+ package_attach_app.add_argument("--app-key", required=True)
61
+ package_attach_app.add_argument("--app-title", dest="legacy_app_title", help=argparse.SUPPRESS)
62
+ package_attach_app.set_defaults(handler=_handle_package_attach_app, format_hint="builder_summary")
63
+
64
+ app = builder_subparsers.add_parser("app", help="应用")
65
+ app_subparsers = app.add_subparsers(dest="builder_app_command", required=True)
66
+
67
+ app_resolve = app_subparsers.add_parser("resolve", help="解析应用")
68
+ app_resolve.add_argument("--app-key", default="")
69
+ app_resolve.add_argument("--app-name", default="")
70
+ app_resolve.add_argument("--package-tag-id", type=int)
71
+ app_resolve.set_defaults(handler=_handle_app_resolve, format_hint="builder_summary")
72
+
73
+ app_release_lock = app_subparsers.add_parser("release-edit-lock-if-mine", help="释放当前账号持有的编辑锁")
74
+ app_release_lock.add_argument("--app-key", required=True)
75
+ app_release_lock.add_argument("--lock-owner-email", required=True)
76
+ app_release_lock.add_argument("--lock-owner-name", required=True)
77
+ app_release_lock.set_defaults(handler=_handle_app_release_edit_lock_if_mine, format_hint="builder_summary")
78
+
79
+ button = builder_subparsers.add_parser("button", help="自定义按钮")
80
+ button_subparsers = button.add_subparsers(dest="builder_button_command", required=True)
81
+
82
+ button_list = button_subparsers.add_parser("list", help="列出自定义按钮")
83
+ button_list.add_argument("--app-key", required=True)
84
+ button_list.set_defaults(handler=_handle_button_list, format_hint="builder_summary")
85
+
86
+ button_get = button_subparsers.add_parser("get", help="读取自定义按钮")
87
+ button_get.add_argument("--app-key", required=True)
88
+ button_get.add_argument("--button-id", type=int, required=True)
89
+ button_get.set_defaults(handler=_handle_button_get, format_hint="builder_summary")
90
+
91
+ button_create = button_subparsers.add_parser("create", help="创建自定义按钮")
92
+ button_create.add_argument("--app-key", required=True)
93
+ button_create.add_argument("--payload-file", required=True)
94
+ button_create.set_defaults(handler=_handle_button_create, format_hint="builder_summary")
95
+
96
+ button_update = button_subparsers.add_parser("update", help="更新自定义按钮")
97
+ button_update.add_argument("--app-key", required=True)
98
+ button_update.add_argument("--button-id", type=int, required=True)
99
+ button_update.add_argument("--payload-file", required=True)
100
+ button_update.set_defaults(handler=_handle_button_update, format_hint="builder_summary")
101
+
102
+ button_delete = button_subparsers.add_parser("delete", help="删除自定义按钮")
103
+ button_delete.add_argument("--app-key", required=True)
104
+ button_delete.add_argument("--button-id", type=int, required=True)
105
+ button_delete.set_defaults(handler=_handle_button_delete, format_hint="builder_summary")
106
+
107
+ for name, help_text, handler in [
108
+ ("read-summary", "读取应用摘要", _handle_app_read_summary),
109
+ ("read-fields", "读取字段摘要", _handle_app_read_fields),
110
+ ("read-layout", "读取布局摘要", _handle_app_read_layout),
111
+ ("read-views", "读取视图摘要", _handle_app_read_views),
112
+ ("read-flow", "读取流程摘要", _handle_app_read_flow),
113
+ ("read-charts", "读取报表摘要", _handle_app_read_charts),
114
+ ]:
115
+ sub = app_subparsers.add_parser(name, help=help_text)
116
+ sub.add_argument("--app-key", required=True)
117
+ sub.set_defaults(handler=handler, format_hint="builder_summary")
118
+
119
+ portal = builder_subparsers.add_parser("portal", help="门户")
120
+ portal_subparsers = portal.add_subparsers(dest="builder_portal_command", required=True)
121
+ portal_list = portal_subparsers.add_parser("list", help="列出可访问门户")
122
+ portal_list.set_defaults(handler=_handle_portal_list, format_hint="builder_summary")
123
+
124
+ portal_get = portal_subparsers.add_parser("get", help="读取门户详情")
125
+ portal_get.add_argument("--dash-key", required=True)
126
+ portal_get.add_argument("--being-draft", action=argparse.BooleanOptionalAction, default=True)
127
+ portal_get.set_defaults(handler=_handle_portal_get, format_hint="builder_summary")
128
+
129
+ portal_read = portal_subparsers.add_parser("read-summary", help="读取门户摘要")
130
+ portal_read.add_argument("--dash-key", required=True)
131
+ portal_read.add_argument("--being-draft", action=argparse.BooleanOptionalAction, default=True)
132
+ portal_read.set_defaults(handler=_handle_portal_read_summary, format_hint="builder_summary")
133
+
134
+ portal_apply = portal_subparsers.add_parser("apply", help="更新门户")
135
+ portal_apply.add_argument("--dash-key", default="")
136
+ portal_apply.add_argument("--dash-name", default="")
137
+ portal_apply.add_argument("--package-tag-id", type=int)
138
+ portal_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
139
+ portal_apply.add_argument("--sections-file", required=True)
140
+ portal_apply.add_argument("--auth-file")
141
+ portal_apply.add_argument("--icon")
142
+ portal_apply.add_argument("--color")
143
+ portal_apply.add_argument("--hide-copyright", action=argparse.BooleanOptionalAction, default=None)
144
+ portal_apply.add_argument("--dash-global-config-file")
145
+ portal_apply.add_argument("--config-file")
146
+ portal_apply.set_defaults(handler=_handle_portal_apply, format_hint="builder_summary")
147
+
148
+ schema_apply = builder_subparsers.add_parser("schema", help="字段搭建")
149
+ schema_apply_subparsers = schema_apply.add_subparsers(dest="builder_schema_command", required=True)
150
+ schema_apply_apply = schema_apply_subparsers.add_parser("apply", help="执行字段变更")
151
+ schema_apply_apply.add_argument("--app-key", default="")
152
+ schema_apply_apply.add_argument("--package-tag-id", type=int)
153
+ schema_apply_apply.add_argument("--app-name", default="")
154
+ schema_apply_apply.add_argument("--app-title", default="")
155
+ schema_apply_apply.add_argument("--icon")
156
+ schema_apply_apply.add_argument("--color")
157
+ schema_apply_apply.add_argument("--create-if-missing", action="store_true")
158
+ schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
159
+ schema_apply_apply.add_argument("--add-fields-file")
160
+ schema_apply_apply.add_argument("--update-fields-file")
161
+ schema_apply_apply.add_argument("--remove-fields-file")
162
+ schema_apply_apply.set_defaults(handler=_handle_schema_apply, format_hint="builder_summary")
163
+
164
+ layout_apply = builder_subparsers.add_parser("layout", help="布局")
165
+ layout_apply_subparsers = layout_apply.add_subparsers(dest="builder_layout_command", required=True)
166
+ layout_apply_apply = layout_apply_subparsers.add_parser("apply", help="执行布局变更")
167
+ layout_apply_apply.add_argument("--app-key", required=True)
168
+ layout_apply_apply.add_argument("--mode", choices=["merge", "replace"], default="merge")
169
+ layout_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
170
+ layout_apply_apply.add_argument("--sections-file", required=True)
171
+ layout_apply_apply.set_defaults(handler=_handle_layout_apply, format_hint="builder_summary")
172
+
173
+ views_apply = builder_subparsers.add_parser("views", help="视图")
174
+ views_apply_subparsers = views_apply.add_subparsers(dest="builder_views_command", required=True)
175
+ views_apply_apply = views_apply_subparsers.add_parser("apply", help="执行视图变更")
176
+ views_apply_apply.add_argument("--app-key", required=True)
177
+ views_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
178
+ views_apply_apply.add_argument("--upsert-views-file")
179
+ views_apply_apply.add_argument("--remove-views-file")
180
+ views_apply_apply.set_defaults(handler=_handle_views_apply, format_hint="builder_summary")
181
+
182
+ flow_apply = builder_subparsers.add_parser("flow", help="流程")
183
+ flow_apply_subparsers = flow_apply.add_subparsers(dest="builder_flow_command", required=True)
184
+ flow_apply_apply = flow_apply_subparsers.add_parser("apply", help="执行流程变更")
185
+ flow_apply_apply.add_argument("--app-key", required=True)
186
+ flow_apply_apply.add_argument("--mode", choices=["replace"], default="replace")
187
+ flow_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
188
+ flow_apply_apply.add_argument("--nodes-file", required=True)
189
+ flow_apply_apply.add_argument("--transitions-file", required=True)
190
+ flow_apply_apply.set_defaults(handler=_handle_flow_apply, format_hint="builder_summary")
191
+
192
+ charts_apply = builder_subparsers.add_parser("charts", help="报表")
193
+ charts_apply_subparsers = charts_apply.add_subparsers(dest="builder_charts_command", required=True)
194
+ charts_apply_apply = charts_apply_subparsers.add_parser("apply", help="执行报表变更")
195
+ charts_apply_apply.add_argument("--app-key", required=True)
196
+ charts_apply_apply.add_argument("--upsert-file")
197
+ charts_apply_apply.add_argument("--remove-chart-ids-file")
198
+ charts_apply_apply.add_argument("--reorder-chart-ids-file")
199
+ charts_apply_apply.set_defaults(handler=_handle_charts_apply, format_hint="builder_summary")
200
+
201
+ publish_verify = builder_subparsers.add_parser("publish", help="发布校验")
202
+ publish_verify_subparsers = publish_verify.add_subparsers(dest="builder_publish_command", required=True)
203
+ publish_verify_verify = publish_verify_subparsers.add_parser("verify", help="校验应用发布")
204
+ publish_verify_verify.add_argument("--app-key", required=True)
205
+ publish_verify_verify.add_argument("--expected-package-tag-id", type=int)
206
+ publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary")
207
+
208
+ view = builder_subparsers.add_parser("view", help="视图详情")
209
+ view_subparsers = view.add_subparsers(dest="builder_view_command", required=True)
210
+ view_get = view_subparsers.add_parser("get", help="读取视图详情")
211
+ view_get.add_argument("--viewgraph-key", required=True)
212
+ view_get.set_defaults(handler=_handle_view_get, format_hint="builder_summary")
213
+
214
+ chart = builder_subparsers.add_parser("chart", help="报表详情")
215
+ chart_subparsers = chart.add_subparsers(dest="builder_chart_command", required=True)
216
+ chart_get = chart_subparsers.add_parser("get", help="读取报表详情和数据")
217
+ chart_get.add_argument("--chart-id", required=True)
218
+ chart_get.add_argument("--data-payload-file")
219
+ chart_get.add_argument("--page-num", type=int)
220
+ chart_get.add_argument("--page-size", type=int)
221
+ chart_get.add_argument("--page-num-y", type=int)
222
+ chart_get.add_argument("--page-size-y", type=int)
223
+ chart_get.set_defaults(handler=_handle_chart_get, format_hint="builder_summary")
224
+
225
+
226
+ def _handle_package_list(args: argparse.Namespace, context: CliContext) -> dict:
227
+ return context.builder.package_list(profile=args.profile, trial_status=args.trial_status)
228
+
229
+
230
+ def _handle_contract(args: argparse.Namespace, context: CliContext) -> dict:
231
+ return context.builder.builder_tool_contract(tool_name=args.tool_name)
232
+
233
+
234
+ def _handle_member_search(args: argparse.Namespace, context: CliContext) -> dict:
235
+ return context.builder.member_search(
236
+ profile=args.profile,
237
+ query=args.query,
238
+ page_num=args.page_num,
239
+ page_size=args.page_size,
240
+ contain_disable=bool(args.contain_disable),
241
+ )
242
+
243
+
244
+ def _handle_role_search(args: argparse.Namespace, context: CliContext) -> dict:
245
+ return context.builder.role_search(
246
+ profile=args.profile,
247
+ keyword=args.keyword,
248
+ page_num=args.page_num,
249
+ page_size=args.page_size,
250
+ )
251
+
252
+
253
+ def _handle_role_create(args: argparse.Namespace, context: CliContext) -> dict:
254
+ return context.builder.role_create(
255
+ profile=args.profile,
256
+ role_name=args.role_name,
257
+ member_uids=load_list_arg(args.member_uids_file, option_name="--member-uids-file"),
258
+ member_emails=load_list_arg(args.member_emails_file, option_name="--member-emails-file"),
259
+ member_names=load_list_arg(args.member_names_file, option_name="--member-names-file"),
260
+ role_icon=args.role_icon,
261
+ )
262
+
263
+
264
+ def _handle_package_resolve(args: argparse.Namespace, context: CliContext) -> dict:
265
+ return context.builder.package_resolve(profile=args.profile, package_name=args.package_name)
266
+
267
+
268
+ def _handle_package_create(args: argparse.Namespace, context: CliContext) -> dict:
269
+ return context.builder.package_create(
270
+ profile=args.profile,
271
+ package_name=args.package_name,
272
+ icon=args.icon,
273
+ color=args.color,
274
+ )
275
+
276
+
277
+ def _handle_package_attach_app(args: argparse.Namespace, context: CliContext) -> dict:
278
+ if (args.legacy_app_title or "").strip():
279
+ raise_config_error(
280
+ "package attach-app no longer accepts app_title.",
281
+ fix_hint="Use `--app-key` to identify the app to attach.",
282
+ )
283
+ return context.builder.package_attach_app(
284
+ profile=args.profile,
285
+ tag_id=args.tag_id,
286
+ app_key=args.app_key,
287
+ )
288
+
289
+
290
+ def _handle_app_resolve(args: argparse.Namespace, context: CliContext) -> dict:
291
+ has_app_key = bool((args.app_key or "").strip())
292
+ has_app_name = bool((args.app_name or "").strip())
293
+ has_package_tag_id = args.package_tag_id is not None
294
+ if has_app_key and (has_app_name or has_package_tag_id):
295
+ raise_config_error(
296
+ "app resolve accepts exactly one selector mode.",
297
+ fix_hint="Use only `--app-key`, or use `--app-name` together with `--package-tag-id`.",
298
+ )
299
+ if not has_app_key and not (has_app_name and has_package_tag_id):
300
+ raise_config_error(
301
+ "app resolve requires either --app-key, or --app-name together with --package-tag-id.",
302
+ fix_hint="For an existing known app, pass `--app-key`. For package-scoped lookup, pass both `--app-name` and `--package-tag-id`.",
303
+ )
304
+ return context.builder.app_resolve(
305
+ profile=args.profile,
306
+ app_key=args.app_key,
307
+ app_name=args.app_name,
308
+ package_tag_id=args.package_tag_id,
309
+ )
310
+
311
+
312
+ def _handle_app_release_edit_lock_if_mine(args: argparse.Namespace, context: CliContext) -> dict:
313
+ return context.builder.app_release_edit_lock_if_mine(
314
+ profile=args.profile,
315
+ app_key=args.app_key,
316
+ lock_owner_email=args.lock_owner_email,
317
+ lock_owner_name=args.lock_owner_name,
318
+ )
319
+
320
+
321
+ def _handle_button_list(args: argparse.Namespace, context: CliContext) -> dict:
322
+ return context.builder.app_custom_button_list(profile=args.profile, app_key=args.app_key)
323
+
324
+
325
+ def _handle_button_get(args: argparse.Namespace, context: CliContext) -> dict:
326
+ return context.builder.app_custom_button_get(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
327
+
328
+
329
+ def _handle_button_create(args: argparse.Namespace, context: CliContext) -> dict:
330
+ return context.builder.app_custom_button_create(
331
+ profile=args.profile,
332
+ app_key=args.app_key,
333
+ payload=load_object_arg(args.payload_file, option_name="--payload-file"),
334
+ )
335
+
336
+
337
+ def _handle_button_update(args: argparse.Namespace, context: CliContext) -> dict:
338
+ return context.builder.app_custom_button_update(
339
+ profile=args.profile,
340
+ app_key=args.app_key,
341
+ button_id=args.button_id,
342
+ payload=load_object_arg(args.payload_file, option_name="--payload-file"),
343
+ )
344
+
345
+
346
+ def _handle_button_delete(args: argparse.Namespace, context: CliContext) -> dict:
347
+ return context.builder.app_custom_button_delete(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
348
+
349
+
350
+ def _handle_app_read_summary(args: argparse.Namespace, context: CliContext) -> dict:
351
+ return context.builder.app_read_summary(profile=args.profile, app_key=args.app_key)
352
+
353
+
354
+ def _handle_app_read_fields(args: argparse.Namespace, context: CliContext) -> dict:
355
+ return context.builder.app_read_fields(profile=args.profile, app_key=args.app_key)
356
+
357
+
358
+ def _handle_app_read_layout(args: argparse.Namespace, context: CliContext) -> dict:
359
+ return context.builder.app_read_layout_summary(profile=args.profile, app_key=args.app_key)
360
+
361
+
362
+ def _handle_app_read_views(args: argparse.Namespace, context: CliContext) -> dict:
363
+ return context.builder.app_read_views_summary(profile=args.profile, app_key=args.app_key)
364
+
365
+
366
+ def _handle_app_read_flow(args: argparse.Namespace, context: CliContext) -> dict:
367
+ return context.builder.app_read_flow_summary(profile=args.profile, app_key=args.app_key)
368
+
369
+
370
+ def _handle_app_read_charts(args: argparse.Namespace, context: CliContext) -> dict:
371
+ return context.builder.app_read_charts_summary(profile=args.profile, app_key=args.app_key)
372
+
373
+
374
+ def _handle_portal_list(args: argparse.Namespace, context: CliContext) -> dict:
375
+ return context.builder.portal_list(profile=args.profile)
376
+
377
+
378
+ def _handle_portal_get(args: argparse.Namespace, context: CliContext) -> dict:
379
+ return context.builder.portal_get(profile=args.profile, dash_key=args.dash_key, being_draft=bool(args.being_draft))
380
+
381
+
382
+ def _handle_portal_read_summary(args: argparse.Namespace, context: CliContext) -> dict:
383
+ return context.builder.portal_read_summary(
384
+ profile=args.profile,
385
+ dash_key=args.dash_key,
386
+ being_draft=bool(args.being_draft),
387
+ )
388
+
389
+
390
+ def _handle_view_get(args: argparse.Namespace, context: CliContext) -> dict:
391
+ return context.builder.view_get(profile=args.profile, viewgraph_key=args.viewgraph_key)
392
+
393
+
394
+ def _handle_chart_get(args: argparse.Namespace, context: CliContext) -> dict:
395
+ return context.builder.chart_get(
396
+ profile=args.profile,
397
+ chart_id=args.chart_id,
398
+ data_payload=load_object_arg(args.data_payload_file, option_name="--data-payload-file") if args.data_payload_file else {},
399
+ page_num=args.page_num,
400
+ page_size=args.page_size,
401
+ page_num_y=args.page_num_y,
402
+ page_size_y=args.page_size_y,
403
+ )
404
+
405
+
406
+ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
407
+ has_app_key = bool((args.app_key or "").strip())
408
+ has_app_name = bool((args.app_name or "").strip())
409
+ has_app_title = bool((args.app_title or "").strip())
410
+ has_package_tag_id = args.package_tag_id is not None
411
+ if has_app_key:
412
+ if args.create_if_missing or has_app_name or has_package_tag_id or has_app_title:
413
+ raise_config_error(
414
+ "schema apply edit mode only accepts --app-key as the resource selector.",
415
+ fix_hint="For existing apps, pass `--app-key` only. For create mode, use `--package-tag-id --app-name --create-if-missing`.",
416
+ )
417
+ else:
418
+ if not args.create_if_missing or not has_package_tag_id or not has_app_name:
419
+ raise_config_error(
420
+ "schema apply create mode requires --package-tag-id, --app-name, and --create-if-missing.",
421
+ fix_hint="Use `--app-key` for existing apps, or pass `--package-tag-id --app-name --create-if-missing` to create a new app.",
422
+ )
423
+ return context.builder.app_schema_apply(
424
+ profile=args.profile,
425
+ app_key=args.app_key,
426
+ package_tag_id=args.package_tag_id,
427
+ app_name=args.app_name,
428
+ app_title=args.app_title,
429
+ icon=args.icon,
430
+ color=args.color,
431
+ create_if_missing=bool(args.create_if_missing),
432
+ publish=bool(args.publish),
433
+ add_fields=load_list_arg(args.add_fields_file, option_name="--add-fields-file"),
434
+ update_fields=load_list_arg(args.update_fields_file, option_name="--update-fields-file"),
435
+ remove_fields=load_list_arg(args.remove_fields_file, option_name="--remove-fields-file"),
436
+ )
437
+
438
+
439
+ def _handle_layout_apply(args: argparse.Namespace, context: CliContext) -> dict:
440
+ return context.builder.app_layout_apply(
441
+ profile=args.profile,
442
+ app_key=args.app_key,
443
+ mode=args.mode,
444
+ publish=bool(args.publish),
445
+ sections=require_list_arg(args.sections_file, option_name="--sections-file"),
446
+ )
447
+
448
+
449
+ def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
450
+ return context.builder.app_views_apply(
451
+ profile=args.profile,
452
+ app_key=args.app_key,
453
+ publish=bool(args.publish),
454
+ upsert_views=load_list_arg(args.upsert_views_file, option_name="--upsert-views-file"),
455
+ remove_views=load_list_arg(args.remove_views_file, option_name="--remove-views-file"),
456
+ )
457
+
458
+
459
+ def _handle_flow_apply(args: argparse.Namespace, context: CliContext) -> dict:
460
+ return context.builder.app_flow_apply(
461
+ profile=args.profile,
462
+ app_key=args.app_key,
463
+ mode=args.mode,
464
+ publish=bool(args.publish),
465
+ nodes=require_list_arg(args.nodes_file, option_name="--nodes-file"),
466
+ transitions=require_list_arg(args.transitions_file, option_name="--transitions-file"),
467
+ )
468
+
469
+
470
+ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
471
+ return context.builder.app_charts_apply(
472
+ profile=args.profile,
473
+ app_key=args.app_key,
474
+ upsert_charts=load_list_arg(args.upsert_file, option_name="--upsert-file"),
475
+ remove_chart_ids=load_list_arg(args.remove_chart_ids_file, option_name="--remove-chart-ids-file"),
476
+ reorder_chart_ids=load_list_arg(args.reorder_chart_ids_file, option_name="--reorder-chart-ids-file"),
477
+ )
478
+
479
+
480
+ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
481
+ has_dash_key = bool((args.dash_key or "").strip())
482
+ has_dash_name = bool((args.dash_name or "").strip())
483
+ has_package_tag_id = args.package_tag_id is not None
484
+ if has_dash_key and has_package_tag_id:
485
+ raise_config_error(
486
+ "portal apply accepts exactly one selector mode.",
487
+ fix_hint="Use `--dash-key` to update an existing portal, or use `--package-tag-id --dash-name` to create a new portal.",
488
+ )
489
+ if not has_dash_key and not (has_package_tag_id and has_dash_name):
490
+ raise_config_error(
491
+ "portal apply requires either --dash-key, or --package-tag-id together with --dash-name.",
492
+ fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-tag-id --dash-name`.",
493
+ )
494
+ return context.builder.portal_apply(
495
+ profile=args.profile,
496
+ dash_key=args.dash_key,
497
+ dash_name=args.dash_name,
498
+ package_tag_id=args.package_tag_id,
499
+ publish=bool(args.publish),
500
+ sections=require_list_arg(args.sections_file, option_name="--sections-file"),
501
+ auth=load_object_arg(args.auth_file, option_name="--auth-file"),
502
+ icon=args.icon,
503
+ color=args.color,
504
+ hide_copyright=args.hide_copyright,
505
+ dash_global_config=load_object_arg(args.dash_global_config_file, option_name="--dash-global-config-file"),
506
+ config=load_object_arg(args.config_file, option_name="--config-file"),
507
+ )
508
+
509
+
510
+ def _handle_publish_verify(args: argparse.Namespace, context: CliContext) -> dict:
511
+ return context.builder.app_publish_verify(
512
+ profile=args.profile,
513
+ app_key=args.app_key,
514
+ expected_package_tag_id=args.expected_package_tag_id,
515
+ )