@josephyan/qingflow-cli 0.2.0-beta.57 → 0.2.0-beta.59
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 +3 -2
- package/docs/local-agent-install.md +9 -0
- package/npm/bin/qingflow.mjs +1 -1
- package/npm/lib/runtime.mjs +156 -21
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/builder_facade/service.py +137 -5
- package/src/qingflow_mcp/cli/commands/app.py +16 -16
- package/src/qingflow_mcp/cli/commands/auth.py +19 -16
- package/src/qingflow_mcp/cli/commands/builder.py +124 -162
- package/src/qingflow_mcp/cli/commands/common.py +21 -95
- package/src/qingflow_mcp/cli/commands/imports.py +42 -34
- package/src/qingflow_mcp/cli/commands/record.py +131 -133
- package/src/qingflow_mcp/cli/commands/task.py +43 -44
- package/src/qingflow_mcp/cli/commands/workspace.py +10 -10
- package/src/qingflow_mcp/cli/context.py +35 -32
- package/src/qingflow_mcp/cli/formatters.py +124 -121
- package/src/qingflow_mcp/cli/main.py +52 -17
- package/src/qingflow_mcp/server_app_builder.py +122 -190
- package/src/qingflow_mcp/server_app_user.py +63 -662
- package/src/qingflow_mcp/tools/solution_tools.py +95 -3
- package/src/qingflow_mcp/ops/__init__.py +0 -3
- package/src/qingflow_mcp/ops/apps.py +0 -64
- package/src/qingflow_mcp/ops/auth.py +0 -121
- package/src/qingflow_mcp/ops/base.py +0 -290
- package/src/qingflow_mcp/ops/builder.py +0 -323
- package/src/qingflow_mcp/ops/context.py +0 -120
- package/src/qingflow_mcp/ops/directory.py +0 -171
- package/src/qingflow_mcp/ops/feedback.py +0 -49
- package/src/qingflow_mcp/ops/files.py +0 -78
- package/src/qingflow_mcp/ops/imports.py +0 -140
- package/src/qingflow_mcp/ops/records.py +0 -415
- package/src/qingflow_mcp/ops/tasks.py +0 -171
- package/src/qingflow_mcp/ops/workspace.py +0 -76
|
@@ -1,222 +1,184 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
-
from typing import Any
|
|
5
4
|
|
|
6
|
-
from ...errors import QingflowApiError
|
|
7
5
|
from ..context import CliContext
|
|
8
|
-
from .common import
|
|
6
|
+
from .common import load_list_arg, load_object_arg, require_list_arg
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
12
|
-
parser = subparsers.add_parser("
|
|
13
|
-
|
|
10
|
+
parser = subparsers.add_parser("builder", help="稳定 builder 命令")
|
|
11
|
+
builder_subparsers = parser.add_subparsers(dest="builder_command", required=True)
|
|
14
12
|
|
|
15
|
-
package =
|
|
16
|
-
package_subparsers = package.add_subparsers(dest="
|
|
13
|
+
package = builder_subparsers.add_parser("package", help="应用包")
|
|
14
|
+
package_subparsers = package.add_subparsers(dest="builder_package_command", required=True)
|
|
17
15
|
package_resolve = package_subparsers.add_parser("resolve", help="解析应用包")
|
|
18
|
-
package_resolve.add_argument("--name", required=True)
|
|
19
|
-
package_resolve.set_defaults(handler=_handle_package_resolve, format_hint="
|
|
16
|
+
package_resolve.add_argument("--package-name", required=True)
|
|
17
|
+
package_resolve.set_defaults(handler=_handle_package_resolve, format_hint="builder_summary")
|
|
18
|
+
|
|
19
|
+
app = builder_subparsers.add_parser("app", help="应用")
|
|
20
|
+
app_subparsers = app.add_subparsers(dest="builder_app_command", required=True)
|
|
20
21
|
|
|
21
|
-
app = build_subparsers.add_parser("app", help="应用")
|
|
22
|
-
app_subparsers = app.add_subparsers(dest="build_app_command", required=True)
|
|
23
22
|
app_resolve = app_subparsers.add_parser("resolve", help="解析应用")
|
|
24
23
|
app_resolve.add_argument("--app-key", default="")
|
|
25
|
-
app_resolve.add_argument("--name", default="")
|
|
24
|
+
app_resolve.add_argument("--app-name", default="")
|
|
26
25
|
app_resolve.add_argument("--package-tag-id", type=int)
|
|
27
|
-
app_resolve.set_defaults(handler=_handle_app_resolve, format_hint="
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
layout_show = layout_subparsers.add_parser("show", help="读取布局摘要")
|
|
52
|
-
layout_show.add_argument("--app", required=True)
|
|
53
|
-
layout_show.set_defaults(handler=_handle_layout_show, format_hint="builder_result")
|
|
54
|
-
|
|
55
|
-
views = build_subparsers.add_parser("views", help="视图")
|
|
56
|
-
views_subparsers = views.add_subparsers(dest="build_views_command", required=True)
|
|
57
|
-
views_show = views_subparsers.add_parser("show", help="读取视图摘要")
|
|
58
|
-
views_show.add_argument("--app", required=True)
|
|
59
|
-
views_show.set_defaults(handler=_handle_views_show, format_hint="builder_result")
|
|
60
|
-
|
|
61
|
-
flow = build_subparsers.add_parser("flow", help="流程")
|
|
62
|
-
flow_subparsers = flow.add_subparsers(dest="build_flow_command", required=True)
|
|
63
|
-
flow_show = flow_subparsers.add_parser("show", help="读取流程摘要")
|
|
64
|
-
flow_show.add_argument("--app", required=True)
|
|
65
|
-
flow_show.set_defaults(handler=_handle_flow_show, format_hint="builder_result")
|
|
66
|
-
|
|
67
|
-
charts = build_subparsers.add_parser("charts", help="报表")
|
|
68
|
-
charts_subparsers = charts.add_subparsers(dest="build_charts_command", required=True)
|
|
69
|
-
charts_show = charts_subparsers.add_parser("show", help="读取报表摘要")
|
|
70
|
-
charts_show.add_argument("--app", required=True)
|
|
71
|
-
charts_show.set_defaults(handler=_handle_charts_show, format_hint="builder_result")
|
|
72
|
-
charts_apply = charts_subparsers.add_parser("apply", help="执行报表变更")
|
|
73
|
-
charts_apply.add_argument("--app", required=True)
|
|
74
|
-
add_file_arg(charts_apply, required=False, help_text="读取报表变更 JSON,支持 upsert_charts/remove_chart_ids/reorder_chart_ids")
|
|
75
|
-
add_stdin_json_flag(charts_apply)
|
|
76
|
-
charts_apply.set_defaults(handler=_handle_charts_apply, format_hint="builder_result")
|
|
77
|
-
|
|
78
|
-
portal = build_subparsers.add_parser("portal", help="门户")
|
|
79
|
-
portal_subparsers = portal.add_subparsers(dest="build_portal_command", required=True)
|
|
80
|
-
portal_show = portal_subparsers.add_parser("show", help="读取门户摘要")
|
|
81
|
-
portal_show.add_argument("--dash", required=True)
|
|
82
|
-
portal_show.add_argument("--draft", action=argparse.BooleanOptionalAction, default=True)
|
|
83
|
-
portal_show.set_defaults(handler=_handle_portal_show, format_hint="builder_result")
|
|
84
|
-
portal_apply = portal_subparsers.add_parser("apply", help="执行门户变更")
|
|
85
|
-
portal_apply.add_argument("--dash", default="")
|
|
86
|
-
portal_apply.add_argument("--name", dest="dash_name", default="")
|
|
26
|
+
app_resolve.set_defaults(handler=_handle_app_resolve, format_hint="builder_summary")
|
|
27
|
+
|
|
28
|
+
for name, help_text, handler in [
|
|
29
|
+
("read-summary", "读取应用摘要", _handle_app_read_summary),
|
|
30
|
+
("read-fields", "读取字段摘要", _handle_app_read_fields),
|
|
31
|
+
("read-layout", "读取布局摘要", _handle_app_read_layout),
|
|
32
|
+
("read-views", "读取视图摘要", _handle_app_read_views),
|
|
33
|
+
("read-flow", "读取流程摘要", _handle_app_read_flow),
|
|
34
|
+
("read-charts", "读取报表摘要", _handle_app_read_charts),
|
|
35
|
+
]:
|
|
36
|
+
sub = app_subparsers.add_parser(name, help=help_text)
|
|
37
|
+
sub.add_argument("--app-key", required=True)
|
|
38
|
+
sub.set_defaults(handler=handler, format_hint="builder_summary")
|
|
39
|
+
|
|
40
|
+
portal = builder_subparsers.add_parser("portal", help="门户")
|
|
41
|
+
portal_subparsers = portal.add_subparsers(dest="builder_portal_command", required=True)
|
|
42
|
+
portal_read = portal_subparsers.add_parser("read-summary", help="读取门户摘要")
|
|
43
|
+
portal_read.add_argument("--dash-key", required=True)
|
|
44
|
+
portal_read.add_argument("--being-draft", action=argparse.BooleanOptionalAction, default=True)
|
|
45
|
+
portal_read.set_defaults(handler=_handle_portal_read_summary, format_hint="builder_summary")
|
|
46
|
+
|
|
47
|
+
portal_apply = portal_subparsers.add_parser("apply", help="更新门户")
|
|
48
|
+
portal_apply.add_argument("--dash-key", default="")
|
|
49
|
+
portal_apply.add_argument("--dash-name", default="")
|
|
87
50
|
portal_apply.add_argument("--package-tag-id", type=int)
|
|
88
51
|
portal_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
portal_apply.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
52
|
+
portal_apply.add_argument("--sections-file", required=True)
|
|
53
|
+
portal_apply.add_argument("--auth-file")
|
|
54
|
+
portal_apply.add_argument("--icon")
|
|
55
|
+
portal_apply.add_argument("--color")
|
|
56
|
+
portal_apply.add_argument("--hide-copyright", action=argparse.BooleanOptionalAction, default=None)
|
|
57
|
+
portal_apply.add_argument("--dash-global-config-file")
|
|
58
|
+
portal_apply.add_argument("--config-file")
|
|
59
|
+
portal_apply.set_defaults(handler=_handle_portal_apply, format_hint="builder_summary")
|
|
60
|
+
|
|
61
|
+
schema_apply = builder_subparsers.add_parser("schema", help="字段搭建")
|
|
62
|
+
schema_apply_subparsers = schema_apply.add_subparsers(dest="builder_schema_command", required=True)
|
|
63
|
+
schema_apply_apply = schema_apply_subparsers.add_parser("apply", help="执行字段变更")
|
|
64
|
+
schema_apply_apply.add_argument("--app-key", default="")
|
|
65
|
+
schema_apply_apply.add_argument("--package-tag-id", type=int)
|
|
66
|
+
schema_apply_apply.add_argument("--app-name", default="")
|
|
67
|
+
schema_apply_apply.add_argument("--app-title", default="")
|
|
68
|
+
schema_apply_apply.add_argument("--create-if-missing", action="store_true")
|
|
69
|
+
schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
70
|
+
schema_apply_apply.add_argument("--add-fields-file")
|
|
71
|
+
schema_apply_apply.add_argument("--update-fields-file")
|
|
72
|
+
schema_apply_apply.add_argument("--remove-fields-file")
|
|
73
|
+
schema_apply_apply.set_defaults(handler=_handle_schema_apply, format_hint="builder_summary")
|
|
74
|
+
|
|
75
|
+
charts_apply = builder_subparsers.add_parser("charts", help="报表")
|
|
76
|
+
charts_apply_subparsers = charts_apply.add_subparsers(dest="builder_charts_command", required=True)
|
|
77
|
+
charts_apply_apply = charts_apply_subparsers.add_parser("apply", help="执行报表变更")
|
|
78
|
+
charts_apply_apply.add_argument("--app-key", required=True)
|
|
79
|
+
charts_apply_apply.add_argument("--upsert-file")
|
|
80
|
+
charts_apply_apply.add_argument("--remove-chart-ids-file")
|
|
81
|
+
charts_apply_apply.add_argument("--reorder-chart-ids-file")
|
|
82
|
+
charts_apply_apply.set_defaults(handler=_handle_charts_apply, format_hint="builder_summary")
|
|
83
|
+
|
|
84
|
+
publish_verify = builder_subparsers.add_parser("publish", help="发布校验")
|
|
85
|
+
publish_verify_subparsers = publish_verify.add_subparsers(dest="builder_publish_command", required=True)
|
|
86
|
+
publish_verify_verify = publish_verify_subparsers.add_parser("verify", help="校验应用发布")
|
|
87
|
+
publish_verify_verify.add_argument("--app-key", required=True)
|
|
88
|
+
publish_verify_verify.add_argument("--expected-package-tag-id", type=int)
|
|
89
|
+
publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary")
|
|
99
90
|
|
|
100
91
|
|
|
101
92
|
def _handle_package_resolve(args: argparse.Namespace, context: CliContext) -> dict:
|
|
102
|
-
return context.
|
|
93
|
+
return context.builder.package_resolve(profile=args.profile, package_name=args.package_name)
|
|
103
94
|
|
|
104
95
|
|
|
105
96
|
def _handle_app_resolve(args: argparse.Namespace, context: CliContext) -> dict:
|
|
106
|
-
return context.
|
|
97
|
+
return context.builder.app_resolve(
|
|
107
98
|
profile=args.profile,
|
|
108
99
|
app_key=args.app_key,
|
|
109
|
-
app_name=args.
|
|
100
|
+
app_name=args.app_name,
|
|
110
101
|
package_tag_id=args.package_tag_id,
|
|
111
102
|
)
|
|
112
103
|
|
|
113
104
|
|
|
114
|
-
def
|
|
115
|
-
return context.
|
|
105
|
+
def _handle_app_read_summary(args: argparse.Namespace, context: CliContext) -> dict:
|
|
106
|
+
return context.builder.app_read_summary(profile=args.profile, app_key=args.app_key)
|
|
116
107
|
|
|
117
108
|
|
|
118
|
-
def
|
|
119
|
-
return context.
|
|
109
|
+
def _handle_app_read_fields(args: argparse.Namespace, context: CliContext) -> dict:
|
|
110
|
+
return context.builder.app_read_fields(profile=args.profile, app_key=args.app_key)
|
|
120
111
|
|
|
121
112
|
|
|
122
|
-
def
|
|
123
|
-
|
|
124
|
-
return context.build.fields_apply(
|
|
125
|
-
profile=args.profile,
|
|
126
|
-
app_key=args.app,
|
|
127
|
-
package_tag_id=args.package_tag_id,
|
|
128
|
-
app_name=args.app_name,
|
|
129
|
-
app_title=args.app_title,
|
|
130
|
-
create_if_missing=bool(args.create_if_missing),
|
|
131
|
-
publish=bool(args.publish),
|
|
132
|
-
add_fields=[_normalize_field_spec(item) for item in _coerce_list(payload.get("add_fields"))],
|
|
133
|
-
update_fields=_coerce_list(payload.get("update_fields")),
|
|
134
|
-
remove_fields=[_normalize_field_selector(item) for item in _coerce_list(payload.get("remove_fields"))],
|
|
135
|
-
)
|
|
113
|
+
def _handle_app_read_layout(args: argparse.Namespace, context: CliContext) -> dict:
|
|
114
|
+
return context.builder.app_read_layout_summary(profile=args.profile, app_key=args.app_key)
|
|
136
115
|
|
|
137
116
|
|
|
138
|
-
def
|
|
139
|
-
return context.
|
|
117
|
+
def _handle_app_read_views(args: argparse.Namespace, context: CliContext) -> dict:
|
|
118
|
+
return context.builder.app_read_views_summary(profile=args.profile, app_key=args.app_key)
|
|
140
119
|
|
|
141
120
|
|
|
142
|
-
def
|
|
143
|
-
return context.
|
|
121
|
+
def _handle_app_read_flow(args: argparse.Namespace, context: CliContext) -> dict:
|
|
122
|
+
return context.builder.app_read_flow_summary(profile=args.profile, app_key=args.app_key)
|
|
144
123
|
|
|
145
124
|
|
|
146
|
-
def
|
|
147
|
-
return context.
|
|
125
|
+
def _handle_app_read_charts(args: argparse.Namespace, context: CliContext) -> dict:
|
|
126
|
+
return context.builder.app_read_charts_summary(profile=args.profile, app_key=args.app_key)
|
|
148
127
|
|
|
149
128
|
|
|
150
|
-
def
|
|
151
|
-
return context.
|
|
129
|
+
def _handle_portal_read_summary(args: argparse.Namespace, context: CliContext) -> dict:
|
|
130
|
+
return context.builder.portal_read_summary(
|
|
131
|
+
profile=args.profile,
|
|
132
|
+
dash_key=args.dash_key,
|
|
133
|
+
being_draft=bool(args.being_draft),
|
|
134
|
+
)
|
|
152
135
|
|
|
153
136
|
|
|
154
|
-
def
|
|
155
|
-
|
|
156
|
-
return context.build.charts_apply(
|
|
137
|
+
def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
138
|
+
return context.builder.app_schema_apply(
|
|
157
139
|
profile=args.profile,
|
|
158
|
-
app_key=args.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
140
|
+
app_key=args.app_key,
|
|
141
|
+
package_tag_id=args.package_tag_id,
|
|
142
|
+
app_name=args.app_name,
|
|
143
|
+
app_title=args.app_title,
|
|
144
|
+
create_if_missing=bool(args.create_if_missing),
|
|
145
|
+
publish=bool(args.publish),
|
|
146
|
+
add_fields=load_list_arg(args.add_fields_file, option_name="--add-fields-file"),
|
|
147
|
+
update_fields=load_list_arg(args.update_fields_file, option_name="--update-fields-file"),
|
|
148
|
+
remove_fields=load_list_arg(args.remove_fields_file, option_name="--remove-fields-file"),
|
|
162
149
|
)
|
|
163
150
|
|
|
164
151
|
|
|
165
|
-
def
|
|
166
|
-
return context.
|
|
152
|
+
def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
153
|
+
return context.builder.app_charts_apply(
|
|
154
|
+
profile=args.profile,
|
|
155
|
+
app_key=args.app_key,
|
|
156
|
+
upsert_charts=load_list_arg(args.upsert_file, option_name="--upsert-file"),
|
|
157
|
+
remove_chart_ids=load_list_arg(args.remove_chart_ids_file, option_name="--remove-chart-ids-file"),
|
|
158
|
+
reorder_chart_ids=load_list_arg(args.reorder_chart_ids_file, option_name="--reorder-chart-ids-file"),
|
|
159
|
+
)
|
|
167
160
|
|
|
168
161
|
|
|
169
162
|
def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
170
|
-
|
|
171
|
-
sections = payload.get("sections")
|
|
172
|
-
if not isinstance(sections, list) or not sections:
|
|
173
|
-
raise QingflowApiError.config_error("sections must be a non-empty list")
|
|
174
|
-
return context.build.portal_apply(
|
|
163
|
+
return context.builder.portal_apply(
|
|
175
164
|
profile=args.profile,
|
|
176
|
-
dash_key=args.
|
|
165
|
+
dash_key=args.dash_key,
|
|
177
166
|
dash_name=args.dash_name,
|
|
178
167
|
package_tag_id=args.package_tag_id,
|
|
179
168
|
publish=bool(args.publish),
|
|
180
|
-
sections=sections,
|
|
181
|
-
auth=
|
|
182
|
-
icon=
|
|
183
|
-
color=
|
|
184
|
-
hide_copyright=
|
|
185
|
-
dash_global_config=
|
|
186
|
-
config=
|
|
169
|
+
sections=require_list_arg(args.sections_file, option_name="--sections-file"),
|
|
170
|
+
auth=load_object_arg(args.auth_file, option_name="--auth-file"),
|
|
171
|
+
icon=args.icon,
|
|
172
|
+
color=args.color,
|
|
173
|
+
hide_copyright=args.hide_copyright,
|
|
174
|
+
dash_global_config=load_object_arg(args.dash_global_config_file, option_name="--dash-global-config-file"),
|
|
175
|
+
config=load_object_arg(args.config_file, option_name="--config-file"),
|
|
187
176
|
)
|
|
188
177
|
|
|
189
178
|
|
|
190
179
|
def _handle_publish_verify(args: argparse.Namespace, context: CliContext) -> dict:
|
|
191
|
-
return context.
|
|
180
|
+
return context.builder.app_publish_verify(
|
|
192
181
|
profile=args.profile,
|
|
193
|
-
app_key=args.
|
|
182
|
+
app_key=args.app_key,
|
|
194
183
|
expected_package_tag_id=args.expected_package_tag_id,
|
|
195
184
|
)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def _coerce_list(value: Any) -> list[Any]:
|
|
199
|
-
return value if isinstance(value, list) else []
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def _normalize_field_spec(value: Any) -> Any:
|
|
203
|
-
if not isinstance(value, dict):
|
|
204
|
-
return value
|
|
205
|
-
normalized = dict(value)
|
|
206
|
-
if "name" not in normalized and isinstance(normalized.get("field_name"), str):
|
|
207
|
-
normalized["name"] = normalized["field_name"]
|
|
208
|
-
if "type" not in normalized and isinstance(normalized.get("field_type"), str):
|
|
209
|
-
normalized["type"] = normalized["field_type"]
|
|
210
|
-
normalized.pop("field_name", None)
|
|
211
|
-
normalized.pop("field_type", None)
|
|
212
|
-
return normalized
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def _normalize_field_selector(value: Any) -> Any:
|
|
216
|
-
if not isinstance(value, dict):
|
|
217
|
-
return value
|
|
218
|
-
normalized = dict(value)
|
|
219
|
-
if "name" not in normalized and isinstance(normalized.get("field_name"), str):
|
|
220
|
-
normalized["name"] = normalized["field_name"]
|
|
221
|
-
normalized.pop("field_name", None)
|
|
222
|
-
return normalized
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
-
import json
|
|
5
4
|
import sys
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
8
7
|
from ...errors import QingflowApiError
|
|
9
|
-
from ..json_io import load_json_list, load_json_object
|
|
8
|
+
from ..json_io import load_json_list, load_json_object, load_optional_json_list, load_optional_json_object
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def parse_bool_text(value: str) -> bool:
|
|
@@ -18,104 +17,31 @@ def parse_bool_text(value: str) -> bool:
|
|
|
18
17
|
raise argparse.ArgumentTypeError("expected one of: true, false, 1, 0, yes, no")
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
def
|
|
22
|
-
|
|
23
|
-
secret = sys.stdin.read().strip()
|
|
24
|
-
if secret:
|
|
25
|
-
return secret
|
|
26
|
-
if value:
|
|
27
|
-
return value
|
|
28
|
-
raise QingflowApiError.config_error(f"{label} is required")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def add_stdin_json_flag(parser: argparse.ArgumentParser) -> None:
|
|
32
|
-
parser.add_argument("--stdin-json", action="store_true", help="从标准输入读取 JSON")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def add_file_arg(parser: argparse.ArgumentParser, *, required: bool = False, help_text: str = "从文件读取 JSON") -> None:
|
|
36
|
-
parser.add_argument("--file", required=required, help=help_text)
|
|
37
|
-
|
|
20
|
+
def load_list_arg(path: str | None, *, option_name: str) -> list[Any]:
|
|
21
|
+
return load_optional_json_list(path, option_name=option_name)
|
|
38
22
|
|
|
39
|
-
def add_field_args(parser: argparse.ArgumentParser) -> None:
|
|
40
|
-
parser.add_argument("--field", dest="fields", action="append", default=[], help="字段赋值,格式:字段名=值")
|
|
41
23
|
|
|
24
|
+
def load_object_arg(path: str | None, *, option_name: str) -> dict[str, Any] | None:
|
|
25
|
+
return load_optional_json_object(path, option_name=option_name)
|
|
42
26
|
|
|
43
|
-
def load_object_input(args: argparse.Namespace, *, required: bool = False) -> dict[str, Any]:
|
|
44
|
-
if getattr(args, "stdin_json", False):
|
|
45
|
-
payload = _read_stdin_json()
|
|
46
|
-
if not isinstance(payload, dict):
|
|
47
|
-
raise QingflowApiError.config_error("stdin JSON must be an object")
|
|
48
|
-
return payload
|
|
49
|
-
file_path = getattr(args, "file", None)
|
|
50
|
-
if file_path:
|
|
51
|
-
return load_json_object(file_path, option_name="--file")
|
|
52
|
-
if required:
|
|
53
|
-
raise QingflowApiError.config_error("either --stdin-json or --file is required")
|
|
54
|
-
return {}
|
|
55
27
|
|
|
28
|
+
def require_list_arg(path: str | None, *, option_name: str) -> list[Any]:
|
|
29
|
+
if not path:
|
|
30
|
+
raise QingflowApiError.config_error(f"{option_name} is required")
|
|
31
|
+
return load_json_list(path, option_name=option_name)
|
|
56
32
|
|
|
57
|
-
def load_list_input(args: argparse.Namespace, *, required: bool = False) -> list[Any]:
|
|
58
|
-
if getattr(args, "stdin_json", False):
|
|
59
|
-
payload = _read_stdin_json()
|
|
60
|
-
if not isinstance(payload, list):
|
|
61
|
-
raise QingflowApiError.config_error("stdin JSON must be a list")
|
|
62
|
-
return payload
|
|
63
|
-
file_path = getattr(args, "file", None)
|
|
64
|
-
if file_path:
|
|
65
|
-
return load_json_list(file_path, option_name="--file")
|
|
66
|
-
if required:
|
|
67
|
-
raise QingflowApiError.config_error("either --stdin-json or --file is required")
|
|
68
|
-
return []
|
|
69
33
|
|
|
34
|
+
def require_object_arg(path: str | None, *, option_name: str) -> dict[str, Any]:
|
|
35
|
+
if not path:
|
|
36
|
+
raise QingflowApiError.config_error(f"{option_name} is required")
|
|
37
|
+
return load_json_object(path, option_name=option_name)
|
|
70
38
|
|
|
71
|
-
def parse_field_assignments(items: list[str]) -> dict[str, Any]:
|
|
72
|
-
payload: dict[str, Any] = {}
|
|
73
|
-
for item in items:
|
|
74
|
-
if "=" not in item:
|
|
75
|
-
raise QingflowApiError.config_error("field assignments must use name=value format")
|
|
76
|
-
key, raw_value = item.split("=", 1)
|
|
77
|
-
name = key.strip()
|
|
78
|
-
if not name:
|
|
79
|
-
raise QingflowApiError.config_error("field name cannot be empty")
|
|
80
|
-
payload[name] = _coerce_value(raw_value)
|
|
81
|
-
return payload
|
|
82
39
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return payload
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def _read_stdin_json() -> Any:
|
|
95
|
-
raw = sys.stdin.read()
|
|
96
|
-
if not raw.strip():
|
|
97
|
-
raise QingflowApiError.config_error("stdin JSON is empty")
|
|
98
|
-
try:
|
|
99
|
-
return json.loads(raw)
|
|
100
|
-
except json.JSONDecodeError as error:
|
|
101
|
-
raise QingflowApiError.config_error(f"stdin JSON is invalid: {error.msg}") from error
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _coerce_value(raw: str) -> Any:
|
|
105
|
-
text = raw.strip()
|
|
106
|
-
if not text:
|
|
107
|
-
return ""
|
|
108
|
-
if text.startswith("{") or text.startswith("[") or text.startswith('"'):
|
|
109
|
-
try:
|
|
110
|
-
return json.loads(text)
|
|
111
|
-
except json.JSONDecodeError:
|
|
112
|
-
return raw
|
|
113
|
-
normalized = text.lower()
|
|
114
|
-
if normalized in {"true", "false", "null"}:
|
|
115
|
-
return json.loads(normalized)
|
|
116
|
-
try:
|
|
117
|
-
if "." in text:
|
|
118
|
-
return float(text)
|
|
119
|
-
return int(text)
|
|
120
|
-
except ValueError:
|
|
121
|
-
return raw
|
|
40
|
+
def read_secret_arg(value: str | None, *, stdin_enabled: bool, label: str) -> str:
|
|
41
|
+
if stdin_enabled:
|
|
42
|
+
secret = sys.stdin.read().strip()
|
|
43
|
+
if secret:
|
|
44
|
+
return secret
|
|
45
|
+
if value:
|
|
46
|
+
return value
|
|
47
|
+
raise QingflowApiError.config_error(f"{label} is required")
|
|
@@ -10,69 +10,77 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
10
10
|
parser = subparsers.add_parser("import", help="导入")
|
|
11
11
|
import_subparsers = parser.add_subparsers(dest="import_command", required=True)
|
|
12
12
|
|
|
13
|
-
template = import_subparsers.add_parser("template", help="
|
|
14
|
-
template.add_argument("--app", required=True)
|
|
15
|
-
template.add_argument("--to")
|
|
16
|
-
template.set_defaults(handler=_handle_template, format_hint="
|
|
13
|
+
template = import_subparsers.add_parser("template", help="下载导入模板")
|
|
14
|
+
template.add_argument("--app-key", required=True)
|
|
15
|
+
template.add_argument("--download-to-path")
|
|
16
|
+
template.set_defaults(handler=_handle_template, format_hint="")
|
|
17
17
|
|
|
18
18
|
verify = import_subparsers.add_parser("verify", help="校验导入文件")
|
|
19
|
-
verify.add_argument("--app", required=True)
|
|
20
|
-
verify.add_argument("--file", required=True)
|
|
19
|
+
verify.add_argument("--app-key", required=True)
|
|
20
|
+
verify.add_argument("--file-path", required=True)
|
|
21
21
|
verify.set_defaults(handler=_handle_verify, format_hint="import_verify")
|
|
22
22
|
|
|
23
|
-
repair = import_subparsers.add_parser("repair", help="
|
|
24
|
-
repair.add_argument("--verification", required=True)
|
|
25
|
-
repair.add_argument("--
|
|
26
|
-
repair.add_argument("--
|
|
23
|
+
repair = import_subparsers.add_parser("repair", help="授权后修复导入文件")
|
|
24
|
+
repair.add_argument("--verification-id", required=True)
|
|
25
|
+
repair.add_argument("--authorized-file-modification", action="store_true")
|
|
26
|
+
repair.add_argument("--output-path")
|
|
27
27
|
repair.add_argument("--repair", dest="selected_repairs", action="append", default=[])
|
|
28
|
-
repair.set_defaults(handler=_handle_repair, format_hint="
|
|
28
|
+
repair.set_defaults(handler=_handle_repair, format_hint="")
|
|
29
29
|
|
|
30
30
|
start = import_subparsers.add_parser("start", help="启动导入")
|
|
31
|
-
start.add_argument("--app", required=True)
|
|
32
|
-
start.add_argument("--verification", required=True)
|
|
33
|
-
start.add_argument("--enter-auditing", type=parse_bool_text, required=True)
|
|
34
|
-
start.add_argument("--view")
|
|
35
|
-
start.set_defaults(handler=_handle_start, format_hint="
|
|
36
|
-
|
|
37
|
-
status = import_subparsers.add_parser("status", help="
|
|
38
|
-
status.add_argument("--app", required=True)
|
|
31
|
+
start.add_argument("--app-key", required=True)
|
|
32
|
+
start.add_argument("--verification-id", required=True)
|
|
33
|
+
start.add_argument("--being-enter-auditing", type=parse_bool_text, required=True)
|
|
34
|
+
start.add_argument("--view-key")
|
|
35
|
+
start.set_defaults(handler=_handle_start, format_hint="")
|
|
36
|
+
|
|
37
|
+
status = import_subparsers.add_parser("status", help="查询导入状态")
|
|
38
|
+
status.add_argument("--app-key", required=True)
|
|
39
39
|
status.add_argument("--import-id")
|
|
40
|
-
status.add_argument("--process-id")
|
|
40
|
+
status.add_argument("--process-id-str")
|
|
41
41
|
status.set_defaults(handler=_handle_status, format_hint="import_status")
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def _handle_template(args: argparse.Namespace, context: CliContext) -> dict:
|
|
45
|
-
return context.imports.
|
|
45
|
+
return context.imports.record_import_template_get(
|
|
46
|
+
profile=args.profile,
|
|
47
|
+
app_key=args.app_key,
|
|
48
|
+
download_to_path=args.download_to_path,
|
|
49
|
+
)
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
def _handle_verify(args: argparse.Namespace, context: CliContext) -> dict:
|
|
49
|
-
return context.imports.
|
|
53
|
+
return context.imports.record_import_verify(
|
|
54
|
+
profile=args.profile,
|
|
55
|
+
app_key=args.app_key,
|
|
56
|
+
file_path=args.file_path,
|
|
57
|
+
)
|
|
50
58
|
|
|
51
59
|
|
|
52
60
|
def _handle_repair(args: argparse.Namespace, context: CliContext) -> dict:
|
|
53
|
-
return context.imports.
|
|
61
|
+
return context.imports.record_import_repair_local(
|
|
54
62
|
profile=args.profile,
|
|
55
|
-
verification_id=args.
|
|
56
|
-
authorized_file_modification=bool(args.
|
|
57
|
-
output_path=args.
|
|
63
|
+
verification_id=args.verification_id,
|
|
64
|
+
authorized_file_modification=bool(args.authorized_file_modification),
|
|
65
|
+
output_path=args.output_path,
|
|
58
66
|
selected_repairs=list(args.selected_repairs or []),
|
|
59
67
|
)
|
|
60
68
|
|
|
61
69
|
|
|
62
70
|
def _handle_start(args: argparse.Namespace, context: CliContext) -> dict:
|
|
63
|
-
return context.imports.
|
|
71
|
+
return context.imports.record_import_start(
|
|
64
72
|
profile=args.profile,
|
|
65
|
-
app_key=args.
|
|
66
|
-
verification_id=args.
|
|
67
|
-
being_enter_auditing=bool(args.
|
|
68
|
-
view_key=args.
|
|
73
|
+
app_key=args.app_key,
|
|
74
|
+
verification_id=args.verification_id,
|
|
75
|
+
being_enter_auditing=bool(args.being_enter_auditing),
|
|
76
|
+
view_key=args.view_key,
|
|
69
77
|
)
|
|
70
78
|
|
|
71
79
|
|
|
72
80
|
def _handle_status(args: argparse.Namespace, context: CliContext) -> dict:
|
|
73
|
-
return context.imports.
|
|
81
|
+
return context.imports.record_import_status_get(
|
|
74
82
|
profile=args.profile,
|
|
75
|
-
app_key=args.
|
|
83
|
+
app_key=args.app_key,
|
|
76
84
|
import_id=args.import_id,
|
|
77
|
-
process_id_str=args.
|
|
85
|
+
process_id_str=args.process_id_str,
|
|
78
86
|
)
|