@josephyan/qingflow-cli 0.2.0-beta.69 → 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.
@@ -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", required=True)
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("--app-key", required=True)
17
- schema.add_argument("--mode", choices=["applicant", "browse", "insert", "update", "import", "code-block"], default="applicant")
18
- schema.add_argument("--view-id")
19
- schema.add_argument("--record-id", type=int)
20
- schema.set_defaults(handler=_handle_schema, format_hint="")
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 _handle_schema(args: argparse.Namespace, context: CliContext) -> dict:
94
- mode = args.mode
95
- if mode == "applicant":
96
- return context.record.record_schema_get(
97
- profile=args.profile,
98
- app_key=args.app_key,
99
- schema_mode="applicant",
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 mode == "browse":
102
- if not args.view_id:
103
- raise QingflowApiError.config_error("--view-id is required when --mode browse")
104
- return context.record.record_browse_schema_get_public(
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 mode == "insert":
110
- return context.record.record_insert_schema_get_public(profile=args.profile, app_key=args.app_key)
111
- if mode == "update":
112
- if not args.record_id:
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: