@josephyan/qingflow-cli 0.2.0-beta.1000

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 (92) hide show
  1. package/README.md +31 -0
  2. package/docs/local-agent-install.md +309 -0
  3. package/entry_point.py +13 -0
  4. package/npm/bin/qingflow.mjs +5 -0
  5. package/npm/lib/runtime.mjs +346 -0
  6. package/npm/scripts/postinstall.mjs +16 -0
  7. package/package.json +34 -0
  8. package/pyproject.toml +67 -0
  9. package/qingflow +15 -0
  10. package/src/qingflow_mcp/__init__.py +37 -0
  11. package/src/qingflow_mcp/__main__.py +5 -0
  12. package/src/qingflow_mcp/backend_client.py +649 -0
  13. package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
  14. package/src/qingflow_mcp/builder_facade/models.py +1846 -0
  15. package/src/qingflow_mcp/builder_facade/service.py +16502 -0
  16. package/src/qingflow_mcp/cli/__init__.py +1 -0
  17. package/src/qingflow_mcp/cli/commands/__init__.py +18 -0
  18. package/src/qingflow_mcp/cli/commands/app.py +40 -0
  19. package/src/qingflow_mcp/cli/commands/auth.py +112 -0
  20. package/src/qingflow_mcp/cli/commands/builder.py +539 -0
  21. package/src/qingflow_mcp/cli/commands/chart.py +18 -0
  22. package/src/qingflow_mcp/cli/commands/common.py +62 -0
  23. package/src/qingflow_mcp/cli/commands/imports.py +96 -0
  24. package/src/qingflow_mcp/cli/commands/portal.py +25 -0
  25. package/src/qingflow_mcp/cli/commands/record.py +331 -0
  26. package/src/qingflow_mcp/cli/commands/repo.py +80 -0
  27. package/src/qingflow_mcp/cli/commands/task.py +141 -0
  28. package/src/qingflow_mcp/cli/commands/view.py +18 -0
  29. package/src/qingflow_mcp/cli/commands/workspace.py +110 -0
  30. package/src/qingflow_mcp/cli/context.py +60 -0
  31. package/src/qingflow_mcp/cli/formatters.py +573 -0
  32. package/src/qingflow_mcp/cli/json_io.py +50 -0
  33. package/src/qingflow_mcp/cli/main.py +186 -0
  34. package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
  35. package/src/qingflow_mcp/cli/terminal_ui.py +173 -0
  36. package/src/qingflow_mcp/config.py +407 -0
  37. package/src/qingflow_mcp/errors.py +66 -0
  38. package/src/qingflow_mcp/id_utils.py +49 -0
  39. package/src/qingflow_mcp/import_store.py +121 -0
  40. package/src/qingflow_mcp/json_types.py +18 -0
  41. package/src/qingflow_mcp/list_type_labels.py +76 -0
  42. package/src/qingflow_mcp/public_surface.py +243 -0
  43. package/src/qingflow_mcp/repository_store.py +71 -0
  44. package/src/qingflow_mcp/response_trim.py +841 -0
  45. package/src/qingflow_mcp/server.py +216 -0
  46. package/src/qingflow_mcp/server_app_builder.py +543 -0
  47. package/src/qingflow_mcp/server_app_user.py +386 -0
  48. package/src/qingflow_mcp/session_store.py +369 -0
  49. package/src/qingflow_mcp/solution/__init__.py +6 -0
  50. package/src/qingflow_mcp/solution/build_assembly_store.py +181 -0
  51. package/src/qingflow_mcp/solution/compiler/__init__.py +282 -0
  52. package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
  53. package/src/qingflow_mcp/solution/compiler/form_compiler.py +495 -0
  54. package/src/qingflow_mcp/solution/compiler/icon_utils.py +187 -0
  55. package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
  56. package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
  57. package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
  58. package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
  59. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
  60. package/src/qingflow_mcp/solution/design_session.py +222 -0
  61. package/src/qingflow_mcp/solution/design_store.py +100 -0
  62. package/src/qingflow_mcp/solution/executor.py +2398 -0
  63. package/src/qingflow_mcp/solution/normalizer.py +23 -0
  64. package/src/qingflow_mcp/solution/requirements_builder.py +536 -0
  65. package/src/qingflow_mcp/solution/run_store.py +244 -0
  66. package/src/qingflow_mcp/solution/spec_models.py +855 -0
  67. package/src/qingflow_mcp/tools/__init__.py +1 -0
  68. package/src/qingflow_mcp/tools/ai_builder_tools.py +3449 -0
  69. package/src/qingflow_mcp/tools/app_tools.py +926 -0
  70. package/src/qingflow_mcp/tools/approval_tools.py +1062 -0
  71. package/src/qingflow_mcp/tools/auth_tools.py +1133 -0
  72. package/src/qingflow_mcp/tools/base.py +281 -0
  73. package/src/qingflow_mcp/tools/code_block_tools.py +777 -0
  74. package/src/qingflow_mcp/tools/custom_button_tools.py +202 -0
  75. package/src/qingflow_mcp/tools/directory_tools.py +675 -0
  76. package/src/qingflow_mcp/tools/feedback_tools.py +238 -0
  77. package/src/qingflow_mcp/tools/file_tools.py +409 -0
  78. package/src/qingflow_mcp/tools/import_tools.py +2223 -0
  79. package/src/qingflow_mcp/tools/navigation_tools.py +210 -0
  80. package/src/qingflow_mcp/tools/package_tools.py +326 -0
  81. package/src/qingflow_mcp/tools/portal_tools.py +158 -0
  82. package/src/qingflow_mcp/tools/qingbi_report_tools.py +374 -0
  83. package/src/qingflow_mcp/tools/record_tools.py +14291 -0
  84. package/src/qingflow_mcp/tools/repository_dev_tools.py +552 -0
  85. package/src/qingflow_mcp/tools/resource_read_tools.py +503 -0
  86. package/src/qingflow_mcp/tools/role_tools.py +112 -0
  87. package/src/qingflow_mcp/tools/solution_tools.py +4054 -0
  88. package/src/qingflow_mcp/tools/task_context_tools.py +2986 -0
  89. package/src/qingflow_mcp/tools/task_tools.py +889 -0
  90. package/src/qingflow_mcp/tools/view_tools.py +335 -0
  91. package/src/qingflow_mcp/tools/workflow_tools.py +376 -0
  92. package/src/qingflow_mcp/tools/workspace_tools.py +266 -0
@@ -0,0 +1,539 @@
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
+ file_parser = builder_subparsers.add_parser("file", help="builder 侧文件上传")
14
+ file_subparsers = file_parser.add_subparsers(dest="builder_file_command", required=True)
15
+ file_upload_local = file_subparsers.add_parser("upload-local", help="上传本地文件并返回附件值")
16
+ file_upload_local.add_argument("--upload-kind", default="attachment")
17
+ file_upload_local.add_argument("--file-path", required=True)
18
+ file_upload_local.add_argument("--upload-mark")
19
+ file_upload_local.add_argument("--content-type")
20
+ file_upload_local.add_argument("--bucket-type")
21
+ file_upload_local.add_argument("--path-id", type=int)
22
+ file_upload_local.add_argument("--file-related-url")
23
+ file_upload_local.set_defaults(handler=_handle_file_upload_local, format_hint="generic")
24
+
25
+ feedback = builder_subparsers.add_parser("feedback", help="builder 侧反馈提交")
26
+ feedback_subparsers = feedback.add_subparsers(dest="builder_feedback_command", required=True)
27
+ feedback_submit = feedback_subparsers.add_parser("submit", help="提交 builder 能力反馈")
28
+ feedback_submit.add_argument("--category", required=True)
29
+ feedback_submit.add_argument("--title", required=True)
30
+ feedback_submit.add_argument("--description", required=True)
31
+ feedback_submit.add_argument("--expected-behavior")
32
+ feedback_submit.add_argument("--actual-behavior")
33
+ feedback_submit.add_argument("--impact-scope")
34
+ feedback_submit.add_argument("--tool-name")
35
+ feedback_submit.add_argument("--app-key")
36
+ feedback_submit.add_argument("--record-id")
37
+ feedback_submit.add_argument("--workflow-node-id")
38
+ feedback_submit.add_argument("--note")
39
+ feedback_submit.set_defaults(handler=_handle_feedback_submit, format_hint="generic")
40
+
41
+ contract = builder_subparsers.add_parser("contract", help="读取 builder tool 合约")
42
+ contract.add_argument("--tool-name", required=True)
43
+ contract.set_defaults(handler=_handle_contract, format_hint="builder_summary")
44
+
45
+ member = builder_subparsers.add_parser("member", help="成员目录")
46
+ member_subparsers = member.add_subparsers(dest="builder_member_command", required=True)
47
+ member_search = member_subparsers.add_parser("search", help="搜索成员")
48
+ member_search.add_argument("--query", default="")
49
+ member_search.add_argument("--page-num", type=int, default=1)
50
+ member_search.add_argument("--page-size", type=int, default=20)
51
+ member_search.add_argument("--contain-disable", action=argparse.BooleanOptionalAction, default=False)
52
+ member_search.set_defaults(handler=_handle_member_search, format_hint="builder_summary")
53
+
54
+ role = builder_subparsers.add_parser("role", help="角色目录")
55
+ role_subparsers = role.add_subparsers(dest="builder_role_command", required=True)
56
+ role_search = role_subparsers.add_parser("search", help="搜索角色")
57
+ role_search.add_argument("--keyword", default="")
58
+ role_search.add_argument("--page-num", type=int, default=1)
59
+ role_search.add_argument("--page-size", type=int, default=20)
60
+ role_search.set_defaults(handler=_handle_role_search, format_hint="builder_summary")
61
+
62
+ role_create = role_subparsers.add_parser("create", help="创建角色")
63
+ role_create.add_argument("--role-name", required=True)
64
+ role_create.add_argument("--member-uids-file")
65
+ role_create.add_argument("--member-emails-file")
66
+ role_create.add_argument("--member-names-file")
67
+ role_create.add_argument("--role-icon", default="ex-user-outlined")
68
+ role_create.set_defaults(handler=_handle_role_create, format_hint="builder_summary")
69
+
70
+ package = builder_subparsers.add_parser("package", help="应用包")
71
+ package_subparsers = package.add_subparsers(dest="builder_package_command", required=True)
72
+
73
+ solution = builder_subparsers.add_parser("solution", help="解决方案")
74
+ solution_subparsers = solution.add_subparsers(dest="builder_solution_command", required=True)
75
+ solution_install = solution_subparsers.add_parser("install", help="安装解决方案")
76
+ solution_install.add_argument("--solution-key", required=True)
77
+ solution_install.add_argument("--being-copy-data", action=argparse.BooleanOptionalAction, default=True)
78
+ solution_install.add_argument("--solution-source", default="solutionDetail")
79
+ solution_install.set_defaults(handler=_handle_solution_install, format_hint="builder_summary")
80
+
81
+ package_get = package_subparsers.add_parser("get", help="读取应用包详情")
82
+ package_get.add_argument("--package-id", type=int, required=True)
83
+ package_get.set_defaults(handler=_handle_package_get, format_hint="builder_summary")
84
+
85
+ package_apply = package_subparsers.add_parser("apply", help="创建或更新应用包配置")
86
+ package_apply.add_argument("--config-file", required=True)
87
+ package_apply.set_defaults(handler=_handle_package_apply, format_hint="builder_summary")
88
+
89
+ app = builder_subparsers.add_parser("app", help="应用")
90
+ app_subparsers = app.add_subparsers(dest="builder_app_command", required=True)
91
+
92
+ app_resolve = app_subparsers.add_parser("resolve", help="解析应用")
93
+ app_resolve.add_argument("--app-key", default="")
94
+ app_resolve.add_argument("--app-name", default="")
95
+ app_resolve.add_argument("--package-id", type=int)
96
+ app_resolve.set_defaults(handler=_handle_app_resolve, format_hint="builder_summary")
97
+
98
+ app_release_lock = app_subparsers.add_parser("release-edit-lock-if-mine", help="释放当前账号持有的编辑锁")
99
+ app_release_lock.add_argument("--app-key", required=True)
100
+ app_release_lock.add_argument("--lock-owner-email", required=True)
101
+ app_release_lock.add_argument("--lock-owner-name", required=True)
102
+ app_release_lock.set_defaults(handler=_handle_app_release_edit_lock_if_mine, format_hint="builder_summary")
103
+
104
+ app_get = app_subparsers.add_parser("get", help="读取应用配置(字段请使用: builder app get --app-key APP fields)")
105
+ app_get.add_argument(
106
+ "builder_app_get_section",
107
+ nargs="?",
108
+ choices=["summary", "fields", "layout", "views", "flow", "charts"],
109
+ default="summary",
110
+ )
111
+ app_get.add_argument("--app-key", required=True)
112
+ app_get.set_defaults(handler=_handle_app_get, format_hint="builder_summary")
113
+
114
+ app_repair_code_blocks = app_subparsers.add_parser("repair-code-blocks", help="扫描或修复现有代码块配置")
115
+ app_repair_code_blocks.add_argument("--app-key", required=True)
116
+ app_repair_code_blocks.add_argument("--field")
117
+ app_repair_code_blocks.add_argument("--apply", action="store_true")
118
+ app_repair_code_blocks.set_defaults(handler=_handle_app_repair_code_blocks, format_hint="builder_summary")
119
+
120
+ button = builder_subparsers.add_parser("button", help="自定义按钮")
121
+ button_subparsers = button.add_subparsers(dest="builder_button_command", required=True)
122
+
123
+ button_list = button_subparsers.add_parser("list", help="列出自定义按钮")
124
+ button_list.add_argument("--app-key", required=True)
125
+ button_list.set_defaults(handler=_handle_button_list, format_hint="builder_summary")
126
+
127
+ button_get = button_subparsers.add_parser("get", help="读取自定义按钮")
128
+ button_get.add_argument("--app-key", required=True)
129
+ button_get.add_argument("--button-id", type=int, required=True)
130
+ button_get.set_defaults(handler=_handle_button_get, format_hint="builder_summary")
131
+
132
+ button_create = button_subparsers.add_parser("create", help="创建自定义按钮")
133
+ button_create.add_argument("--app-key", required=True)
134
+ button_create.add_argument("--payload-file", required=True)
135
+ button_create.set_defaults(handler=_handle_button_create, format_hint="builder_summary")
136
+
137
+ button_update = button_subparsers.add_parser("update", help="更新自定义按钮")
138
+ button_update.add_argument("--app-key", required=True)
139
+ button_update.add_argument("--button-id", type=int, required=True)
140
+ button_update.add_argument("--payload-file", required=True)
141
+ button_update.set_defaults(handler=_handle_button_update, format_hint="builder_summary")
142
+
143
+ button_delete = button_subparsers.add_parser("delete", help="删除自定义按钮")
144
+ button_delete.add_argument("--app-key", required=True)
145
+ button_delete.add_argument("--button-id", type=int, required=True)
146
+ button_delete.set_defaults(handler=_handle_button_delete, format_hint="builder_summary")
147
+
148
+ portal = builder_subparsers.add_parser("portal", help="门户")
149
+ portal_subparsers = portal.add_subparsers(dest="builder_portal_command", required=True)
150
+ portal_list = portal_subparsers.add_parser("list", help="列出可配置门户")
151
+ portal_list.set_defaults(handler=_handle_portal_list, format_hint="builder_summary")
152
+
153
+ portal_get = portal_subparsers.add_parser("get", help="读取门户详情")
154
+ portal_get.add_argument("--dash-key", required=True)
155
+ portal_get.add_argument("--being-draft", action=argparse.BooleanOptionalAction, default=True)
156
+ portal_get.set_defaults(handler=_handle_portal_get, format_hint="builder_summary")
157
+
158
+ portal_apply = portal_subparsers.add_parser("apply", help="更新门户")
159
+ portal_apply.add_argument("--dash-key", default="")
160
+ portal_apply.add_argument("--dash-name", default="")
161
+ portal_apply.add_argument("--package-id", type=int)
162
+ portal_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
163
+ portal_apply.add_argument("--sections-file")
164
+ portal_apply.add_argument("--visibility-file")
165
+ portal_apply.add_argument("--auth-file")
166
+ portal_apply.add_argument("--icon")
167
+ portal_apply.add_argument("--color")
168
+ portal_apply.add_argument("--hide-copyright", action=argparse.BooleanOptionalAction, default=None)
169
+ portal_apply.add_argument("--dash-global-config-file")
170
+ portal_apply.add_argument("--config-file")
171
+ portal_apply.set_defaults(handler=_handle_portal_apply, format_hint="builder_summary")
172
+
173
+ schema_apply = builder_subparsers.add_parser("schema", help="字段搭建")
174
+ schema_apply_subparsers = schema_apply.add_subparsers(dest="builder_schema_command", required=True)
175
+ schema_apply_apply = schema_apply_subparsers.add_parser("apply", help="执行字段变更")
176
+ schema_apply_apply.add_argument("--app-key", default="")
177
+ schema_apply_apply.add_argument("--package-id", type=int)
178
+ schema_apply_apply.add_argument("--app-name", default="")
179
+ schema_apply_apply.add_argument("--app-title", default="")
180
+ schema_apply_apply.add_argument("--icon")
181
+ schema_apply_apply.add_argument("--color")
182
+ schema_apply_apply.add_argument("--visibility-file")
183
+ schema_apply_apply.add_argument("--create-if-missing", action="store_true")
184
+ schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
185
+ schema_apply_apply.add_argument("--add-fields-file")
186
+ schema_apply_apply.add_argument("--update-fields-file")
187
+ schema_apply_apply.add_argument("--remove-fields-file")
188
+ schema_apply_apply.set_defaults(handler=_handle_schema_apply, format_hint="builder_summary")
189
+
190
+ layout_apply = builder_subparsers.add_parser("layout", help="布局")
191
+ layout_apply_subparsers = layout_apply.add_subparsers(dest="builder_layout_command", required=True)
192
+ layout_apply_apply = layout_apply_subparsers.add_parser("apply", help="执行布局变更")
193
+ layout_apply_apply.add_argument("--app-key", required=True)
194
+ layout_apply_apply.add_argument("--mode", choices=["merge", "replace"], default="merge")
195
+ layout_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
196
+ layout_apply_apply.add_argument("--sections-file", required=True)
197
+ layout_apply_apply.set_defaults(handler=_handle_layout_apply, format_hint="builder_summary")
198
+
199
+ views_apply = builder_subparsers.add_parser("views", help="视图")
200
+ views_apply_subparsers = views_apply.add_subparsers(dest="builder_views_command", required=True)
201
+ views_apply_apply = views_apply_subparsers.add_parser("apply", help="执行视图变更")
202
+ views_apply_apply.add_argument("--app-key", required=True)
203
+ views_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
204
+ views_apply_apply.add_argument("--upsert-views-file")
205
+ views_apply_apply.add_argument("--remove-views-file")
206
+ views_apply_apply.set_defaults(handler=_handle_views_apply, format_hint="builder_summary")
207
+
208
+ flow_apply = builder_subparsers.add_parser("flow", help="流程")
209
+ flow_apply_subparsers = flow_apply.add_subparsers(dest="builder_flow_command", required=True)
210
+ flow_apply_apply = flow_apply_subparsers.add_parser("apply", help="执行流程变更")
211
+ flow_apply_apply.add_argument("--app-key", required=True)
212
+ flow_apply_apply.add_argument("--mode", choices=["replace"], default="replace")
213
+ flow_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
214
+ flow_apply_apply.add_argument("--nodes-file", required=True)
215
+ flow_apply_apply.add_argument("--transitions-file", required=True)
216
+ flow_apply_apply.set_defaults(handler=_handle_flow_apply, format_hint="builder_summary")
217
+
218
+ charts_apply = builder_subparsers.add_parser("charts", help="报表")
219
+ charts_apply_subparsers = charts_apply.add_subparsers(dest="builder_charts_command", required=True)
220
+ charts_apply_apply = charts_apply_subparsers.add_parser("apply", help="执行报表变更")
221
+ charts_apply_apply.add_argument("--app-key", required=True)
222
+ charts_apply_apply.add_argument("--upsert-file")
223
+ charts_apply_apply.add_argument("--remove-chart-ids-file")
224
+ charts_apply_apply.add_argument("--reorder-chart-ids-file")
225
+ charts_apply_apply.set_defaults(handler=_handle_charts_apply, format_hint="builder_summary")
226
+
227
+ publish_verify = builder_subparsers.add_parser("publish", help="发布校验")
228
+ publish_verify_subparsers = publish_verify.add_subparsers(dest="builder_publish_command", required=True)
229
+ publish_verify_verify = publish_verify_subparsers.add_parser("verify", help="校验应用发布")
230
+ publish_verify_verify.add_argument("--app-key", required=True)
231
+ publish_verify_verify.add_argument("--expected-package-id", type=int)
232
+ publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary")
233
+
234
+ view = builder_subparsers.add_parser("view", help="视图详情")
235
+ view_subparsers = view.add_subparsers(dest="builder_view_command", required=True)
236
+ view_get = view_subparsers.add_parser("get", help="读取视图详情")
237
+ view_get.add_argument("--view-key", required=True)
238
+ view_get.set_defaults(handler=_handle_view_get, format_hint="builder_summary")
239
+
240
+ chart = builder_subparsers.add_parser("chart", help="报表详情")
241
+ chart_subparsers = chart.add_subparsers(dest="builder_chart_command", required=True)
242
+ chart_get = chart_subparsers.add_parser("get", help="读取报表配置详情")
243
+ chart_get.add_argument("--chart-id", required=True)
244
+ chart_get.set_defaults(handler=_handle_chart_get, format_hint="builder_summary")
245
+
246
+
247
+ def _handle_file_upload_local(args: argparse.Namespace, context: CliContext) -> dict:
248
+ return context.files.file_upload_local(
249
+ profile=args.profile,
250
+ upload_kind=args.upload_kind,
251
+ file_path=args.file_path,
252
+ upload_mark=args.upload_mark,
253
+ content_type=args.content_type,
254
+ bucket_type=args.bucket_type,
255
+ path_id=args.path_id,
256
+ file_related_url=args.file_related_url,
257
+ )
258
+
259
+
260
+ def _handle_feedback_submit(args: argparse.Namespace, context: CliContext) -> dict:
261
+ return context.builder_feedback.feedback_submit(
262
+ category=args.category,
263
+ title=args.title,
264
+ description=args.description,
265
+ expected_behavior=args.expected_behavior,
266
+ actual_behavior=args.actual_behavior,
267
+ impact_scope=args.impact_scope,
268
+ tool_name=args.tool_name,
269
+ app_key=args.app_key,
270
+ record_id=args.record_id,
271
+ workflow_node_id=args.workflow_node_id,
272
+ note=args.note,
273
+ )
274
+
275
+
276
+ def _handle_contract(args: argparse.Namespace, context: CliContext) -> dict:
277
+ return context.builder.builder_tool_contract(tool_name=args.tool_name)
278
+
279
+
280
+ def _handle_member_search(args: argparse.Namespace, context: CliContext) -> dict:
281
+ return context.builder.member_search(
282
+ profile=args.profile,
283
+ query=args.query,
284
+ page_num=args.page_num,
285
+ page_size=args.page_size,
286
+ contain_disable=bool(args.contain_disable),
287
+ )
288
+
289
+
290
+ def _handle_role_search(args: argparse.Namespace, context: CliContext) -> dict:
291
+ return context.builder.role_search(
292
+ profile=args.profile,
293
+ keyword=args.keyword,
294
+ page_num=args.page_num,
295
+ page_size=args.page_size,
296
+ )
297
+
298
+
299
+ def _handle_role_create(args: argparse.Namespace, context: CliContext) -> dict:
300
+ return context.builder.role_create(
301
+ profile=args.profile,
302
+ role_name=args.role_name,
303
+ member_uids=load_list_arg(args.member_uids_file, option_name="--member-uids-file"),
304
+ member_emails=load_list_arg(args.member_emails_file, option_name="--member-emails-file"),
305
+ member_names=load_list_arg(args.member_names_file, option_name="--member-names-file"),
306
+ role_icon=args.role_icon,
307
+ )
308
+
309
+
310
+ def _handle_solution_install(args: argparse.Namespace, context: CliContext) -> dict:
311
+ return context.builder.solution_install(
312
+ profile=args.profile,
313
+ solution_key=args.solution_key,
314
+ being_copy_data=bool(args.being_copy_data),
315
+ solution_source=args.solution_source,
316
+ )
317
+
318
+
319
+ def _handle_package_get(args: argparse.Namespace, context: CliContext) -> dict:
320
+ return context.builder.package_get(profile=args.profile, package_id=args.package_id)
321
+
322
+
323
+ def _handle_package_apply(args: argparse.Namespace, context: CliContext) -> dict:
324
+ config = load_object_arg(args.config_file, option_name="--config-file")
325
+ if not isinstance(config, dict):
326
+ raise_config_error("package apply --config-file must contain a JSON object.")
327
+ return context.builder.package_apply(profile=args.profile, **config)
328
+
329
+
330
+ def _handle_app_resolve(args: argparse.Namespace, context: CliContext) -> dict:
331
+ has_app_key = bool((args.app_key or "").strip())
332
+ has_app_name = bool((args.app_name or "").strip())
333
+ has_package_id = args.package_id is not None
334
+ if has_app_key and (has_app_name or has_package_id):
335
+ raise_config_error(
336
+ "app resolve accepts exactly one selector mode.",
337
+ fix_hint="Use only `--app-key`, or use `--app-name` together with `--package-id`.",
338
+ )
339
+ if not has_app_key and not (has_app_name and has_package_id):
340
+ raise_config_error(
341
+ "app resolve requires either --app-key, or --app-name together with --package-id.",
342
+ fix_hint="For an existing known app, pass `--app-key`. For package-scoped lookup, pass both `--app-name` and `--package-id`.",
343
+ )
344
+ return context.builder.app_resolve(
345
+ profile=args.profile,
346
+ app_key=args.app_key,
347
+ app_name=args.app_name,
348
+ package_id=args.package_id,
349
+ )
350
+
351
+
352
+ def _handle_app_release_edit_lock_if_mine(args: argparse.Namespace, context: CliContext) -> dict:
353
+ return context.builder.app_release_edit_lock_if_mine(
354
+ profile=args.profile,
355
+ app_key=args.app_key,
356
+ lock_owner_email=args.lock_owner_email,
357
+ lock_owner_name=args.lock_owner_name,
358
+ )
359
+
360
+
361
+ def _handle_button_list(args: argparse.Namespace, context: CliContext) -> dict:
362
+ return context.builder.app_custom_button_list(profile=args.profile, app_key=args.app_key)
363
+
364
+
365
+ def _handle_button_get(args: argparse.Namespace, context: CliContext) -> dict:
366
+ return context.builder.app_custom_button_get(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
367
+
368
+
369
+ def _handle_button_create(args: argparse.Namespace, context: CliContext) -> dict:
370
+ return context.builder.app_custom_button_create(
371
+ profile=args.profile,
372
+ app_key=args.app_key,
373
+ payload=load_object_arg(args.payload_file, option_name="--payload-file"),
374
+ )
375
+
376
+
377
+ def _handle_button_update(args: argparse.Namespace, context: CliContext) -> dict:
378
+ return context.builder.app_custom_button_update(
379
+ profile=args.profile,
380
+ app_key=args.app_key,
381
+ button_id=args.button_id,
382
+ payload=load_object_arg(args.payload_file, option_name="--payload-file"),
383
+ )
384
+
385
+
386
+ def _handle_button_delete(args: argparse.Namespace, context: CliContext) -> dict:
387
+ return context.builder.app_custom_button_delete(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
388
+
389
+
390
+ def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
391
+ handlers = {
392
+ "summary": context.builder.app_get,
393
+ "fields": context.builder.app_get_fields,
394
+ "layout": context.builder.app_get_layout,
395
+ "views": context.builder.app_get_views,
396
+ "flow": context.builder.app_get_flow,
397
+ "charts": context.builder.app_get_charts,
398
+ }
399
+ return handlers[args.builder_app_get_section](profile=args.profile, app_key=args.app_key)
400
+
401
+
402
+ def _handle_app_repair_code_blocks(args: argparse.Namespace, context: CliContext) -> dict:
403
+ return context.builder.app_repair_code_blocks(
404
+ profile=args.profile,
405
+ app_key=args.app_key,
406
+ field=args.field,
407
+ apply=bool(args.apply),
408
+ )
409
+
410
+
411
+ def _handle_portal_list(args: argparse.Namespace, context: CliContext) -> dict:
412
+ return context.builder.portal_list(profile=args.profile)
413
+
414
+
415
+ def _handle_portal_get(args: argparse.Namespace, context: CliContext) -> dict:
416
+ return context.builder.portal_get(profile=args.profile, dash_key=args.dash_key, being_draft=bool(args.being_draft))
417
+
418
+
419
+ def _handle_view_get(args: argparse.Namespace, context: CliContext) -> dict:
420
+ return context.builder.view_get(profile=args.profile, view_key=args.view_key)
421
+
422
+
423
+ def _handle_chart_get(args: argparse.Namespace, context: CliContext) -> dict:
424
+ return context.builder.chart_get(profile=args.profile, chart_id=args.chart_id)
425
+
426
+
427
+ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
428
+ has_app_key = bool((args.app_key or "").strip())
429
+ has_app_name = bool((args.app_name or "").strip())
430
+ has_app_title = bool((args.app_title or "").strip())
431
+ has_package_id = args.package_id is not None
432
+ if has_app_key:
433
+ if args.create_if_missing or has_package_id:
434
+ raise_config_error(
435
+ "schema apply edit mode accepts --app-key and optional --app-name only.",
436
+ fix_hint="For existing apps, use `--app-key` and optionally `--app-name`. For create mode, use `--package-id --app-name --create-if-missing`.",
437
+ )
438
+ else:
439
+ if not args.create_if_missing or not has_package_id or not (has_app_name or has_app_title):
440
+ raise_config_error(
441
+ "schema apply create mode requires --package-id, --app-name, and --create-if-missing.",
442
+ fix_hint="Use `--app-key` for existing apps, or pass `--package-id --app-name --create-if-missing` to create a new app.",
443
+ )
444
+ return context.builder.app_schema_apply(
445
+ profile=args.profile,
446
+ app_key=args.app_key,
447
+ package_id=args.package_id,
448
+ app_name=args.app_name,
449
+ app_title=args.app_title,
450
+ icon=args.icon,
451
+ color=args.color,
452
+ visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
453
+ create_if_missing=bool(args.create_if_missing),
454
+ publish=bool(args.publish),
455
+ add_fields=load_list_arg(args.add_fields_file, option_name="--add-fields-file"),
456
+ update_fields=load_list_arg(args.update_fields_file, option_name="--update-fields-file"),
457
+ remove_fields=load_list_arg(args.remove_fields_file, option_name="--remove-fields-file"),
458
+ )
459
+
460
+
461
+ def _handle_layout_apply(args: argparse.Namespace, context: CliContext) -> dict:
462
+ return context.builder.app_layout_apply(
463
+ profile=args.profile,
464
+ app_key=args.app_key,
465
+ mode=args.mode,
466
+ publish=bool(args.publish),
467
+ sections=require_list_arg(args.sections_file, option_name="--sections-file"),
468
+ )
469
+
470
+
471
+ def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
472
+ return context.builder.app_views_apply(
473
+ profile=args.profile,
474
+ app_key=args.app_key,
475
+ publish=bool(args.publish),
476
+ upsert_views=load_list_arg(args.upsert_views_file, option_name="--upsert-views-file"),
477
+ remove_views=load_list_arg(args.remove_views_file, option_name="--remove-views-file"),
478
+ )
479
+
480
+
481
+ def _handle_flow_apply(args: argparse.Namespace, context: CliContext) -> dict:
482
+ return context.builder.app_flow_apply(
483
+ profile=args.profile,
484
+ app_key=args.app_key,
485
+ mode=args.mode,
486
+ publish=bool(args.publish),
487
+ nodes=require_list_arg(args.nodes_file, option_name="--nodes-file"),
488
+ transitions=require_list_arg(args.transitions_file, option_name="--transitions-file"),
489
+ )
490
+
491
+
492
+ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
493
+ return context.builder.app_charts_apply(
494
+ profile=args.profile,
495
+ app_key=args.app_key,
496
+ upsert_charts=load_list_arg(args.upsert_file, option_name="--upsert-file"),
497
+ remove_chart_ids=load_list_arg(args.remove_chart_ids_file, option_name="--remove-chart-ids-file"),
498
+ reorder_chart_ids=load_list_arg(args.reorder_chart_ids_file, option_name="--reorder-chart-ids-file"),
499
+ )
500
+
501
+
502
+ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
503
+ has_dash_key = bool((args.dash_key or "").strip())
504
+ has_dash_name = bool((args.dash_name or "").strip())
505
+ has_package_id = args.package_id is not None
506
+ if has_dash_key and has_package_id:
507
+ raise_config_error(
508
+ "portal apply accepts exactly one selector mode.",
509
+ fix_hint="Use `--dash-key` to update an existing portal, or use `--package-id --dash-name` to create a new portal.",
510
+ )
511
+ if not has_dash_key and not (has_package_id and has_dash_name):
512
+ raise_config_error(
513
+ "portal apply requires either --dash-key, or --package-id together with --dash-name.",
514
+ fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-id --dash-name`.",
515
+ )
516
+ sections = [] if not args.sections_file else require_list_arg(args.sections_file, option_name="--sections-file")
517
+ return context.builder.portal_apply(
518
+ profile=args.profile,
519
+ dash_key=args.dash_key,
520
+ dash_name=args.dash_name,
521
+ package_id=args.package_id,
522
+ publish=bool(args.publish),
523
+ sections=sections,
524
+ visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
525
+ auth=load_object_arg(args.auth_file, option_name="--auth-file"),
526
+ icon=args.icon,
527
+ color=args.color,
528
+ hide_copyright=args.hide_copyright,
529
+ dash_global_config=load_object_arg(args.dash_global_config_file, option_name="--dash-global-config-file"),
530
+ config=load_object_arg(args.config_file, option_name="--config-file"),
531
+ )
532
+
533
+
534
+ def _handle_publish_verify(args: argparse.Namespace, context: CliContext) -> dict:
535
+ return context.builder.app_publish_verify(
536
+ profile=args.profile,
537
+ app_key=args.app_key,
538
+ expected_package_id=args.expected_package_id,
539
+ )
@@ -0,0 +1,18 @@
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("chart", help="报表访问")
10
+ chart_subparsers = parser.add_subparsers(dest="chart_command", required=True)
11
+
12
+ get_parser = chart_subparsers.add_parser("get", help="读取报表数据")
13
+ get_parser.add_argument("--chart-id", required=True)
14
+ get_parser.set_defaults(handler=_handle_get, format_hint="generic")
15
+
16
+
17
+ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
18
+ return context.resource.chart_get(profile=args.profile, chart_id=args.chart_id)
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import sys
6
+ from typing import Any
7
+
8
+ from ...errors import QingflowApiError
9
+ from ..json_io import load_json_list, load_json_object, load_optional_json_list, load_optional_json_object
10
+
11
+
12
+ def parse_bool_text(value: str) -> bool:
13
+ normalized = value.strip().lower()
14
+ if normalized in {"true", "1", "yes", "y", "on"}:
15
+ return True
16
+ if normalized in {"false", "0", "no", "n", "off"}:
17
+ return False
18
+ raise argparse.ArgumentTypeError("expected one of: true, false, 1, 0, yes, no")
19
+
20
+
21
+ def load_list_arg(path: str | None, *, option_name: str) -> list[Any]:
22
+ return load_optional_json_list(path, option_name=option_name)
23
+
24
+
25
+ def load_object_arg(path: str | None, *, option_name: str) -> dict[str, Any] | None:
26
+ return load_optional_json_object(path, option_name=option_name)
27
+
28
+
29
+ def require_list_arg(path: str | None, *, option_name: str) -> list[Any]:
30
+ if not path:
31
+ raise QingflowApiError.config_error(f"{option_name} is required")
32
+ return load_json_list(path, option_name=option_name)
33
+
34
+
35
+ def require_object_arg(path: str | None, *, option_name: str) -> dict[str, Any]:
36
+ if not path:
37
+ raise QingflowApiError.config_error(f"{option_name} is required")
38
+ return load_json_object(path, option_name=option_name)
39
+
40
+
41
+ def read_secret_arg(value: str | None, *, stdin_enabled: bool, label: str) -> str:
42
+ if stdin_enabled:
43
+ secret = sys.stdin.read().strip()
44
+ if secret:
45
+ return secret
46
+ if value:
47
+ return value
48
+ raise QingflowApiError.config_error(f"{label} is required")
49
+
50
+
51
+ def raise_config_error(message: str, *, fix_hint: str) -> None:
52
+ raise RuntimeError(
53
+ json.dumps(
54
+ {
55
+ "category": "config",
56
+ "message": message,
57
+ "error_code": "CONFIG_ERROR",
58
+ "details": {"fix_hint": fix_hint},
59
+ },
60
+ ensure_ascii=False,
61
+ )
62
+ )