@josephyan/qingflow-cli 0.2.0-beta.68 → 0.2.0-beta.70
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/backend_client.py +0 -1
- package/src/qingflow_mcp/builder_facade/models.py +34 -0
- package/src/qingflow_mcp/builder_facade/service.py +337 -17
- package/src/qingflow_mcp/cli/commands/builder.py +248 -1
- package/src/qingflow_mcp/cli/commands/common.py +15 -0
- package/src/qingflow_mcp/cli/commands/imports.py +12 -2
- package/src/qingflow_mcp/cli/commands/record.py +132 -32
- package/src/qingflow_mcp/cli/commands/workspace.py +1 -1
- package/src/qingflow_mcp/cli/formatters.py +52 -2
- package/src/qingflow_mcp/cli/main.py +7 -5
- package/src/qingflow_mcp/response_trim.py +668 -0
- package/src/qingflow_mcp/server_app_builder.py +136 -8
- package/src/qingflow_mcp/server_app_user.py +55 -11
- package/src/qingflow_mcp/tools/ai_builder_tools.py +270 -5
- package/src/qingflow_mcp/tools/app_tools.py +29 -0
- package/src/qingflow_mcp/tools/auth_tools.py +259 -1
- package/src/qingflow_mcp/tools/import_tools.py +59 -7
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +23 -6
- package/src/qingflow_mcp/tools/record_tools.py +6 -12
- package/src/qingflow_mcp/tools/workspace_tools.py +124 -7
|
@@ -3,13 +3,42 @@ from __future__ import annotations
|
|
|
3
3
|
import argparse
|
|
4
4
|
|
|
5
5
|
from ..context import CliContext
|
|
6
|
-
from .common import load_list_arg, load_object_arg, require_list_arg
|
|
6
|
+
from .common import load_list_arg, load_object_arg, raise_config_error, require_list_arg
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
10
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
|
+
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
|
+
|
|
13
42
|
package = builder_subparsers.add_parser("package", help="应用包")
|
|
14
43
|
package_subparsers = package.add_subparsers(dest="builder_package_command", required=True)
|
|
15
44
|
package_list = package_subparsers.add_parser("list", help="列出应用包")
|
|
@@ -26,6 +55,12 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
26
55
|
package_create.add_argument("--color")
|
|
27
56
|
package_create.set_defaults(handler=_handle_package_create, format_hint="builder_summary")
|
|
28
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
|
+
|
|
29
64
|
app = builder_subparsers.add_parser("app", help="应用")
|
|
30
65
|
app_subparsers = app.add_subparsers(dest="builder_app_command", required=True)
|
|
31
66
|
|
|
@@ -35,6 +70,12 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
35
70
|
app_resolve.add_argument("--package-tag-id", type=int)
|
|
36
71
|
app_resolve.set_defaults(handler=_handle_app_resolve, format_hint="builder_summary")
|
|
37
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
|
+
|
|
38
79
|
button = builder_subparsers.add_parser("button", help="自定义按钮")
|
|
39
80
|
button_subparsers = button.add_subparsers(dest="builder_button_command", required=True)
|
|
40
81
|
|
|
@@ -77,6 +118,14 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
77
118
|
|
|
78
119
|
portal = builder_subparsers.add_parser("portal", help="门户")
|
|
79
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
|
+
|
|
80
129
|
portal_read = portal_subparsers.add_parser("read-summary", help="读取门户摘要")
|
|
81
130
|
portal_read.add_argument("--dash-key", required=True)
|
|
82
131
|
portal_read.add_argument("--being-draft", action=argparse.BooleanOptionalAction, default=True)
|
|
@@ -112,6 +161,34 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
112
161
|
schema_apply_apply.add_argument("--remove-fields-file")
|
|
113
162
|
schema_apply_apply.set_defaults(handler=_handle_schema_apply, format_hint="builder_summary")
|
|
114
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
|
+
|
|
115
192
|
charts_apply = builder_subparsers.add_parser("charts", help="报表")
|
|
116
193
|
charts_apply_subparsers = charts_apply.add_subparsers(dest="builder_charts_command", required=True)
|
|
117
194
|
charts_apply_apply = charts_apply_subparsers.add_parser("apply", help="执行报表变更")
|
|
@@ -128,11 +205,62 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
128
205
|
publish_verify_verify.add_argument("--expected-package-tag-id", type=int)
|
|
129
206
|
publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary")
|
|
130
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
|
+
|
|
131
225
|
|
|
132
226
|
def _handle_package_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
133
227
|
return context.builder.package_list(profile=args.profile, trial_status=args.trial_status)
|
|
134
228
|
|
|
135
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
|
+
|
|
136
264
|
def _handle_package_resolve(args: argparse.Namespace, context: CliContext) -> dict:
|
|
137
265
|
return context.builder.package_resolve(profile=args.profile, package_name=args.package_name)
|
|
138
266
|
|
|
@@ -146,7 +274,33 @@ def _handle_package_create(args: argparse.Namespace, context: CliContext) -> dic
|
|
|
146
274
|
)
|
|
147
275
|
|
|
148
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
|
+
|
|
149
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
|
+
)
|
|
150
304
|
return context.builder.app_resolve(
|
|
151
305
|
profile=args.profile,
|
|
152
306
|
app_key=args.app_key,
|
|
@@ -155,6 +309,15 @@ def _handle_app_resolve(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
155
309
|
)
|
|
156
310
|
|
|
157
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
|
+
|
|
158
321
|
def _handle_button_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
159
322
|
return context.builder.app_custom_button_list(profile=args.profile, app_key=args.app_key)
|
|
160
323
|
|
|
@@ -208,6 +371,14 @@ def _handle_app_read_charts(args: argparse.Namespace, context: CliContext) -> di
|
|
|
208
371
|
return context.builder.app_read_charts_summary(profile=args.profile, app_key=args.app_key)
|
|
209
372
|
|
|
210
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
|
+
|
|
211
382
|
def _handle_portal_read_summary(args: argparse.Namespace, context: CliContext) -> dict:
|
|
212
383
|
return context.builder.portal_read_summary(
|
|
213
384
|
profile=args.profile,
|
|
@@ -216,7 +387,39 @@ def _handle_portal_read_summary(args: argparse.Namespace, context: CliContext) -
|
|
|
216
387
|
)
|
|
217
388
|
|
|
218
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
|
+
|
|
219
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
|
+
)
|
|
220
423
|
return context.builder.app_schema_apply(
|
|
221
424
|
profile=args.profile,
|
|
222
425
|
app_key=args.app_key,
|
|
@@ -233,6 +436,37 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
233
436
|
)
|
|
234
437
|
|
|
235
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
|
+
|
|
236
470
|
def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
237
471
|
return context.builder.app_charts_apply(
|
|
238
472
|
profile=args.profile,
|
|
@@ -244,6 +478,19 @@ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
244
478
|
|
|
245
479
|
|
|
246
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
|
+
)
|
|
247
494
|
return context.builder.portal_apply(
|
|
248
495
|
profile=args.profile,
|
|
249
496
|
dash_key=args.dash_key,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
+
import json
|
|
4
5
|
import sys
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
@@ -45,3 +46,17 @@ def read_secret_arg(value: str | None, *, stdin_enabled: bool, label: str) -> st
|
|
|
45
46
|
if value:
|
|
46
47
|
return value
|
|
47
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
|
+
)
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import argparse
|
|
4
4
|
|
|
5
5
|
from ..context import CliContext
|
|
6
|
-
from .common import parse_bool_text
|
|
6
|
+
from .common import parse_bool_text, raise_config_error
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
@@ -35,7 +35,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
35
35
|
start.set_defaults(handler=_handle_start, format_hint="")
|
|
36
36
|
|
|
37
37
|
status = import_subparsers.add_parser("status", help="查询导入状态")
|
|
38
|
-
status.add_argument("--app-key"
|
|
38
|
+
status.add_argument("--app-key")
|
|
39
39
|
status.add_argument("--import-id")
|
|
40
40
|
status.add_argument("--process-id-str")
|
|
41
41
|
status.set_defaults(handler=_handle_status, format_hint="import_status")
|
|
@@ -78,6 +78,16 @@ def _handle_start(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
def _handle_status(args: argparse.Namespace, context: CliContext) -> dict:
|
|
81
|
+
selectors = [
|
|
82
|
+
bool((args.app_key or "").strip()),
|
|
83
|
+
bool((args.import_id or "").strip()),
|
|
84
|
+
bool((args.process_id_str or "").strip()),
|
|
85
|
+
]
|
|
86
|
+
if sum(selectors) != 1:
|
|
87
|
+
raise_config_error(
|
|
88
|
+
"import status accepts exactly one selector: --process-id-str, --import-id, or --app-key.",
|
|
89
|
+
fix_hint="Use `--process-id-str` or `--import-id` for a known import, or use only `--app-key` to read the latest import in that app.",
|
|
90
|
+
)
|
|
81
91
|
return context.imports.record_import_status_get(
|
|
82
92
|
profile=args.profile,
|
|
83
93
|
app_key=args.app_key,
|
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
from ...errors import QingflowApiError
|
|
7
7
|
from ..context import CliContext
|
|
8
|
-
from .common import load_list_arg, load_object_arg, require_list_arg, require_object_arg
|
|
8
|
+
from .common import load_list_arg, load_object_arg, raise_config_error, require_list_arg, require_object_arg
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
@@ -13,11 +13,35 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
13
13
|
record_subparsers = parser.add_subparsers(dest="record_command", required=True)
|
|
14
14
|
|
|
15
15
|
schema = record_subparsers.add_parser("schema", help="读取记录相关表结构")
|
|
16
|
-
schema.add_argument("--
|
|
17
|
-
schema.
|
|
18
|
-
schema.
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
schema.add_argument("--mode", dest="legacy_mode", help=argparse.SUPPRESS)
|
|
17
|
+
schema_subparsers = schema.add_subparsers(dest="record_schema_command")
|
|
18
|
+
schema.set_defaults(handler=_handle_schema_root, format_hint="")
|
|
19
|
+
|
|
20
|
+
schema_applicant = schema_subparsers.add_parser("applicant", help="读取申请节点表结构")
|
|
21
|
+
schema_applicant.add_argument("--app-key", required=True)
|
|
22
|
+
schema_applicant.set_defaults(handler=_handle_schema_applicant, format_hint="")
|
|
23
|
+
|
|
24
|
+
schema_browse = schema_subparsers.add_parser("browse", help="读取浏览视图表结构")
|
|
25
|
+
schema_browse.add_argument("--app-key", required=True)
|
|
26
|
+
schema_browse.add_argument("--view-id", required=True)
|
|
27
|
+
schema_browse.set_defaults(handler=_handle_schema_browse, format_hint="")
|
|
28
|
+
|
|
29
|
+
schema_insert = schema_subparsers.add_parser("insert", help="读取新增记录表结构")
|
|
30
|
+
schema_insert.add_argument("--app-key", required=True)
|
|
31
|
+
schema_insert.set_defaults(handler=_handle_schema_insert, format_hint="")
|
|
32
|
+
|
|
33
|
+
schema_update = schema_subparsers.add_parser("update", help="读取更新记录表结构")
|
|
34
|
+
schema_update.add_argument("--app-key", required=True)
|
|
35
|
+
schema_update.add_argument("--record-id", required=True, type=int)
|
|
36
|
+
schema_update.set_defaults(handler=_handle_schema_update, format_hint="")
|
|
37
|
+
|
|
38
|
+
schema_import = schema_subparsers.add_parser("import", help="读取导入表结构")
|
|
39
|
+
schema_import.add_argument("--app-key", required=True)
|
|
40
|
+
schema_import.set_defaults(handler=_handle_schema_import, format_hint="")
|
|
41
|
+
|
|
42
|
+
schema_code_block = schema_subparsers.add_parser("code-block", help="读取代码块执行表结构")
|
|
43
|
+
schema_code_block.add_argument("--app-key", required=True)
|
|
44
|
+
schema_code_block.set_defaults(handler=_handle_schema_code_block, format_hint="")
|
|
21
45
|
|
|
22
46
|
list_parser = record_subparsers.add_parser("list", help="列出记录")
|
|
23
47
|
list_parser.add_argument("--app-key", required=True)
|
|
@@ -28,6 +52,9 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
28
52
|
list_parser.add_argument("--limit", type=int, default=20)
|
|
29
53
|
list_parser.add_argument("--page", type=int, default=1)
|
|
30
54
|
list_parser.add_argument("--view-id")
|
|
55
|
+
list_parser.add_argument("--list-type", dest="legacy_list_type", type=int, help=argparse.SUPPRESS)
|
|
56
|
+
list_parser.add_argument("--view-key", dest="legacy_view_key", help=argparse.SUPPRESS)
|
|
57
|
+
list_parser.add_argument("--view-name", dest="legacy_view_name", help=argparse.SUPPRESS)
|
|
31
58
|
list_parser.set_defaults(handler=_handle_list, format_hint="record_list")
|
|
32
59
|
|
|
33
60
|
get = record_subparsers.add_parser("get", help="读取单条记录")
|
|
@@ -66,6 +93,9 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
66
93
|
analyze.add_argument("--limit", type=int, default=20)
|
|
67
94
|
analyze.add_argument("--strict-full", action=argparse.BooleanOptionalAction, default=False)
|
|
68
95
|
analyze.add_argument("--view-id")
|
|
96
|
+
analyze.add_argument("--list-type", dest="legacy_list_type", type=int, help=argparse.SUPPRESS)
|
|
97
|
+
analyze.add_argument("--view-key", dest="legacy_view_key", help=argparse.SUPPRESS)
|
|
98
|
+
analyze.add_argument("--view-name", dest="legacy_view_name", help=argparse.SUPPRESS)
|
|
69
99
|
analyze.set_defaults(handler=_handle_analyze, format_hint="")
|
|
70
100
|
|
|
71
101
|
code_block = record_subparsers.add_parser("code-block-run", help="执行代码块字段")
|
|
@@ -90,38 +120,96 @@ def _columns(args: argparse.Namespace) -> list[Any]:
|
|
|
90
120
|
return columns
|
|
91
121
|
|
|
92
122
|
|
|
93
|
-
def
|
|
94
|
-
mode = args.
|
|
95
|
-
if mode
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
123
|
+
def _handle_schema_root(args: argparse.Namespace, _context: CliContext) -> dict:
|
|
124
|
+
mode = (args.legacy_mode or "").strip()
|
|
125
|
+
if mode:
|
|
126
|
+
replacement = {
|
|
127
|
+
"applicant": "record schema applicant --app-key APP_KEY",
|
|
128
|
+
"browse": "record schema browse --app-key APP_KEY --view-id VIEW_ID",
|
|
129
|
+
"insert": "record schema insert --app-key APP_KEY",
|
|
130
|
+
"update": "record schema update --app-key APP_KEY --record-id RECORD_ID",
|
|
131
|
+
"import": "record schema import --app-key APP_KEY",
|
|
132
|
+
"code-block": "record schema code-block --app-key APP_KEY",
|
|
133
|
+
}.get(mode, "record schema <applicant|browse|insert|update|import|code-block> ...")
|
|
134
|
+
raise_config_error(
|
|
135
|
+
"record schema --mode is no longer accepted.",
|
|
136
|
+
fix_hint=f"Use `{replacement}` instead.",
|
|
137
|
+
)
|
|
138
|
+
raise_config_error(
|
|
139
|
+
"record schema requires an explicit subcommand.",
|
|
140
|
+
fix_hint="Use one of: `record schema applicant`, `record schema browse`, `record schema insert`, `record schema update`, `record schema import`, or `record schema code-block`.",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _handle_schema_applicant(args: argparse.Namespace, context: CliContext) -> dict:
|
|
145
|
+
return context.record.record_schema_get(
|
|
146
|
+
profile=args.profile,
|
|
147
|
+
app_key=args.app_key,
|
|
148
|
+
schema_mode="applicant",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _handle_schema_browse(args: argparse.Namespace, context: CliContext) -> dict:
|
|
153
|
+
return context.record.record_browse_schema_get_public(
|
|
154
|
+
profile=args.profile,
|
|
155
|
+
app_key=args.app_key,
|
|
156
|
+
view_id=args.view_id,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _handle_schema_insert(args: argparse.Namespace, context: CliContext) -> dict:
|
|
161
|
+
return context.record.record_insert_schema_get_public(profile=args.profile, app_key=args.app_key)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _handle_schema_update(args: argparse.Namespace, context: CliContext) -> dict:
|
|
165
|
+
return context.record.record_update_schema_get_public(
|
|
166
|
+
profile=args.profile,
|
|
167
|
+
app_key=args.app_key,
|
|
168
|
+
record_id=args.record_id,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _handle_schema_import(args: argparse.Namespace, context: CliContext) -> dict:
|
|
173
|
+
return context.imports.record_import_schema_get(profile=args.profile, app_key=args.app_key)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _handle_schema_code_block(args: argparse.Namespace, context: CliContext) -> dict:
|
|
177
|
+
return context.code_block.record_code_block_schema_get_public(profile=args.profile, app_key=args.app_key)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _validate_public_view_selector(
|
|
181
|
+
*,
|
|
182
|
+
view_id: str | None,
|
|
183
|
+
legacy_list_type: int | None,
|
|
184
|
+
legacy_view_key: str | None,
|
|
185
|
+
legacy_view_name: str | None,
|
|
186
|
+
tool_name: str,
|
|
187
|
+
) -> None:
|
|
188
|
+
if legacy_list_type is not None:
|
|
189
|
+
raise_config_error(
|
|
190
|
+
f"{tool_name} no longer accepts list_type.",
|
|
191
|
+
fix_hint="Call `app_get` first and use `accessible_views[].view_id`.",
|
|
100
192
|
)
|
|
101
|
-
if
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
profile=args.profile,
|
|
106
|
-
app_key=args.app_key,
|
|
107
|
-
view_id=args.view_id,
|
|
193
|
+
if legacy_view_key or legacy_view_name:
|
|
194
|
+
raise_config_error(
|
|
195
|
+
f"{tool_name} no longer accepts view_key or view_name.",
|
|
196
|
+
fix_hint="Call `app_get` first and pass the exact `view_id` from `accessible_views`.",
|
|
108
197
|
)
|
|
109
|
-
if
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
raise QingflowApiError.config_error("--record-id is required when --mode update")
|
|
114
|
-
return context.record.record_update_schema_get_public(
|
|
115
|
-
profile=args.profile,
|
|
116
|
-
app_key=args.app_key,
|
|
117
|
-
record_id=args.record_id,
|
|
198
|
+
if not (view_id or "").strip():
|
|
199
|
+
raise_config_error(
|
|
200
|
+
f"{tool_name} requires view_id.",
|
|
201
|
+
fix_hint="Call `app_get` first and choose one `accessible_views[].view_id`, then retry with `--view-id`.",
|
|
118
202
|
)
|
|
119
|
-
if mode == "import":
|
|
120
|
-
return context.imports.record_import_schema_get(profile=args.profile, app_key=args.app_key)
|
|
121
|
-
return context.code_block.record_code_block_schema_get_public(profile=args.profile, app_key=args.app_key)
|
|
122
203
|
|
|
123
204
|
|
|
124
205
|
def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
206
|
+
_validate_public_view_selector(
|
|
207
|
+
view_id=args.view_id,
|
|
208
|
+
legacy_list_type=args.legacy_list_type,
|
|
209
|
+
legacy_view_key=args.legacy_view_key,
|
|
210
|
+
legacy_view_name=args.legacy_view_name,
|
|
211
|
+
tool_name="record_list",
|
|
212
|
+
)
|
|
125
213
|
return context.record.record_list(
|
|
126
214
|
profile=args.profile,
|
|
127
215
|
app_key=args.app_key,
|
|
@@ -174,6 +262,13 @@ def _handle_delete(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
174
262
|
|
|
175
263
|
|
|
176
264
|
def _handle_analyze(args: argparse.Namespace, context: CliContext) -> dict:
|
|
265
|
+
_validate_public_view_selector(
|
|
266
|
+
view_id=args.view_id,
|
|
267
|
+
legacy_list_type=args.legacy_list_type,
|
|
268
|
+
legacy_view_key=args.legacy_view_key,
|
|
269
|
+
legacy_view_name=args.legacy_view_name,
|
|
270
|
+
tool_name="record_analyze",
|
|
271
|
+
)
|
|
177
272
|
return context.record.record_analyze(
|
|
178
273
|
profile=args.profile,
|
|
179
274
|
app_key=args.app_key,
|
|
@@ -188,6 +283,11 @@ def _handle_analyze(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
188
283
|
|
|
189
284
|
|
|
190
285
|
def _handle_code_block_run(args: argparse.Namespace, context: CliContext) -> dict:
|
|
286
|
+
if args.workflow_node_id is not None and args.role != 3:
|
|
287
|
+
raise_config_error(
|
|
288
|
+
"workflow_node_id is only accepted when role=3.",
|
|
289
|
+
fix_hint="Remove `--workflow-node-id`, or set `--role 3` when running in workflow context.",
|
|
290
|
+
)
|
|
191
291
|
return context.code_block.record_code_block_run(
|
|
192
292
|
profile=args.profile,
|
|
193
293
|
app_key=args.app_key,
|
|
@@ -17,7 +17,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
17
17
|
|
|
18
18
|
select = workspace_subparsers.add_parser("select", help="切换工作区")
|
|
19
19
|
select.add_argument("--ws-id", type=int, required=True)
|
|
20
|
-
select.set_defaults(handler=_handle_select, format_hint="")
|
|
20
|
+
select.set_defaults(handler=_handle_select, format_hint="workspace_select")
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|