@qingflow-tech/qingflow-cli 1.1.2 → 1.1.34
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/docs/local-agent-install.md +10 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +61 -47
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +35 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +21 -477
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +44 -290
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +11 -102
- package/skills/qingflow-cli/reference/builder/10-build-single-app.md +57 -0
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +84 -0
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +317 -0
- package/skills/qingflow-cli/reference/builder/40-layout.md +70 -0
- package/skills/qingflow-cli/reference/builder/50-views.md +540 -0
- package/skills/qingflow-cli/reference/builder/60-charts.md +328 -0
- package/skills/qingflow-cli/reference/builder/70-portal.md +372 -0
- package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +148 -0
- package/skills/qingflow-cli/reference/builder/90-workflow.md +35 -0
- package/skills/qingflow-cli/reference/builder/99-publish-verify.md +64 -0
- package/skills/qingflow-cli/reference/builder/README.md +62 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/README.md +130 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/code-block.md +66 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/q-linker.md +77 -0
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +508 -0
- package/skills/qingflow-cli/reference/builder/reference/match-rules.md +229 -0
- package/skills/qingflow-cli/reference/builder/reference/workspace-icons.md +89 -0
- package/skills/qingflow-cli/reference/builder/workflow/01-overview.md +48 -0
- package/skills/qingflow-cli/reference/builder/workflow/02-update-mode.md +63 -0
- package/skills/qingflow-cli/reference/builder/workflow/03-flow-patterns.md +59 -0
- package/skills/qingflow-cli/reference/builder/workflow/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-cli/reference/builder/workflow/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-cli/reference/builder/workflow/06-stage3-build-spec.md +211 -0
- package/skills/qingflow-cli/reference/builder/workflow/07-stage4-validate-spec.md +36 -0
- package/skills/qingflow-cli/reference/builder/workflow/08-stage5-apply-verify.md +66 -0
- package/skills/qingflow-cli/reference/builder/workflow/09-stage6-summary.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/10-node-config-reference.md +93 -0
- package/skills/qingflow-cli/reference/builder/workflow/11-troubleshooting.md +18 -0
- package/skills/qingflow-cli/reference/builder/workflow/README.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/workflow-schema.json +1754 -0
- package/skills/qingflow-cli/reference/charts_upsert_dashboard_starter.example.json +81 -0
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +94 -0
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +139 -0
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_EXPLORATION_REPORT.md +84 -0
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_FIELD_DATA_TYPES.md +129 -0
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +195 -0
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +159 -0
- package/skills/qingflow-cli/reference/examples/README.md +17 -0
- package/skills/qingflow-cli/reference/examples/associated/associated_resources_semantic_full.example.json +50 -0
- package/skills/qingflow-cli/reference/examples/buttons/custom_buttons_advanced.example.json +56 -0
- package/skills/qingflow-cli/reference/examples/charts/charts_remove.example.json +1 -0
- package/skills/qingflow-cli/reference/examples/charts/charts_reorder.example.json +1 -0
- package/skills/qingflow-cli/reference/examples/charts/charts_upsert_bar.example.json +8 -0
- package/skills/qingflow-cli/reference/examples/charts/charts_upsert_dashboard_starter.example.json +118 -0
- package/skills/qingflow-cli/reference/examples/charts/charts_upsert_minimal.example.json +13 -0
- package/skills/qingflow-cli/reference/examples/layout/layout_sections_full.example.json +22 -0
- package/skills/qingflow-cli/reference/examples/portal/portal_sections_all_types.example.json +145 -0
- package/skills/qingflow-cli/reference/examples/portal/portal_sections_five_types.example.json +126 -0
- package/skills/qingflow-cli/reference/examples/portal/portal_sections_standard_workbench.example.json +178 -0
- package/skills/qingflow-cli/reference/examples/record/record_insert_all_field_types.example.json +35 -0
- package/skills/qingflow-cli/reference/examples/schema/_batch_schema_complex.json +18 -0
- package/skills/qingflow-cli/reference/examples/schema/_batch_schema_scalar.json +17 -0
- package/skills/qingflow-cli/reference/examples/schema/schema_add_fields_minimal.example.json +7 -0
- package/skills/qingflow-cli/reference/examples/schema/schema_apply_add_fields_all_types.json +78 -0
- package/skills/qingflow-cli/reference/examples/schema/schema_multi_app_form_relation_layout.example.json +166 -0
- package/skills/qingflow-cli/reference/examples/schema/schema_single_app_form_standard.example.json +95 -0
- package/skills/qingflow-cli/reference/examples/views/views_batch_full.example.json +159 -0
- package/skills/qingflow-cli/reference/examples/views/views_upsert_table_minimal.example.json +42 -0
- package/skills/qingflow-cli/reference/portal_sections_all_types.example.json +18 -4
- package/skills/qingflow-cli/reference/portal_sections_five_types.example.json +3 -3
- package/skills/qingflow-cli/reference/portal_sections_standard_workbench.example.json +57 -7
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +22 -0
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_DELETE_WORKFLOW.md +31 -0
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md +176 -0
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +167 -0
- package/skills/qingflow-cli/reference/record/analysis/README.md +130 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +91 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +112 -0
- package/skills/qingflow-cli/reference/record/analysis/business-context.md +74 -0
- package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +69 -0
- package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +106 -0
- package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +172 -0
- package/skills/qingflow-cli/reference/record/analysis/report-format.md +76 -0
- package/skills/qingflow-cli/reference/record/insert/README.md +125 -0
- package/skills/qingflow-cli/reference/schema_apply_add_fields_all_types.json +1 -1
- package/skills/qingflow-cli/reference/task/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +153 -0
- package/skills/qingflow-cli/reference/task/ops/README.md +134 -0
- package/skills/qingflow-cli/reference/task/ops/environments.md +43 -0
- package/skills/qingflow-cli/reference/task/ops/workflow-usage.md +29 -0
- package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +42 -7
- package/skills/qingflow-cli/scripts/validate_system_build_summary.py +124 -0
- package/skills/qingflow-cli/scripts/workflow/diff_flow_spec.py +275 -0
- package/skills/qingflow-cli/scripts/workflow/validate_flow_spec.py +627 -0
- package/src/qingflow_mcp/builder_facade/models.py +413 -119
- package/src/qingflow_mcp/builder_facade/service.py +4238 -666
- package/src/qingflow_mcp/cli/commands/builder.py +232 -75
- package/src/qingflow_mcp/cli/main.py +34 -0
- package/src/qingflow_mcp/public_surface.py +10 -0
- package/src/qingflow_mcp/server_app_builder.py +62 -34
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
- package/src/qingflow_mcp/solution/executor.py +2 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2649 -457
- package/src/qingflow_mcp/tools/record_tools.py +23 -11
|
@@ -117,10 +117,12 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
117
117
|
app_get.add_argument(
|
|
118
118
|
"builder_app_get_section",
|
|
119
119
|
nargs="?",
|
|
120
|
-
choices=["summary", "fields", "layout", "views", "flow", "charts"],
|
|
120
|
+
choices=["summary", "fields", "layout", "views", "flow", "charts", "buttons", "associated-resources"],
|
|
121
121
|
default="summary",
|
|
122
122
|
)
|
|
123
|
-
app_get.add_argument("--app-key",
|
|
123
|
+
app_get.add_argument("--app-key", default="")
|
|
124
|
+
app_get.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量读取同一 section")
|
|
125
|
+
app_get.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量读取同一 section")
|
|
124
126
|
app_get.set_defaults(handler=_handle_app_get, format_hint="builder_summary")
|
|
125
127
|
|
|
126
128
|
app_repair_code_blocks = app_subparsers.add_parser("repair-code-blocks", help="扫描或修复现有代码块配置")
|
|
@@ -135,8 +137,15 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
135
137
|
button_catalog = button_subparsers.add_parser("catalog", help="读取按钮样式目录")
|
|
136
138
|
button_catalog.set_defaults(handler=_handle_button_catalog, format_hint="builder_summary")
|
|
137
139
|
|
|
140
|
+
button_get = button_subparsers.add_parser("get", help="读取应用自定义按钮配置")
|
|
141
|
+
button_get.add_argument("--app-key", default="")
|
|
142
|
+
button_get.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量读取按钮配置")
|
|
143
|
+
button_get.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量读取按钮配置")
|
|
144
|
+
button_get.set_defaults(handler=_handle_button_get, format_hint="builder_summary")
|
|
145
|
+
|
|
138
146
|
button_apply = button_subparsers.add_parser("apply", help="声明式创建、更新或删除自定义按钮")
|
|
139
|
-
button_apply.add_argument("--app-key",
|
|
147
|
+
button_apply.add_argument("--app-key", default="")
|
|
148
|
+
button_apply.add_argument("--apps-file", help="多应用按钮配置 JSON 数组;每项包含 app_key 和按钮 payload")
|
|
140
149
|
button_apply.add_argument("--upsert-buttons-file")
|
|
141
150
|
button_apply.add_argument("--patch-buttons-file")
|
|
142
151
|
button_apply.add_argument("--remove-buttons-file")
|
|
@@ -145,8 +154,15 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
145
154
|
|
|
146
155
|
associated_resource = builder_subparsers.add_parser("associated-resource", aliases=["associated-resources"], help="关联视图/报表")
|
|
147
156
|
associated_resource_subparsers = associated_resource.add_subparsers(dest="builder_associated_resource_command", required=True)
|
|
157
|
+
associated_resource_get = associated_resource_subparsers.add_parser("get", help="读取应用关联资源配置")
|
|
158
|
+
associated_resource_get.add_argument("--app-key", default="")
|
|
159
|
+
associated_resource_get.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量读取关联资源配置")
|
|
160
|
+
associated_resource_get.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量读取关联资源配置")
|
|
161
|
+
associated_resource_get.set_defaults(handler=_handle_associated_resource_get, format_hint="builder_summary")
|
|
162
|
+
|
|
148
163
|
associated_resource_apply = associated_resource_subparsers.add_parser("apply", help="声明式管理应用关联资源池和视图展示配置")
|
|
149
|
-
associated_resource_apply.add_argument("--app-key",
|
|
164
|
+
associated_resource_apply.add_argument("--app-key", default="")
|
|
165
|
+
associated_resource_apply.add_argument("--apps-file", help="多应用关联资源配置 JSON 数组;每项包含 app_key 和资源 payload")
|
|
150
166
|
associated_resource_apply.add_argument("--upsert-resources-file")
|
|
151
167
|
associated_resource_apply.add_argument("--patch-resources-file")
|
|
152
168
|
associated_resource_apply.add_argument("--remove-associated-item-ids-file")
|
|
@@ -179,6 +195,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
179
195
|
portal_apply.add_argument("--hide-copyright", action=argparse.BooleanOptionalAction, default=None)
|
|
180
196
|
portal_apply.add_argument("--dash-global-config-file")
|
|
181
197
|
portal_apply.add_argument("--config-file")
|
|
198
|
+
portal_apply.add_argument("--patch-sections-file", help="门户组件局部更新 JSON 数组,每项含 chart_ref/view_ref/order + set/unset")
|
|
182
199
|
portal_apply.set_defaults(handler=_handle_portal_apply, format_hint="builder_summary", force_json_output=True)
|
|
183
200
|
|
|
184
201
|
portal_delete = portal_subparsers.add_parser("delete", help="删除门户")
|
|
@@ -195,9 +212,9 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
195
212
|
schema_apply_apply.add_argument("--icon")
|
|
196
213
|
schema_apply_apply.add_argument("--color")
|
|
197
214
|
schema_apply_apply.add_argument("--visibility-file")
|
|
198
|
-
schema_apply_apply.add_argument("--create-if-missing", action="store_true")
|
|
199
215
|
schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=None)
|
|
200
|
-
schema_apply_apply.add_argument("--apps-file", help="多应用 schema JSON
|
|
216
|
+
schema_apply_apply.add_argument("--apps-file", help="多应用 schema JSON 对象;每项可带 client_key/app_name/form,支持 relation target_app_ref")
|
|
217
|
+
schema_apply_apply.add_argument("--form-file", help="单应用表单结构 JSON 数组;form[].rows[][] 同时声明字段和表单分组布局")
|
|
201
218
|
schema_apply_apply.add_argument("--add-fields-file", help="字段 JSON 数组;字段可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
202
219
|
schema_apply_apply.add_argument("--update-fields-file", help="字段更新 JSON 数组;set 内可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
203
220
|
schema_apply_apply.add_argument("--remove-fields-file")
|
|
@@ -206,46 +223,53 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
206
223
|
layout_apply = builder_subparsers.add_parser("layout", help="布局")
|
|
207
224
|
layout_apply_subparsers = layout_apply.add_subparsers(dest="builder_layout_command", required=True)
|
|
208
225
|
layout_apply_apply = layout_apply_subparsers.add_parser("apply", help="执行布局变更")
|
|
209
|
-
layout_apply_apply.add_argument("--app-key",
|
|
226
|
+
layout_apply_apply.add_argument("--app-key", default="")
|
|
227
|
+
layout_apply_apply.add_argument("--apps-file", help="多应用布局 JSON 数组;每项包含 app_key/mode?/publish?/sections")
|
|
210
228
|
layout_apply_apply.add_argument("--mode", choices=["merge", "replace"], default="merge")
|
|
211
229
|
layout_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
212
|
-
layout_apply_apply.add_argument("--sections-file"
|
|
230
|
+
layout_apply_apply.add_argument("--sections-file")
|
|
213
231
|
layout_apply_apply.set_defaults(handler=_handle_layout_apply, format_hint="builder_summary", force_json_output=True)
|
|
214
232
|
|
|
215
233
|
views_apply = builder_subparsers.add_parser("views", help="视图")
|
|
216
234
|
views_apply_subparsers = views_apply.add_subparsers(dest="builder_views_command", required=True)
|
|
217
235
|
views_apply_apply = views_apply_subparsers.add_parser("apply", help="执行视图变更")
|
|
218
|
-
views_apply_apply.add_argument("--app-key",
|
|
236
|
+
views_apply_apply.add_argument("--app-key", default="")
|
|
237
|
+
views_apply_apply.add_argument("--views-file", help='推荐批量视图 JSON 对象:{"views":[...]};每项包含 app_key、operation、视图配置')
|
|
238
|
+
views_apply_apply.add_argument("--apps-file", help="多应用视图 JSON 数组;每项包含 app_key 和 upsert/patch/remove payload")
|
|
219
239
|
views_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
220
240
|
views_apply_apply.add_argument("--upsert-views-file")
|
|
221
241
|
views_apply_apply.add_argument("--patch-views-file")
|
|
222
242
|
views_apply_apply.add_argument("--remove-views-file")
|
|
223
243
|
views_apply_apply.set_defaults(handler=_handle_views_apply, format_hint="builder_summary", force_json_output=True)
|
|
224
244
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
flow_schema = flow_subparsers.add_parser("schema", help="读取 WorkflowSpec JSON Schema")
|
|
245
|
+
flow_apply = builder_subparsers.add_parser("flow", help="流程")
|
|
246
|
+
flow_apply_subparsers = flow_apply.add_subparsers(dest="builder_flow_command", required=True)
|
|
247
|
+
flow_schema = flow_apply_subparsers.add_parser("schema", help="读取 WorkflowSpec JSON Schema")
|
|
229
248
|
flow_schema.add_argument("--schema-version", default="")
|
|
230
249
|
flow_schema.set_defaults(handler=_handle_flow_schema, format_hint="builder_summary")
|
|
231
250
|
|
|
232
|
-
flow_get =
|
|
251
|
+
flow_get = flow_apply_subparsers.add_parser("get", help="读取 WorkflowSpec")
|
|
233
252
|
flow_get.add_argument("--app-key", required=True)
|
|
234
253
|
flow_get.add_argument("--version-id", default="")
|
|
235
254
|
flow_get.set_defaults(handler=_handle_flow_get, format_hint="builder_summary")
|
|
236
255
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
256
|
+
flow_apply_apply = flow_apply_subparsers.add_parser("apply", help="执行流程变更")
|
|
257
|
+
flow_apply_apply.add_argument("--app-key", required=True)
|
|
258
|
+
flow_apply_apply.add_argument("--mode", choices=["replace"], default="replace", help=argparse.SUPPRESS)
|
|
259
|
+
flow_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
260
|
+
flow_apply_apply.add_argument("--nodes-file", help=argparse.SUPPRESS)
|
|
261
|
+
flow_apply_apply.add_argument("--transitions-file", help=argparse.SUPPRESS)
|
|
262
|
+
flow_apply_apply.add_argument("--spec-file")
|
|
263
|
+
flow_apply_apply.add_argument("--patch-nodes-file", help="节点局部更新 JSON 数组,每项含 id + set/unset")
|
|
264
|
+
flow_apply_apply.add_argument("--idempotency-key", default="")
|
|
265
|
+
flow_apply_apply.add_argument("--schema-version", default="")
|
|
266
|
+
flow_apply_apply.set_defaults(handler=_handle_flow_apply, format_hint="builder_summary", force_json_output=True)
|
|
244
267
|
|
|
245
268
|
charts_apply = builder_subparsers.add_parser("charts", help="报表")
|
|
246
269
|
charts_apply_subparsers = charts_apply.add_subparsers(dest="builder_charts_command", required=True)
|
|
247
270
|
charts_apply_apply = charts_apply_subparsers.add_parser("apply", help="执行报表变更")
|
|
248
|
-
charts_apply_apply.add_argument("--app-key",
|
|
271
|
+
charts_apply_apply.add_argument("--app-key", default="")
|
|
272
|
+
charts_apply_apply.add_argument("--apps-file", help="多应用报表 JSON 数组;每项包含 app_key 和 upsert/patch/remove/reorder payload")
|
|
249
273
|
charts_apply_apply.add_argument("--upsert-file")
|
|
250
274
|
charts_apply_apply.add_argument("--patch-file")
|
|
251
275
|
charts_apply_apply.add_argument("--remove-chart-ids-file")
|
|
@@ -255,7 +279,9 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
255
279
|
publish_verify = builder_subparsers.add_parser("publish", help="发布校验")
|
|
256
280
|
publish_verify_subparsers = publish_verify.add_subparsers(dest="builder_publish_command", required=True)
|
|
257
281
|
publish_verify_verify = publish_verify_subparsers.add_parser("verify", help="校验应用发布")
|
|
258
|
-
publish_verify_verify.add_argument("--app-key",
|
|
282
|
+
publish_verify_verify.add_argument("--app-key", default="")
|
|
283
|
+
publish_verify_verify.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量发布校验")
|
|
284
|
+
publish_verify_verify.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量发布校验")
|
|
259
285
|
publish_verify_verify.add_argument("--expected-package-id", type=int)
|
|
260
286
|
publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary", force_json_output=True)
|
|
261
287
|
|
|
@@ -360,6 +386,7 @@ def _handle_package_apply(args: argparse.Namespace, context: CliContext) -> dict
|
|
|
360
386
|
config = load_object_arg(args.config_file, option_name="--config-file")
|
|
361
387
|
if not isinstance(config, dict):
|
|
362
388
|
raise_config_error("package apply --config-file must contain a JSON object.")
|
|
389
|
+
config.pop("create_if_missing", None)
|
|
363
390
|
return context.builder.package_apply(profile=args.profile, **config)
|
|
364
391
|
|
|
365
392
|
|
|
@@ -403,6 +430,9 @@ def _handle_button_catalog(args: argparse.Namespace, context: CliContext) -> dic
|
|
|
403
430
|
|
|
404
431
|
|
|
405
432
|
def _handle_button_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
433
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
434
|
+
if apps is None and not (args.app_key or "").strip():
|
|
435
|
+
raise_config_error("builder button apply requires --app-key unless --apps-file is provided")
|
|
406
436
|
return context.builder.app_custom_buttons_apply(
|
|
407
437
|
profile=args.profile,
|
|
408
438
|
app_key=args.app_key,
|
|
@@ -410,10 +440,14 @@ def _handle_button_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
410
440
|
patch_buttons=load_list_arg(args.patch_buttons_file, option_name="--patch-buttons-file"),
|
|
411
441
|
remove_buttons=load_list_arg(args.remove_buttons_file, option_name="--remove-buttons-file"),
|
|
412
442
|
view_configs=load_list_arg(args.view_configs_file, option_name="--view-configs-file"),
|
|
443
|
+
apps=apps,
|
|
413
444
|
)
|
|
414
445
|
|
|
415
446
|
|
|
416
447
|
def _handle_associated_resource_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
448
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
449
|
+
if apps is None and not (args.app_key or "").strip():
|
|
450
|
+
raise_config_error("builder associated-resource apply requires --app-key unless --apps-file is provided")
|
|
417
451
|
return context.builder.app_associated_resources_apply(
|
|
418
452
|
profile=args.profile,
|
|
419
453
|
app_key=args.app_key,
|
|
@@ -422,11 +456,26 @@ def _handle_associated_resource_apply(args: argparse.Namespace, context: CliCont
|
|
|
422
456
|
remove_associated_item_ids=load_list_arg(args.remove_associated_item_ids_file, option_name="--remove-associated-item-ids-file"),
|
|
423
457
|
reorder_associated_item_ids=load_list_arg(args.reorder_associated_item_ids_file, option_name="--reorder-associated-item-ids-file"),
|
|
424
458
|
view_configs=load_list_arg(args.view_configs_file, option_name="--view-configs-file"),
|
|
459
|
+
apps=apps,
|
|
425
460
|
)
|
|
426
461
|
|
|
427
462
|
|
|
428
463
|
def _handle_button_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
429
|
-
|
|
464
|
+
app_keys = _app_keys_from_args(args)
|
|
465
|
+
if app_keys is not None:
|
|
466
|
+
return context.builder.app_get_buttons(profile=args.profile, app_key="", app_keys=app_keys)
|
|
467
|
+
if not (args.app_key or "").strip():
|
|
468
|
+
raise_config_error("builder button get requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
469
|
+
return context.builder.app_get_buttons(profile=args.profile, app_key=args.app_key)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _handle_associated_resource_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
473
|
+
app_keys = _app_keys_from_args(args)
|
|
474
|
+
if app_keys is not None:
|
|
475
|
+
return context.builder.app_get_associated_resources(profile=args.profile, app_key="", app_keys=app_keys)
|
|
476
|
+
if not (args.app_key or "").strip():
|
|
477
|
+
raise_config_error("builder associated-resource get requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
478
|
+
return context.builder.app_get_associated_resources(profile=args.profile, app_key=args.app_key)
|
|
430
479
|
|
|
431
480
|
|
|
432
481
|
def _handle_button_create(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -450,6 +499,17 @@ def _handle_button_delete(args: argparse.Namespace, context: CliContext) -> dict
|
|
|
450
499
|
return context.builder.app_custom_button_delete(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
|
|
451
500
|
|
|
452
501
|
|
|
502
|
+
def _app_keys_from_args(args: argparse.Namespace) -> list[str] | None:
|
|
503
|
+
keys: list[str] = []
|
|
504
|
+
raw = str(getattr(args, "app_keys", "") or "").strip()
|
|
505
|
+
if raw:
|
|
506
|
+
keys.extend(part.strip() for part in raw.split(",") if part.strip())
|
|
507
|
+
keys_file = getattr(args, "app_keys_file", None)
|
|
508
|
+
if keys_file:
|
|
509
|
+
keys.extend(str(item).strip() for item in require_list_arg(keys_file, option_name="--app-keys-file") if str(item).strip())
|
|
510
|
+
return keys or None
|
|
511
|
+
|
|
512
|
+
|
|
453
513
|
def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
454
514
|
handlers = {
|
|
455
515
|
"summary": context.builder.app_get,
|
|
@@ -458,7 +518,14 @@ def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
458
518
|
"views": context.builder.app_get_views,
|
|
459
519
|
"flow": context.builder.app_get_flow,
|
|
460
520
|
"charts": context.builder.app_get_charts,
|
|
521
|
+
"buttons": context.builder.app_get_buttons,
|
|
522
|
+
"associated-resources": context.builder.app_get_associated_resources,
|
|
461
523
|
}
|
|
524
|
+
app_keys = _app_keys_from_args(args)
|
|
525
|
+
if app_keys is not None:
|
|
526
|
+
return handlers[args.builder_app_get_section](profile=args.profile, app_key="", app_keys=app_keys)
|
|
527
|
+
if not (args.app_key or "").strip():
|
|
528
|
+
raise_config_error("builder app get requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
462
529
|
return handlers[args.builder_app_get_section](profile=args.profile, app_key=args.app_key)
|
|
463
530
|
|
|
464
531
|
|
|
@@ -495,17 +562,11 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
495
562
|
if package_id is None and apps_payload.get("package_id") is not None:
|
|
496
563
|
package_id = _coerce_apps_file_package_id(apps_payload["package_id"])
|
|
497
564
|
if args.apps_file:
|
|
498
|
-
file_create_if_missing = _coerce_apps_file_bool(
|
|
499
|
-
apps_payload.get("create_if_missing"),
|
|
500
|
-
field_name="create_if_missing",
|
|
501
|
-
default=False,
|
|
502
|
-
)
|
|
503
565
|
file_publish = _coerce_apps_file_bool(
|
|
504
566
|
apps_payload.get("publish"),
|
|
505
567
|
field_name="publish",
|
|
506
568
|
default=True,
|
|
507
569
|
)
|
|
508
|
-
effective_create_if_missing = bool(args.create_if_missing or file_create_if_missing)
|
|
509
570
|
effective_publish = bool(args.publish if args.publish is not None else file_publish)
|
|
510
571
|
if not apps:
|
|
511
572
|
raise_config_error(
|
|
@@ -513,10 +574,10 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
513
574
|
fix_hint="Pass a JSON array, or a JSON object like {\"package_id\":1001,\"apps\":[...]} with at least one app item.",
|
|
514
575
|
error_code="APPS_FILE_EMPTY",
|
|
515
576
|
)
|
|
516
|
-
if args.app_key or args.app_name or args.app_title or args.add_fields_file or args.update_fields_file or args.remove_fields_file:
|
|
577
|
+
if args.app_key or args.app_name or args.app_title or args.form_file or args.add_fields_file or args.update_fields_file or args.remove_fields_file:
|
|
517
578
|
raise_config_error(
|
|
518
|
-
"schema apply multi-app mode accepts --package-id
|
|
519
|
-
fix_hint="Use
|
|
579
|
+
"schema apply multi-app mode accepts --package-id plus --apps-file only.",
|
|
580
|
+
fix_hint="Use apps[].form inside --apps-file for batch mode, or remove --apps-file and use --form-file for a single app.",
|
|
520
581
|
)
|
|
521
582
|
if package_id is None:
|
|
522
583
|
raise_config_error(
|
|
@@ -527,9 +588,9 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
527
588
|
profile=args.profile,
|
|
528
589
|
package_id=package_id,
|
|
529
590
|
visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
|
|
530
|
-
create_if_missing=effective_create_if_missing,
|
|
531
591
|
publish=effective_publish,
|
|
532
592
|
apps=apps,
|
|
593
|
+
form=None,
|
|
533
594
|
add_fields=[],
|
|
534
595
|
update_fields=[],
|
|
535
596
|
remove_fields=[],
|
|
@@ -544,16 +605,16 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
544
605
|
has_app_title = bool((args.app_title or "").strip())
|
|
545
606
|
has_package_id = args.package_id is not None
|
|
546
607
|
if has_app_key:
|
|
547
|
-
if
|
|
608
|
+
if has_package_id:
|
|
548
609
|
raise_config_error(
|
|
549
610
|
"schema apply edit mode accepts --app-key and optional --app-name only.",
|
|
550
|
-
fix_hint="For existing apps, use `--app-key` and optionally `--app-name`. For create mode, use `--package-id --app-name
|
|
611
|
+
fix_hint="For existing apps, use `--app-key` and optionally `--app-name`. For create mode, use `--package-id --app-name`.",
|
|
551
612
|
)
|
|
552
613
|
else:
|
|
553
|
-
if not
|
|
614
|
+
if not has_package_id or not (has_app_name or has_app_title):
|
|
554
615
|
raise_config_error(
|
|
555
|
-
"schema apply create mode requires --package-id
|
|
556
|
-
fix_hint="Use `--app-key` for existing apps, or pass `--package-id --app-name
|
|
616
|
+
"schema apply create mode requires --package-id and --app-name.",
|
|
617
|
+
fix_hint="Use `--app-key` for existing apps, or pass `--package-id --app-name` to create a new app.",
|
|
557
618
|
)
|
|
558
619
|
return context.builder.app_schema_apply(
|
|
559
620
|
profile=args.profile,
|
|
@@ -564,8 +625,8 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
564
625
|
icon=args.icon,
|
|
565
626
|
color=args.color,
|
|
566
627
|
visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
|
|
567
|
-
create_if_missing=bool(args.create_if_missing),
|
|
568
628
|
publish=True if args.publish is None else bool(args.publish),
|
|
629
|
+
form=load_list_arg(args.form_file, option_name="--form-file") if args.form_file else [],
|
|
569
630
|
add_fields=load_list_arg(args.add_fields_file, option_name="--add-fields-file"),
|
|
570
631
|
update_fields=load_list_arg(args.update_fields_file, option_name="--update-fields-file"),
|
|
571
632
|
remove_fields=load_list_arg(args.remove_fields_file, option_name="--remove-fields-file"),
|
|
@@ -583,7 +644,12 @@ def _load_apps_file_arg(path: str | None) -> dict[str, object]:
|
|
|
583
644
|
"app_name": "员工花名册",
|
|
584
645
|
"icon": "business-personalcard",
|
|
585
646
|
"color": "emerald",
|
|
586
|
-
"
|
|
647
|
+
"form": [
|
|
648
|
+
{
|
|
649
|
+
"section": "基础信息",
|
|
650
|
+
"rows": [[{"name": "员工名称", "type": "text", "data_title": True}]],
|
|
651
|
+
}
|
|
652
|
+
],
|
|
587
653
|
}
|
|
588
654
|
],
|
|
589
655
|
}
|
|
@@ -591,7 +657,8 @@ def _load_apps_file_arg(path: str | None) -> dict[str, object]:
|
|
|
591
657
|
"expected_shape": expected_shape,
|
|
592
658
|
"expected_shape_json": (
|
|
593
659
|
'{"package_id":1001,"apps":[{"client_key":"employee","app_name":"员工花名册",'
|
|
594
|
-
'"icon":"business-personalcard","color":"emerald","
|
|
660
|
+
'"icon":"business-personalcard","color":"emerald","form":[{"section":"基础信息",'
|
|
661
|
+
'"rows":[[{"name":"员工名称","type":"text","data_title":true}]]}]}]}'
|
|
595
662
|
),
|
|
596
663
|
}
|
|
597
664
|
payload = load_json_value(path, option_name="--apps-file")
|
|
@@ -602,7 +669,7 @@ def _load_apps_file_arg(path: str | None) -> dict[str, object]:
|
|
|
602
669
|
if not isinstance(apps, list):
|
|
603
670
|
raise_config_error(
|
|
604
671
|
"--apps-file wrapper object requires an apps array.",
|
|
605
|
-
fix_hint=
|
|
672
|
+
fix_hint='Use {"package_id":1001,"apps":[...]} with apps[].form, or pass a raw apps array with form sections.',
|
|
606
673
|
error_code="APPS_FILE_SHAPE_INVALID",
|
|
607
674
|
details=expected_shape_details,
|
|
608
675
|
)
|
|
@@ -617,15 +684,13 @@ def _load_apps_file_arg(path: str | None) -> dict[str, object]:
|
|
|
617
684
|
}
|
|
618
685
|
if wrapper.get("package_id") is not None:
|
|
619
686
|
result["package_id"] = wrapper.get("package_id")
|
|
620
|
-
if wrapper.get("create_if_missing") is not None:
|
|
621
|
-
result["create_if_missing"] = wrapper.get("create_if_missing")
|
|
622
687
|
if wrapper.get("publish") is not None:
|
|
623
688
|
result["publish"] = wrapper.get("publish")
|
|
624
689
|
return result
|
|
625
690
|
if any(isinstance(item, dict) and "apps" in item for item in payload):
|
|
626
691
|
raise_config_error(
|
|
627
692
|
"--apps-file root array items must be app items, not package wrapper objects.",
|
|
628
|
-
fix_hint=
|
|
693
|
+
fix_hint='Use one object {"package_id":1001,"apps":[...]} with apps[].form, or a raw app array with form sections. Do not wrap multiple {package_id, apps} objects in an array.',
|
|
629
694
|
error_code="APPS_FILE_SHAPE_INVALID",
|
|
630
695
|
details=expected_shape_details,
|
|
631
696
|
)
|
|
@@ -642,8 +707,6 @@ def _load_apps_file_arg(path: str | None) -> dict[str, object]:
|
|
|
642
707
|
result: dict[str, object] = {"apps": apps}
|
|
643
708
|
if payload.get("package_id") is not None:
|
|
644
709
|
result["package_id"] = payload.get("package_id")
|
|
645
|
-
if payload.get("create_if_missing") is not None:
|
|
646
|
-
result["create_if_missing"] = payload.get("create_if_missing")
|
|
647
710
|
if payload.get("publish") is not None:
|
|
648
711
|
result["publish"] = payload.get("publish")
|
|
649
712
|
return result
|
|
@@ -709,17 +772,74 @@ def _coerce_apps_file_bool(value: object, *, field_name: str, default: bool) ->
|
|
|
709
772
|
)
|
|
710
773
|
|
|
711
774
|
|
|
775
|
+
def _load_views_file_arg(path: str | None) -> list[object] | None:
|
|
776
|
+
if not path:
|
|
777
|
+
return None
|
|
778
|
+
payload = load_json_value(path, option_name="--views-file")
|
|
779
|
+
if isinstance(payload, dict):
|
|
780
|
+
views = payload.get("views")
|
|
781
|
+
if not isinstance(views, list):
|
|
782
|
+
raise_config_error(
|
|
783
|
+
"--views-file JSON object must contain a views array.",
|
|
784
|
+
fix_hint='Use {"views": [{"operation": "upsert", "app_key": "...", "name": "...", "type": "table", "columns": [...]}]}.',
|
|
785
|
+
error_code="VIEWS_FILE_SHAPE_INVALID",
|
|
786
|
+
details={"expected": '{"views": [...]}', "actual_type": type(views).__name__},
|
|
787
|
+
)
|
|
788
|
+
return views
|
|
789
|
+
if isinstance(payload, list):
|
|
790
|
+
return payload
|
|
791
|
+
raise_config_error(
|
|
792
|
+
"--views-file must be a JSON object with views[] or a JSON array.",
|
|
793
|
+
fix_hint='Prefer {"views": [...]} for new automation. A bare array is accepted only for compatibility.',
|
|
794
|
+
error_code="VIEWS_FILE_SHAPE_INVALID",
|
|
795
|
+
details={"actual_type": type(payload).__name__},
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
|
|
712
799
|
def _handle_layout_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
800
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
801
|
+
if apps is None and not (args.app_key or "").strip():
|
|
802
|
+
raise_config_error("builder layout apply requires --app-key unless --apps-file is provided")
|
|
803
|
+
if apps is None and not getattr(args, "sections_file", None):
|
|
804
|
+
raise_config_error(
|
|
805
|
+
"--sections-file is required",
|
|
806
|
+
fix_hint="Pass --sections-file for single-app layout apply, or use --apps-file for batch layout apply.",
|
|
807
|
+
error_code="ARGUMENT_ERROR",
|
|
808
|
+
details={
|
|
809
|
+
"prog": "qingflow builder layout apply",
|
|
810
|
+
"usage": "qingflow builder layout apply --app-key APP_KEY --sections-file SECTIONS.json",
|
|
811
|
+
},
|
|
812
|
+
)
|
|
713
813
|
return context.builder.app_layout_apply(
|
|
714
814
|
profile=args.profile,
|
|
715
815
|
app_key=args.app_key,
|
|
716
816
|
mode=args.mode,
|
|
717
817
|
publish=bool(args.publish),
|
|
718
|
-
sections=require_list_arg(args.sections_file, option_name="--sections-file"),
|
|
818
|
+
sections=[] if apps is not None else require_list_arg(args.sections_file, option_name="--sections-file"),
|
|
819
|
+
apps=apps,
|
|
719
820
|
)
|
|
720
821
|
|
|
721
822
|
|
|
722
823
|
def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
824
|
+
views = _load_views_file_arg(args.views_file) if getattr(args, "views_file", None) else None
|
|
825
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
826
|
+
if views is not None:
|
|
827
|
+
if apps is not None or (args.app_key or "").strip() or getattr(args, "upsert_views_file", None) or getattr(args, "patch_views_file", None) or getattr(args, "remove_views_file", None):
|
|
828
|
+
raise_config_error(
|
|
829
|
+
"builder views apply --views-file cannot be mixed with --app-key, --apps-file, --upsert-views-file, --patch-views-file, or --remove-views-file.",
|
|
830
|
+
fix_hint="Use one shape: preferred --views-file for flat batch jobs, or legacy --app-key/--apps-file for grouped payloads.",
|
|
831
|
+
)
|
|
832
|
+
return context.builder.app_views_apply(
|
|
833
|
+
profile=args.profile,
|
|
834
|
+
app_key="",
|
|
835
|
+
publish=bool(args.publish),
|
|
836
|
+
upsert_views=[],
|
|
837
|
+
patch_views=[],
|
|
838
|
+
remove_views=[],
|
|
839
|
+
views=views,
|
|
840
|
+
)
|
|
841
|
+
if apps is None and not (args.app_key or "").strip():
|
|
842
|
+
raise_config_error("builder views apply requires --views-file, --apps-file, or --app-key")
|
|
723
843
|
return context.builder.app_views_apply(
|
|
724
844
|
profile=args.profile,
|
|
725
845
|
app_key=args.app_key,
|
|
@@ -727,43 +847,63 @@ def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
727
847
|
upsert_views=load_list_arg(args.upsert_views_file, option_name="--upsert-views-file"),
|
|
728
848
|
patch_views=load_list_arg(args.patch_views_file, option_name="--patch-views-file"),
|
|
729
849
|
remove_views=load_list_arg(args.remove_views_file, option_name="--remove-views-file"),
|
|
850
|
+
apps=apps,
|
|
730
851
|
)
|
|
731
852
|
|
|
732
853
|
|
|
733
|
-
def
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
854
|
+
def _handle_flow_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
855
|
+
patch_nodes = load_list_arg(getattr(args, "patch_nodes_file", None), option_name="--patch-nodes-file")
|
|
856
|
+
spec = load_object_arg(getattr(args, "spec_file", None), option_name="--spec-file")
|
|
857
|
+
has_legacy_nodes = bool(getattr(args, "nodes_file", None) or getattr(args, "transitions_file", None))
|
|
858
|
+
if has_legacy_nodes:
|
|
859
|
+
raise_config_error(
|
|
860
|
+
"builder flow apply no longer exposes legacy --nodes-file/--transitions-file input.",
|
|
861
|
+
fix_hint="Use --spec-file for a complete WorkflowSpec, or --patch-nodes-file for targeted edits after builder flow get.",
|
|
862
|
+
error_code="FLOW_LEGACY_INPUT_DISABLED",
|
|
863
|
+
details={"allowed_input_modes": ["--spec-file", "--patch-nodes-file"]},
|
|
864
|
+
)
|
|
865
|
+
selected_modes = sum(1 for selected in (bool(patch_nodes), bool(spec), has_legacy_nodes) if selected)
|
|
866
|
+
if selected_modes != 1:
|
|
867
|
+
raise_config_error(
|
|
868
|
+
"builder flow apply requires exactly one input mode: --spec-file or --patch-nodes-file.",
|
|
869
|
+
fix_hint="Use --spec-file for a complete WorkflowSpec, or --patch-nodes-file for targeted edits after builder flow get.",
|
|
870
|
+
)
|
|
871
|
+
if patch_nodes:
|
|
872
|
+
return context.builder.app_flow_apply(
|
|
873
|
+
profile=args.profile,
|
|
874
|
+
app_key=args.app_key,
|
|
875
|
+
publish=bool(args.publish),
|
|
876
|
+
patch_nodes=patch_nodes,
|
|
877
|
+
idempotency_key=args.idempotency_key,
|
|
878
|
+
schema_version=args.schema_version,
|
|
879
|
+
)
|
|
880
|
+
if spec:
|
|
881
|
+
return context.builder.app_flow_apply(
|
|
882
|
+
profile=args.profile,
|
|
883
|
+
app_key=args.app_key,
|
|
884
|
+
publish=bool(args.publish),
|
|
885
|
+
spec=spec,
|
|
886
|
+
idempotency_key=args.idempotency_key,
|
|
887
|
+
schema_version=args.schema_version,
|
|
888
|
+
)
|
|
889
|
+
raise_config_error(
|
|
890
|
+
"builder flow apply requires --spec-file or --patch-nodes-file.",
|
|
891
|
+
fix_hint="Use --spec-file for a complete WorkflowSpec, or --patch-nodes-file for targeted edits after builder flow get.",
|
|
737
892
|
)
|
|
738
893
|
|
|
739
894
|
|
|
740
|
-
def
|
|
741
|
-
return context.builder.
|
|
742
|
-
profile=args.profile,
|
|
743
|
-
app_key=args.app_key,
|
|
744
|
-
version_id=args.version_id or None,
|
|
745
|
-
)
|
|
895
|
+
def _handle_flow_schema(args: argparse.Namespace, context: CliContext) -> dict:
|
|
896
|
+
return context.builder.app_flow_get_schema(profile=args.profile, schema_version=args.schema_version)
|
|
746
897
|
|
|
747
898
|
|
|
748
|
-
def
|
|
749
|
-
|
|
750
|
-
if not isinstance(spec_payload, dict):
|
|
751
|
-
raise_config_error("flow apply --spec-file must contain a JSON object.")
|
|
752
|
-
if "spec" in spec_payload and isinstance(spec_payload.get("spec"), dict):
|
|
753
|
-
spec = spec_payload["spec"]
|
|
754
|
-
else:
|
|
755
|
-
spec = spec_payload
|
|
756
|
-
return context.builder.app_flow_apply(
|
|
757
|
-
profile=args.profile,
|
|
758
|
-
app_key=args.app_key,
|
|
759
|
-
publish=bool(args.publish),
|
|
760
|
-
spec=spec,
|
|
761
|
-
idempotency_key=args.idempotency_key or None,
|
|
762
|
-
schema_version=args.schema_version or None,
|
|
763
|
-
)
|
|
899
|
+
def _handle_flow_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
900
|
+
return context.builder.app_flow_get(profile=args.profile, app_key=args.app_key, version_id=args.version_id)
|
|
764
901
|
|
|
765
902
|
|
|
766
903
|
def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
904
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
905
|
+
if apps is None and not (args.app_key or "").strip():
|
|
906
|
+
raise_config_error("builder charts apply requires --app-key unless --apps-file is provided")
|
|
767
907
|
return context.builder.app_charts_apply(
|
|
768
908
|
profile=args.profile,
|
|
769
909
|
app_key=args.app_key,
|
|
@@ -771,6 +911,7 @@ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
771
911
|
patch_charts=load_list_arg(args.patch_file, option_name="--patch-file"),
|
|
772
912
|
remove_chart_ids=load_list_arg(args.remove_chart_ids_file, option_name="--remove-chart-ids-file"),
|
|
773
913
|
reorder_chart_ids=load_list_arg(args.reorder_chart_ids_file, option_name="--reorder-chart-ids-file"),
|
|
914
|
+
apps=apps,
|
|
774
915
|
)
|
|
775
916
|
|
|
776
917
|
|
|
@@ -793,6 +934,11 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
793
934
|
fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-id --dash-name`.",
|
|
794
935
|
)
|
|
795
936
|
sections = [] if not args.sections_file else require_list_arg(args.sections_file, option_name="--sections-file")
|
|
937
|
+
patch_sections = (
|
|
938
|
+
load_list_arg(args.patch_sections_file, option_name="--patch-sections-file")
|
|
939
|
+
if args.patch_sections_file
|
|
940
|
+
else None
|
|
941
|
+
)
|
|
796
942
|
return context.builder.portal_apply(
|
|
797
943
|
profile=args.profile,
|
|
798
944
|
dash_key=args.dash_key,
|
|
@@ -809,6 +955,7 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
809
955
|
dash_global_config=load_object_arg(args.dash_global_config_file, option_name="--dash-global-config-file"),
|
|
810
956
|
config=load_object_arg(args.config_file, option_name="--config-file"),
|
|
811
957
|
payload=payload,
|
|
958
|
+
patch_sections=patch_sections,
|
|
812
959
|
)
|
|
813
960
|
|
|
814
961
|
|
|
@@ -820,6 +967,16 @@ def _handle_portal_delete(args: argparse.Namespace, context: CliContext) -> dict
|
|
|
820
967
|
|
|
821
968
|
|
|
822
969
|
def _handle_publish_verify(args: argparse.Namespace, context: CliContext) -> dict:
|
|
970
|
+
app_keys = _app_keys_from_args(args)
|
|
971
|
+
if app_keys is not None:
|
|
972
|
+
return context.builder.app_publish_verify(
|
|
973
|
+
profile=args.profile,
|
|
974
|
+
app_key="",
|
|
975
|
+
app_keys=app_keys,
|
|
976
|
+
expected_package_id=args.expected_package_id,
|
|
977
|
+
)
|
|
978
|
+
if not (args.app_key or "").strip():
|
|
979
|
+
raise_config_error("builder publish verify requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
823
980
|
return context.builder.app_publish_verify(
|
|
824
981
|
profile=args.profile,
|
|
825
982
|
app_key=args.app_key,
|
|
@@ -79,6 +79,23 @@ def run(
|
|
|
79
79
|
return 2
|
|
80
80
|
except SystemExit as exc:
|
|
81
81
|
return int(exc.code or 0)
|
|
82
|
+
try:
|
|
83
|
+
_validate_conditional_args(args)
|
|
84
|
+
except _CliArgumentError as exc:
|
|
85
|
+
if _should_force_json_output_argv(normalized_argv):
|
|
86
|
+
payload = {
|
|
87
|
+
"category": "config",
|
|
88
|
+
"status": "failed",
|
|
89
|
+
"error_code": "ARGUMENT_ERROR",
|
|
90
|
+
"message": exc.message,
|
|
91
|
+
"details": {"usage": exc.usage.strip(), "prog": exc.prog},
|
|
92
|
+
}
|
|
93
|
+
payload = _maybe_attach_builder_apply_error_envelope_from_args(args, payload)
|
|
94
|
+
emit_json_result(payload, stream=out)
|
|
95
|
+
return 2
|
|
96
|
+
err.write(exc.usage)
|
|
97
|
+
err.write(f"{exc.prog}: error: {exc.message}\n")
|
|
98
|
+
return 2
|
|
82
99
|
setattr(args, "_stdin", sys.stdin)
|
|
83
100
|
setattr(args, "_stdout_stream", out)
|
|
84
101
|
setattr(args, "_stderr_stream", err)
|
|
@@ -144,6 +161,21 @@ def _emit_version(*, json_mode: bool, stdout: TextIO) -> int:
|
|
|
144
161
|
return 0
|
|
145
162
|
|
|
146
163
|
|
|
164
|
+
def _validate_conditional_args(args: argparse.Namespace) -> None:
|
|
165
|
+
if (
|
|
166
|
+
getattr(args, "command", "") in {"builder", "build"}
|
|
167
|
+
and getattr(args, "builder_command", "") == "layout"
|
|
168
|
+
and getattr(args, "builder_layout_command", "") == "apply"
|
|
169
|
+
and not getattr(args, "apps_file", None)
|
|
170
|
+
and not getattr(args, "sections_file", None)
|
|
171
|
+
):
|
|
172
|
+
raise _CliArgumentError(
|
|
173
|
+
prog="qingflow builder layout apply",
|
|
174
|
+
message="--sections-file is required",
|
|
175
|
+
usage="usage: qingflow builder layout apply --app-key APP_KEY --sections-file SECTIONS.json\n",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
147
179
|
def _should_force_json_output_argv_for_version(argv: list[str]) -> bool:
|
|
148
180
|
return "--json" in argv
|
|
149
181
|
|
|
@@ -384,6 +416,8 @@ def _emit_error(payload: dict[str, Any], *, json_mode: bool, stdout: TextIO, std
|
|
|
384
416
|
|
|
385
417
|
|
|
386
418
|
def _error_exit_code(payload: dict[str, Any]) -> int:
|
|
419
|
+
if str(payload.get("error_code") or "").upper() == "ARGUMENT_ERROR":
|
|
420
|
+
return 2
|
|
387
421
|
if _is_auth_or_workspace_payload(payload):
|
|
388
422
|
return 3
|
|
389
423
|
return 4
|