@josephyan/qingflow-cli 1.1.3 → 1.1.5
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 +7 -3
- package/docs/local-agent-install.md +57 -6
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-skills.mjs +5 -0
- package/npm/bin/qingflow.mjs +1 -34
- package/npm/lib/runtime.mjs +21 -101
- package/npm/scripts/postinstall.mjs +1 -10
- package/package.json +3 -2
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +58 -44
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +35 -0
- package/skills/qingflow-cli/reference/builder/10-build-single-app.md +38 -0
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +39 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md → builder/30-schema-fields.md} +52 -10
- package/skills/qingflow-cli/reference/builder/40-layout.md +52 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md → builder/50-views.md} +39 -15
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md → builder/60-charts.md} +36 -13
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md → builder/70-portal.md} +36 -13
- package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +41 -0
- package/skills/qingflow-cli/reference/builder/90-workflow.md +34 -0
- package/skills/qingflow-cli/reference/builder/99-publish-verify.md +46 -0
- package/skills/qingflow-cli/reference/builder/README.md +41 -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/{QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md → builder/reference/app-delivery-sop.md} +26 -16
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/README.md +293 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-complete-system.md +809 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-single-app.md +830 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/complete-system-development-guide.md +123 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/create-app.md +182 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/environments.md +63 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/flow-actors-and-permissions.md +142 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/gotchas.md +108 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/match-rules.md +114 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/public-surface-sync.md +75 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/single-app-development-guide.md +58 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/solution-playbooks.md +52 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/tool-selection.md +107 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-flow.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-layout.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-schema.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-views.md +7 -0
- package/skills/qingflow-cli/reference/builder/workflow/01-overview.md +45 -0
- package/skills/qingflow-cli/reference/builder/workflow/02-update-mode.md +53 -0
- package/skills/qingflow-cli/reference/builder/workflow/03-flow-patterns.md +57 -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 +165 -0
- package/skills/qingflow-cli/reference/builder/workflow/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-cli/reference/builder/workflow/08-stage5-apply-verify.md +51 -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 +15 -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/{QINGFLOW_CLI_ADMIN_CHEATSHEET.md → core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md} +3 -3
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md → core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md} +6 -6
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_EXPLORATION_REPORT.md → core/QINGFLOW_CLI_EXPLORATION_REPORT.md} +2 -2
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_FIELD_DATA_TYPES.md → core/QINGFLOW_CLI_FIELD_DATA_TYPES.md} +11 -11
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_MEMBER_CHEATSHEET.md → core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md → core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md} +3 -3
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_DELETE_WORKFLOW.md +31 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md} +7 -7
- 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 +75 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md → task/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md} +5 -5
- package/skills/qingflow-cli/reference/task/ops/README.md +131 -0
- package/skills/qingflow-cli/reference/task/ops/environments.md +43 -0
- package/skills/qingflow-cli/reference/task/ops/workflow-usage.md +26 -0
- 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 +605 -0
- package/skills/qingflow-mcp-setup/SKILL.md +115 -0
- package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
- package/skills/qingflow-mcp-setup/references/environments.md +62 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +32 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/__main__.py +6 -2
- package/src/qingflow_mcp/builder_facade/models.py +287 -25
- package/src/qingflow_mcp/builder_facade/service.py +4195 -856
- package/src/qingflow_mcp/cli/commands/builder.py +316 -247
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/common.py +12 -3
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +3 -3
- package/src/qingflow_mcp/cli/commands/portal.py +2 -2
- package/src/qingflow_mcp/cli/commands/record.py +101 -27
- package/src/qingflow_mcp/cli/commands/task.py +28 -47
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/context.py +0 -3
- package/src/qingflow_mcp/cli/formatters.py +784 -16
- package/src/qingflow_mcp/cli/main.py +117 -33
- package/src/qingflow_mcp/errors.py +43 -2
- package/src/qingflow_mcp/public_surface.py +26 -17
- package/src/qingflow_mcp/response_trim.py +81 -17
- package/src/qingflow_mcp/server.py +14 -12
- package/src/qingflow_mcp/server_app_builder.py +65 -21
- package/src/qingflow_mcp/server_app_user.py +22 -16
- package/src/qingflow_mcp/session_store.py +11 -7
- package/src/qingflow_mcp/solution/compiler/__init__.py +3 -1
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
- package/src/qingflow_mcp/solution/executor.py +245 -18
- package/src/qingflow_mcp/tools/ai_builder_tools.py +1782 -399
- package/src/qingflow_mcp/tools/app_tools.py +184 -43
- package/src/qingflow_mcp/tools/approval_tools.py +197 -35
- package/src/qingflow_mcp/tools/auth_tools.py +92 -16
- package/src/qingflow_mcp/tools/code_block_tools.py +298 -40
- package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
- package/src/qingflow_mcp/tools/directory_tools.py +236 -72
- package/src/qingflow_mcp/tools/export_tools.py +244 -34
- package/src/qingflow_mcp/tools/feedback_tools.py +9 -0
- package/src/qingflow_mcp/tools/file_tools.py +9 -3
- package/src/qingflow_mcp/tools/import_tools.py +336 -49
- package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
- package/src/qingflow_mcp/tools/package_tools.py +118 -6
- package/src/qingflow_mcp/tools/portal_tools.py +39 -3
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
- package/src/qingflow_mcp/tools/record_tools.py +1141 -356
- package/src/qingflow_mcp/tools/resource_read_tools.py +188 -39
- package/src/qingflow_mcp/tools/role_tools.py +80 -9
- package/src/qingflow_mcp/tools/solution_tools.py +59 -45
- package/src/qingflow_mcp/tools/task_context_tools.py +662 -158
- package/src/qingflow_mcp/tools/task_tools.py +113 -29
- package/src/qingflow_mcp/tools/view_tools.py +106 -3
- package/src/qingflow_mcp/tools/workflow_tools.py +48 -4
- package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_MATCH_RULES.md → builder/reference/match-rules.md} +0 -0
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md → builder/reference/workspace-icons.md} +0 -0
- /package/skills/qingflow-cli/reference/{charts_remove.example.json → examples/charts/charts_remove.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_reorder.example.json → examples/charts/charts_reorder.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_bar.example.json → examples/charts/charts_upsert_bar.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_dashboard_starter.example.json → examples/charts/charts_upsert_dashboard_starter.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_minimal.example.json → examples/charts/charts_upsert_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_all_types.example.json → examples/portal/portal_sections_all_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_five_types.example.json → examples/portal/portal_sections_five_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_standard_workbench.example.json → examples/portal/portal_sections_standard_workbench.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_complex.json → examples/schema/_batch_schema_complex.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_scalar.json → examples/schema/_batch_schema_scalar.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_add_fields_minimal.example.json → examples/schema/schema_add_fields_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_apply_add_fields_all_types.json → examples/schema/schema_apply_add_fields_all_types.json} +0 -0
- /package/skills/qingflow-cli/reference/{views_upsert_table_minimal.example.json → examples/views/views_upsert_table_minimal.example.json} +0 -0
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
-
from
|
|
4
|
+
from copy import deepcopy
|
|
5
5
|
|
|
6
6
|
from ..context import CliContext
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _parse_app_keys_arg(raw: Any) -> list[str] | None:
|
|
11
|
-
"""Parse comma-separated --app-keys argument into a list, or return None if empty."""
|
|
12
|
-
if not raw:
|
|
13
|
-
return None
|
|
14
|
-
keys = [k.strip() for k in str(raw).split(",") if k.strip()]
|
|
15
|
-
return keys if keys else None
|
|
7
|
+
from ..json_io import load_json_value
|
|
8
|
+
from .common import load_list_arg, load_object_arg, parse_bool_text, raise_config_error, require_list_arg
|
|
16
9
|
|
|
17
10
|
|
|
18
11
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
@@ -29,7 +22,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
29
22
|
file_upload_local.add_argument("--bucket-type")
|
|
30
23
|
file_upload_local.add_argument("--path-id", type=int)
|
|
31
24
|
file_upload_local.add_argument("--file-related-url")
|
|
32
|
-
file_upload_local.set_defaults(handler=_handle_file_upload_local, format_hint="
|
|
25
|
+
file_upload_local.set_defaults(handler=_handle_file_upload_local, format_hint="file_upload_local")
|
|
33
26
|
|
|
34
27
|
feedback = builder_subparsers.add_parser("feedback", help="builder 侧反馈提交")
|
|
35
28
|
feedback_subparsers = feedback.add_subparsers(dest="builder_feedback_command", required=True)
|
|
@@ -45,7 +38,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
45
38
|
feedback_submit.add_argument("--record-id")
|
|
46
39
|
feedback_submit.add_argument("--workflow-node-id")
|
|
47
40
|
feedback_submit.add_argument("--note")
|
|
48
|
-
feedback_submit.set_defaults(handler=_handle_feedback_submit, format_hint="
|
|
41
|
+
feedback_submit.set_defaults(handler=_handle_feedback_submit, format_hint="feedback_submit")
|
|
49
42
|
|
|
50
43
|
contract = builder_subparsers.add_parser("contract", help="读取 builder tool 合约")
|
|
51
44
|
contract.add_argument("--tool-name", required=True)
|
|
@@ -59,7 +52,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
59
52
|
member = builder_subparsers.add_parser("member", help="成员目录")
|
|
60
53
|
member_subparsers = member.add_subparsers(dest="builder_member_command", required=True)
|
|
61
54
|
member_search = member_subparsers.add_parser("search", help="搜索成员")
|
|
62
|
-
member_search.add_argument("--query",
|
|
55
|
+
member_search.add_argument("--query", required=True)
|
|
63
56
|
member_search.add_argument("--page-num", type=int, default=1)
|
|
64
57
|
member_search.add_argument("--page-size", type=int, default=20)
|
|
65
58
|
member_search.add_argument("--contain-disable", action=argparse.BooleanOptionalAction, default=False)
|
|
@@ -68,7 +61,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
68
61
|
role = builder_subparsers.add_parser("role", help="角色目录")
|
|
69
62
|
role_subparsers = role.add_subparsers(dest="builder_role_command", required=True)
|
|
70
63
|
role_search = role_subparsers.add_parser("search", help="搜索角色")
|
|
71
|
-
role_search.add_argument("--keyword",
|
|
64
|
+
role_search.add_argument("--keyword", required=True)
|
|
72
65
|
role_search.add_argument("--page-num", type=int, default=1)
|
|
73
66
|
role_search.add_argument("--page-size", type=int, default=20)
|
|
74
67
|
role_search.set_defaults(handler=_handle_role_search, format_hint="builder_summary")
|
|
@@ -128,7 +121,8 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
128
121
|
default="summary",
|
|
129
122
|
)
|
|
130
123
|
app_get.add_argument("--app-key", default="")
|
|
131
|
-
app_get.add_argument("--app-keys", help="
|
|
124
|
+
app_get.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量读取同一 section")
|
|
125
|
+
app_get.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量读取同一 section")
|
|
132
126
|
app_get.set_defaults(handler=_handle_app_get, format_hint="builder_summary")
|
|
133
127
|
|
|
134
128
|
app_repair_code_blocks = app_subparsers.add_parser("repair-code-blocks", help="扫描或修复现有代码块配置")
|
|
@@ -145,34 +139,35 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
145
139
|
|
|
146
140
|
button_get = button_subparsers.add_parser("get", help="读取应用自定义按钮配置")
|
|
147
141
|
button_get.add_argument("--app-key", default="")
|
|
148
|
-
button_get.add_argument("--app-keys", help="
|
|
142
|
+
button_get.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量读取按钮配置")
|
|
143
|
+
button_get.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量读取按钮配置")
|
|
149
144
|
button_get.set_defaults(handler=_handle_button_get, format_hint="builder_summary")
|
|
150
145
|
|
|
151
146
|
button_apply = button_subparsers.add_parser("apply", help="声明式创建、更新或删除自定义按钮")
|
|
152
147
|
button_apply.add_argument("--app-key", default="")
|
|
148
|
+
button_apply.add_argument("--apps-file", help="多应用按钮配置 JSON 数组;每项包含 app_key 和按钮 payload")
|
|
153
149
|
button_apply.add_argument("--upsert-buttons-file")
|
|
154
150
|
button_apply.add_argument("--patch-buttons-file")
|
|
155
151
|
button_apply.add_argument("--remove-buttons-file")
|
|
156
152
|
button_apply.add_argument("--view-configs-file")
|
|
157
|
-
button_apply.add_argument("--apps-file", help="多应用批量按钮 JSON 数组,每项含 app_key + upsert_buttons/patch_buttons/remove_buttons/view_configs")
|
|
158
153
|
button_apply.set_defaults(handler=_handle_button_apply, format_hint="builder_summary", force_json_output=True)
|
|
159
154
|
|
|
160
155
|
associated_resource = builder_subparsers.add_parser("associated-resource", aliases=["associated-resources"], help="关联视图/报表")
|
|
161
156
|
associated_resource_subparsers = associated_resource.add_subparsers(dest="builder_associated_resource_command", required=True)
|
|
162
|
-
|
|
163
157
|
associated_resource_get = associated_resource_subparsers.add_parser("get", help="读取应用关联资源配置")
|
|
164
158
|
associated_resource_get.add_argument("--app-key", default="")
|
|
165
|
-
associated_resource_get.add_argument("--app-keys", help="
|
|
159
|
+
associated_resource_get.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量读取关联资源配置")
|
|
160
|
+
associated_resource_get.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量读取关联资源配置")
|
|
166
161
|
associated_resource_get.set_defaults(handler=_handle_associated_resource_get, format_hint="builder_summary")
|
|
167
162
|
|
|
168
163
|
associated_resource_apply = associated_resource_subparsers.add_parser("apply", help="声明式管理应用关联资源池和视图展示配置")
|
|
169
164
|
associated_resource_apply.add_argument("--app-key", default="")
|
|
165
|
+
associated_resource_apply.add_argument("--apps-file", help="多应用关联资源配置 JSON 数组;每项包含 app_key 和资源 payload")
|
|
170
166
|
associated_resource_apply.add_argument("--upsert-resources-file")
|
|
171
167
|
associated_resource_apply.add_argument("--patch-resources-file")
|
|
172
168
|
associated_resource_apply.add_argument("--remove-associated-item-ids-file")
|
|
173
169
|
associated_resource_apply.add_argument("--reorder-associated-item-ids-file")
|
|
174
170
|
associated_resource_apply.add_argument("--view-configs-file")
|
|
175
|
-
associated_resource_apply.add_argument("--apps-file", help="多应用批量关联资源 JSON 数组,每项含 app_key + upsert_resources/patch_resources/remove_associated_item_ids/reorder_associated_item_ids/view_configs")
|
|
176
171
|
associated_resource_apply.set_defaults(handler=_handle_associated_resource_apply, format_hint="builder_summary", force_json_output=True)
|
|
177
172
|
|
|
178
173
|
portal = builder_subparsers.add_parser("portal", help="门户")
|
|
@@ -203,6 +198,10 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
203
198
|
portal_apply.add_argument("--patch-sections-file", help="门户组件局部更新 JSON 数组,每项含 chart_ref/view_ref/order + set/unset")
|
|
204
199
|
portal_apply.set_defaults(handler=_handle_portal_apply, format_hint="builder_summary", force_json_output=True)
|
|
205
200
|
|
|
201
|
+
portal_delete = portal_subparsers.add_parser("delete", help="删除门户")
|
|
202
|
+
portal_delete.add_argument("--dash-key", required=True)
|
|
203
|
+
portal_delete.set_defaults(handler=_handle_portal_delete, format_hint="builder_summary", force_json_output=True)
|
|
204
|
+
|
|
206
205
|
schema_apply = builder_subparsers.add_parser("schema", help="字段搭建")
|
|
207
206
|
schema_apply_subparsers = schema_apply.add_subparsers(dest="builder_schema_command", required=True)
|
|
208
207
|
schema_apply_apply = schema_apply_subparsers.add_parser("apply", help="执行字段变更")
|
|
@@ -214,7 +213,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
214
213
|
schema_apply_apply.add_argument("--color")
|
|
215
214
|
schema_apply_apply.add_argument("--visibility-file")
|
|
216
215
|
schema_apply_apply.add_argument("--create-if-missing", action="store_true")
|
|
217
|
-
schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=
|
|
216
|
+
schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=None)
|
|
218
217
|
schema_apply_apply.add_argument("--apps-file", help="多应用 schema JSON 数组;每项可带 client_key/app_name/add_fields,支持 relation target_app_ref")
|
|
219
218
|
schema_apply_apply.add_argument("--add-fields-file", help="字段 JSON 数组;字段可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
220
219
|
schema_apply_apply.add_argument("--update-fields-file", help="字段更新 JSON 数组;set 内可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
@@ -225,60 +224,63 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
225
224
|
layout_apply_subparsers = layout_apply.add_subparsers(dest="builder_layout_command", required=True)
|
|
226
225
|
layout_apply_apply = layout_apply_subparsers.add_parser("apply", help="执行布局变更")
|
|
227
226
|
layout_apply_apply.add_argument("--app-key", default="")
|
|
227
|
+
layout_apply_apply.add_argument("--apps-file", help="多应用布局 JSON 数组;每项包含 app_key/mode?/publish?/sections")
|
|
228
228
|
layout_apply_apply.add_argument("--mode", choices=["merge", "replace"], default="merge")
|
|
229
229
|
layout_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
230
230
|
layout_apply_apply.add_argument("--sections-file")
|
|
231
|
-
layout_apply_apply.add_argument("--apps-file", help="多应用批量布局 JSON 数组,每项含 app_key + sections")
|
|
232
231
|
layout_apply_apply.set_defaults(handler=_handle_layout_apply, format_hint="builder_summary", force_json_output=True)
|
|
233
232
|
|
|
234
233
|
views_apply = builder_subparsers.add_parser("views", help="视图")
|
|
235
234
|
views_apply_subparsers = views_apply.add_subparsers(dest="builder_views_command", required=True)
|
|
236
235
|
views_apply_apply = views_apply_subparsers.add_parser("apply", help="执行视图变更")
|
|
237
236
|
views_apply_apply.add_argument("--app-key", default="")
|
|
237
|
+
views_apply_apply.add_argument("--apps-file", help="多应用视图 JSON 数组;每项包含 app_key 和 upsert/patch/remove payload")
|
|
238
238
|
views_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
239
239
|
views_apply_apply.add_argument("--upsert-views-file")
|
|
240
240
|
views_apply_apply.add_argument("--patch-views-file")
|
|
241
241
|
views_apply_apply.add_argument("--remove-views-file")
|
|
242
|
-
views_apply_apply.add_argument("--apps-file", help="多应用批量视图 JSON 数组,每项含 app_key + upsert_views/patch_views/remove_views")
|
|
243
242
|
views_apply_apply.set_defaults(handler=_handle_views_apply, format_hint="builder_summary", force_json_output=True)
|
|
244
243
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
flow_schema = flow_subparsers.add_parser("schema", help="读取 WorkflowSpec JSON Schema")
|
|
244
|
+
flow_apply = builder_subparsers.add_parser("flow", help="流程")
|
|
245
|
+
flow_apply_subparsers = flow_apply.add_subparsers(dest="builder_flow_command", required=True)
|
|
246
|
+
flow_schema = flow_apply_subparsers.add_parser("schema", help="读取 WorkflowSpec JSON Schema")
|
|
249
247
|
flow_schema.add_argument("--schema-version", default="")
|
|
250
248
|
flow_schema.set_defaults(handler=_handle_flow_schema, format_hint="builder_summary")
|
|
251
249
|
|
|
252
|
-
flow_get =
|
|
250
|
+
flow_get = flow_apply_subparsers.add_parser("get", help="读取 WorkflowSpec")
|
|
253
251
|
flow_get.add_argument("--app-key", required=True)
|
|
254
252
|
flow_get.add_argument("--version-id", default="")
|
|
255
253
|
flow_get.set_defaults(handler=_handle_flow_get, format_hint="builder_summary")
|
|
256
254
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
255
|
+
flow_apply_apply = flow_apply_subparsers.add_parser("apply", help="执行流程变更")
|
|
256
|
+
flow_apply_apply.add_argument("--app-key", required=True)
|
|
257
|
+
flow_apply_apply.add_argument("--mode", choices=["replace"], default="replace")
|
|
258
|
+
flow_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
259
|
+
flow_apply_apply.add_argument("--nodes-file")
|
|
260
|
+
flow_apply_apply.add_argument("--transitions-file")
|
|
261
|
+
flow_apply_apply.add_argument("--spec-file")
|
|
262
|
+
flow_apply_apply.add_argument("--patch-nodes-file", help="节点局部更新 JSON 数组,每项含 id + set/unset")
|
|
263
|
+
flow_apply_apply.add_argument("--idempotency-key", default="")
|
|
264
|
+
flow_apply_apply.add_argument("--schema-version", default="")
|
|
265
|
+
flow_apply_apply.set_defaults(handler=_handle_flow_apply, format_hint="builder_summary", force_json_output=True)
|
|
265
266
|
|
|
266
267
|
charts_apply = builder_subparsers.add_parser("charts", help="报表")
|
|
267
268
|
charts_apply_subparsers = charts_apply.add_subparsers(dest="builder_charts_command", required=True)
|
|
268
269
|
charts_apply_apply = charts_apply_subparsers.add_parser("apply", help="执行报表变更")
|
|
269
270
|
charts_apply_apply.add_argument("--app-key", default="")
|
|
271
|
+
charts_apply_apply.add_argument("--apps-file", help="多应用报表 JSON 数组;每项包含 app_key 和 upsert/patch/remove/reorder payload")
|
|
270
272
|
charts_apply_apply.add_argument("--upsert-file")
|
|
271
273
|
charts_apply_apply.add_argument("--patch-file")
|
|
272
274
|
charts_apply_apply.add_argument("--remove-chart-ids-file")
|
|
273
275
|
charts_apply_apply.add_argument("--reorder-chart-ids-file")
|
|
274
|
-
charts_apply_apply.add_argument("--apps-file", help="多应用批量报表 JSON 数组,每项含 app_key + upsert_charts/patch_charts/remove_chart_ids/reorder_chart_ids")
|
|
275
276
|
charts_apply_apply.set_defaults(handler=_handle_charts_apply, format_hint="builder_summary", force_json_output=True)
|
|
276
277
|
|
|
277
278
|
publish_verify = builder_subparsers.add_parser("publish", help="发布校验")
|
|
278
279
|
publish_verify_subparsers = publish_verify.add_subparsers(dest="builder_publish_command", required=True)
|
|
279
280
|
publish_verify_verify = publish_verify_subparsers.add_parser("verify", help="校验应用发布")
|
|
280
281
|
publish_verify_verify.add_argument("--app-key", default="")
|
|
281
|
-
publish_verify_verify.add_argument("--app-keys", help="
|
|
282
|
+
publish_verify_verify.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量发布校验")
|
|
283
|
+
publish_verify_verify.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量发布校验")
|
|
282
284
|
publish_verify_verify.add_argument("--expected-package-id", type=int)
|
|
283
285
|
publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary", force_json_output=True)
|
|
284
286
|
|
|
@@ -426,26 +428,9 @@ def _handle_button_catalog(args: argparse.Namespace, context: CliContext) -> dic
|
|
|
426
428
|
|
|
427
429
|
|
|
428
430
|
def _handle_button_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
429
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
430
|
-
if apps:
|
|
431
|
-
|
|
432
|
-
if single_app_args:
|
|
433
|
-
raise_config_error(
|
|
434
|
-
f"button apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
435
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
436
|
-
)
|
|
437
|
-
if apps:
|
|
438
|
-
return context.builder.app_custom_buttons_apply(
|
|
439
|
-
profile=args.profile,
|
|
440
|
-
app_key="",
|
|
441
|
-
upsert_buttons=[],
|
|
442
|
-
patch_buttons=[],
|
|
443
|
-
remove_buttons=[],
|
|
444
|
-
view_configs=[],
|
|
445
|
-
apps=apps,
|
|
446
|
-
)
|
|
447
|
-
if not args.app_key:
|
|
448
|
-
raise_config_error("button apply requires --app-key or --apps-file", fix_hint="Pass --app-key APP_KEY for single-app mode, or --apps-file for batch mode.")
|
|
431
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
432
|
+
if apps is None and not (args.app_key or "").strip():
|
|
433
|
+
raise_config_error("builder button apply requires --app-key unless --apps-file is provided")
|
|
449
434
|
return context.builder.app_custom_buttons_apply(
|
|
450
435
|
profile=args.profile,
|
|
451
436
|
app_key=args.app_key,
|
|
@@ -453,40 +438,14 @@ def _handle_button_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
453
438
|
patch_buttons=load_list_arg(args.patch_buttons_file, option_name="--patch-buttons-file"),
|
|
454
439
|
remove_buttons=load_list_arg(args.remove_buttons_file, option_name="--remove-buttons-file"),
|
|
455
440
|
view_configs=load_list_arg(args.view_configs_file, option_name="--view-configs-file"),
|
|
441
|
+
apps=apps,
|
|
456
442
|
)
|
|
457
443
|
|
|
458
444
|
|
|
459
445
|
def _handle_associated_resource_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
460
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
461
|
-
if apps:
|
|
462
|
-
|
|
463
|
-
a for a in [
|
|
464
|
-
"--app-key" if args.app_key else None,
|
|
465
|
-
"--upsert-resources-file" if getattr(args, "upsert_resources_file", None) else None,
|
|
466
|
-
"--patch-resources-file" if getattr(args, "patch_resources_file", None) else None,
|
|
467
|
-
"--remove-associated-item-ids-file" if getattr(args, "remove_associated_item_ids_file", None) else None,
|
|
468
|
-
"--reorder-associated-item-ids-file" if getattr(args, "reorder_associated_item_ids_file", None) else None,
|
|
469
|
-
"--view-configs-file" if getattr(args, "view_configs_file", None) else None,
|
|
470
|
-
] if a
|
|
471
|
-
]
|
|
472
|
-
if single_app_args:
|
|
473
|
-
raise_config_error(
|
|
474
|
-
f"associated-resource apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
475
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
476
|
-
)
|
|
477
|
-
if apps:
|
|
478
|
-
return context.builder.app_associated_resources_apply(
|
|
479
|
-
profile=args.profile,
|
|
480
|
-
app_key="",
|
|
481
|
-
upsert_resources=[],
|
|
482
|
-
patch_resources=[],
|
|
483
|
-
remove_associated_item_ids=[],
|
|
484
|
-
reorder_associated_item_ids=[],
|
|
485
|
-
view_configs=[],
|
|
486
|
-
apps=apps,
|
|
487
|
-
)
|
|
488
|
-
if not args.app_key:
|
|
489
|
-
raise_config_error("associated-resource apply requires --app-key or --apps-file", fix_hint="Pass --app-key APP_KEY for single-app mode, or --apps-file for batch mode.")
|
|
446
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
447
|
+
if apps is None and not (args.app_key or "").strip():
|
|
448
|
+
raise_config_error("builder associated-resource apply requires --app-key unless --apps-file is provided")
|
|
490
449
|
return context.builder.app_associated_resources_apply(
|
|
491
450
|
profile=args.profile,
|
|
492
451
|
app_key=args.app_key,
|
|
@@ -495,27 +454,26 @@ def _handle_associated_resource_apply(args: argparse.Namespace, context: CliCont
|
|
|
495
454
|
remove_associated_item_ids=load_list_arg(args.remove_associated_item_ids_file, option_name="--remove-associated-item-ids-file"),
|
|
496
455
|
reorder_associated_item_ids=load_list_arg(args.reorder_associated_item_ids_file, option_name="--reorder-associated-item-ids-file"),
|
|
497
456
|
view_configs=load_list_arg(args.view_configs_file, option_name="--view-configs-file"),
|
|
457
|
+
apps=apps,
|
|
498
458
|
)
|
|
499
459
|
|
|
500
460
|
|
|
501
461
|
def _handle_button_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
502
|
-
app_keys =
|
|
503
|
-
if app_keys:
|
|
504
|
-
return context.builder.app_get_buttons(profile=args.profile, app_keys=app_keys)
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
return context.builder.app_get_buttons(profile=args.profile, app_key=app_key)
|
|
462
|
+
app_keys = _app_keys_from_args(args)
|
|
463
|
+
if app_keys is not None:
|
|
464
|
+
return context.builder.app_get_buttons(profile=args.profile, app_key="", app_keys=app_keys)
|
|
465
|
+
if not (args.app_key or "").strip():
|
|
466
|
+
raise_config_error("builder button get requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
467
|
+
return context.builder.app_get_buttons(profile=args.profile, app_key=args.app_key)
|
|
509
468
|
|
|
510
469
|
|
|
511
470
|
def _handle_associated_resource_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
512
|
-
app_keys =
|
|
513
|
-
if app_keys:
|
|
514
|
-
return context.builder.app_get_associated_resources(profile=args.profile, app_keys=app_keys)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
return context.builder.app_get_associated_resources(profile=args.profile, app_key=app_key)
|
|
471
|
+
app_keys = _app_keys_from_args(args)
|
|
472
|
+
if app_keys is not None:
|
|
473
|
+
return context.builder.app_get_associated_resources(profile=args.profile, app_key="", app_keys=app_keys)
|
|
474
|
+
if not (args.app_key or "").strip():
|
|
475
|
+
raise_config_error("builder associated-resource get requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
476
|
+
return context.builder.app_get_associated_resources(profile=args.profile, app_key=args.app_key)
|
|
519
477
|
|
|
520
478
|
|
|
521
479
|
def _handle_button_create(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -539,31 +497,18 @@ def _handle_button_delete(args: argparse.Namespace, context: CliContext) -> dict
|
|
|
539
497
|
return context.builder.app_custom_button_delete(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
|
|
540
498
|
|
|
541
499
|
|
|
500
|
+
def _app_keys_from_args(args: argparse.Namespace) -> list[str] | None:
|
|
501
|
+
keys: list[str] = []
|
|
502
|
+
raw = str(getattr(args, "app_keys", "") or "").strip()
|
|
503
|
+
if raw:
|
|
504
|
+
keys.extend(part.strip() for part in raw.split(",") if part.strip())
|
|
505
|
+
keys_file = getattr(args, "app_keys_file", None)
|
|
506
|
+
if keys_file:
|
|
507
|
+
keys.extend(str(item).strip() for item in require_list_arg(keys_file, option_name="--app-keys-file") if str(item).strip())
|
|
508
|
+
return keys or None
|
|
509
|
+
|
|
510
|
+
|
|
542
511
|
def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
543
|
-
section = args.builder_app_get_section
|
|
544
|
-
app_keys = _parse_app_keys_arg(getattr(args, "app_keys", None))
|
|
545
|
-
app_key = str(getattr(args, "app_key", "") or "").strip()
|
|
546
|
-
if not app_keys and not app_key:
|
|
547
|
-
raise_config_error("app get requires --app-key or --app-keys", fix_hint="Pass --app-key APP_KEY or --app-keys KEY1,KEY2")
|
|
548
|
-
if section == "buttons":
|
|
549
|
-
if app_keys:
|
|
550
|
-
return context.builder.app_get_buttons(profile=args.profile, app_keys=app_keys)
|
|
551
|
-
return context.builder.app_get_buttons(profile=args.profile, app_key=app_key)
|
|
552
|
-
if section == "associated-resources":
|
|
553
|
-
if app_keys:
|
|
554
|
-
return context.builder.app_get_associated_resources(profile=args.profile, app_keys=app_keys)
|
|
555
|
-
return context.builder.app_get_associated_resources(profile=args.profile, app_key=app_key)
|
|
556
|
-
if app_keys:
|
|
557
|
-
batch_handlers = {
|
|
558
|
-
"fields": context.builder.app_get_fields,
|
|
559
|
-
"layout": context.builder.app_get_layout,
|
|
560
|
-
"views": context.builder.app_get_views,
|
|
561
|
-
"flow": context.builder.app_get_flow,
|
|
562
|
-
"charts": context.builder.app_get_charts,
|
|
563
|
-
}
|
|
564
|
-
if section not in batch_handlers:
|
|
565
|
-
raise_config_error(f"app get --app-keys does not support section '{section}'", fix_hint="Batch reads support: fields, layout, views, flow, charts, buttons, associated-resources")
|
|
566
|
-
return batch_handlers[section](profile=args.profile, app_keys=app_keys)
|
|
567
512
|
handlers = {
|
|
568
513
|
"summary": context.builder.app_get,
|
|
569
514
|
"fields": context.builder.app_get_fields,
|
|
@@ -571,8 +516,15 @@ def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
571
516
|
"views": context.builder.app_get_views,
|
|
572
517
|
"flow": context.builder.app_get_flow,
|
|
573
518
|
"charts": context.builder.app_get_charts,
|
|
519
|
+
"buttons": context.builder.app_get_buttons,
|
|
520
|
+
"associated-resources": context.builder.app_get_associated_resources,
|
|
574
521
|
}
|
|
575
|
-
|
|
522
|
+
app_keys = _app_keys_from_args(args)
|
|
523
|
+
if app_keys is not None:
|
|
524
|
+
return handlers[args.builder_app_get_section](profile=args.profile, app_key="", app_keys=app_keys)
|
|
525
|
+
if not (args.app_key or "").strip():
|
|
526
|
+
raise_config_error("builder app get requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
527
|
+
return handlers[args.builder_app_get_section](profile=args.profile, app_key=args.app_key)
|
|
576
528
|
|
|
577
529
|
|
|
578
530
|
def _handle_app_repair_code_blocks(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -601,34 +553,57 @@ def _handle_chart_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
601
553
|
|
|
602
554
|
|
|
603
555
|
def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
604
|
-
|
|
556
|
+
apps_payload = _load_apps_file_arg(args.apps_file)
|
|
557
|
+
apps = apps_payload["apps"]
|
|
558
|
+
apps_warnings = apps_payload.get("warnings") or []
|
|
559
|
+
package_id = args.package_id
|
|
560
|
+
if package_id is None and apps_payload.get("package_id") is not None:
|
|
561
|
+
package_id = _coerce_apps_file_package_id(apps_payload["package_id"])
|
|
605
562
|
if args.apps_file:
|
|
563
|
+
file_create_if_missing = _coerce_apps_file_bool(
|
|
564
|
+
apps_payload.get("create_if_missing"),
|
|
565
|
+
field_name="create_if_missing",
|
|
566
|
+
default=False,
|
|
567
|
+
)
|
|
568
|
+
file_publish = _coerce_apps_file_bool(
|
|
569
|
+
apps_payload.get("publish"),
|
|
570
|
+
field_name="publish",
|
|
571
|
+
default=True,
|
|
572
|
+
)
|
|
573
|
+
effective_create_if_missing = bool(args.create_if_missing or file_create_if_missing)
|
|
574
|
+
effective_publish = bool(args.publish if args.publish is not None else file_publish)
|
|
606
575
|
if not apps:
|
|
607
576
|
raise_config_error(
|
|
608
577
|
"schema apply multi-app mode requires a non-empty --apps-file.",
|
|
609
|
-
fix_hint="Pass a JSON array with at least one app item.",
|
|
578
|
+
fix_hint="Pass a JSON array, or a JSON object like {\"package_id\":1001,\"apps\":[...]} with at least one app item.",
|
|
579
|
+
error_code="APPS_FILE_EMPTY",
|
|
610
580
|
)
|
|
611
581
|
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:
|
|
612
582
|
raise_config_error(
|
|
613
583
|
"schema apply multi-app mode accepts --package-id/--create-if-missing plus --apps-file only.",
|
|
614
584
|
fix_hint="Use `--apps-file` for batch mode, or remove `--apps-file` and use the single-app arguments.",
|
|
615
585
|
)
|
|
616
|
-
if
|
|
586
|
+
if package_id is None:
|
|
617
587
|
raise_config_error(
|
|
618
588
|
"schema apply multi-app mode requires --package-id.",
|
|
619
|
-
fix_hint="Pass `--package-id`
|
|
589
|
+
fix_hint="Pass `--package-id`, or put `package_id` at the top level of --apps-file. `package_name` alone does not create the package here; run `builder package apply` first.",
|
|
620
590
|
)
|
|
621
|
-
|
|
591
|
+
result = context.builder.app_schema_apply(
|
|
622
592
|
profile=args.profile,
|
|
623
|
-
package_id=
|
|
593
|
+
package_id=package_id,
|
|
624
594
|
visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
|
|
625
|
-
create_if_missing=
|
|
626
|
-
publish=
|
|
595
|
+
create_if_missing=effective_create_if_missing,
|
|
596
|
+
publish=effective_publish,
|
|
627
597
|
apps=apps,
|
|
628
598
|
add_fields=[],
|
|
629
599
|
update_fields=[],
|
|
630
600
|
remove_fields=[],
|
|
631
601
|
)
|
|
602
|
+
if apps_warnings and isinstance(result, dict):
|
|
603
|
+
result_warnings = list(result.get("warnings") or [])
|
|
604
|
+
result_warnings.extend(deepcopy(apps_warnings))
|
|
605
|
+
result["warnings"] = result_warnings
|
|
606
|
+
return result
|
|
632
607
|
has_app_key = bool((args.app_key or "").strip())
|
|
633
608
|
has_app_name = bool((args.app_name or "").strip())
|
|
634
609
|
has_app_title = bool((args.app_title or "").strip())
|
|
@@ -655,63 +630,178 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
655
630
|
color=args.color,
|
|
656
631
|
visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
|
|
657
632
|
create_if_missing=bool(args.create_if_missing),
|
|
658
|
-
publish=bool(args.publish),
|
|
633
|
+
publish=True if args.publish is None else bool(args.publish),
|
|
659
634
|
add_fields=load_list_arg(args.add_fields_file, option_name="--add-fields-file"),
|
|
660
635
|
update_fields=load_list_arg(args.update_fields_file, option_name="--update-fields-file"),
|
|
661
636
|
remove_fields=load_list_arg(args.remove_fields_file, option_name="--remove-fields-file"),
|
|
662
637
|
)
|
|
663
638
|
|
|
664
639
|
|
|
665
|
-
def
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
640
|
+
def _load_apps_file_arg(path: str | None) -> dict[str, object]:
|
|
641
|
+
if not path:
|
|
642
|
+
return {"apps": []}
|
|
643
|
+
expected_shape = {
|
|
644
|
+
"package_id": 1001,
|
|
645
|
+
"apps": [
|
|
646
|
+
{
|
|
647
|
+
"client_key": "employee",
|
|
648
|
+
"app_name": "员工花名册",
|
|
649
|
+
"icon": "business-personalcard",
|
|
650
|
+
"color": "emerald",
|
|
651
|
+
"add_fields": [],
|
|
652
|
+
}
|
|
653
|
+
],
|
|
654
|
+
}
|
|
655
|
+
expected_shape_details = {
|
|
656
|
+
"expected_shape": expected_shape,
|
|
657
|
+
"expected_shape_json": (
|
|
658
|
+
'{"package_id":1001,"apps":[{"client_key":"employee","app_name":"员工花名册",'
|
|
659
|
+
'"icon":"business-personalcard","color":"emerald","add_fields":[]}]}'
|
|
660
|
+
),
|
|
661
|
+
}
|
|
662
|
+
payload = load_json_value(path, option_name="--apps-file")
|
|
663
|
+
if isinstance(payload, list):
|
|
664
|
+
if len(payload) == 1 and isinstance(payload[0], dict) and "apps" in payload[0]:
|
|
665
|
+
wrapper = payload[0]
|
|
666
|
+
apps = wrapper.get("apps")
|
|
667
|
+
if not isinstance(apps, list):
|
|
668
|
+
raise_config_error(
|
|
669
|
+
"--apps-file wrapper object requires an apps array.",
|
|
670
|
+
fix_hint="Use {\"package_id\":1001,\"apps\":[...]} or pass a raw apps array like [{\"app_name\":\"...\",\"icon\":\"...\",\"color\":\"...\",\"add_fields\":[...]}].",
|
|
671
|
+
error_code="APPS_FILE_SHAPE_INVALID",
|
|
672
|
+
details=expected_shape_details,
|
|
673
|
+
)
|
|
674
|
+
result: dict[str, object] = {
|
|
675
|
+
"apps": apps,
|
|
676
|
+
"warnings": [
|
|
677
|
+
{
|
|
678
|
+
"code": "APPS_FILE_WRAPPER_ARRAY_UNWRAPPED",
|
|
679
|
+
"message": "--apps-file was a singleton wrapper array; normalized it to {package_id, apps}.",
|
|
680
|
+
}
|
|
681
|
+
],
|
|
682
|
+
}
|
|
683
|
+
if wrapper.get("package_id") is not None:
|
|
684
|
+
result["package_id"] = wrapper.get("package_id")
|
|
685
|
+
if wrapper.get("create_if_missing") is not None:
|
|
686
|
+
result["create_if_missing"] = wrapper.get("create_if_missing")
|
|
687
|
+
if wrapper.get("publish") is not None:
|
|
688
|
+
result["publish"] = wrapper.get("publish")
|
|
689
|
+
return result
|
|
690
|
+
if any(isinstance(item, dict) and "apps" in item for item in payload):
|
|
670
691
|
raise_config_error(
|
|
671
|
-
|
|
672
|
-
fix_hint="Use
|
|
692
|
+
"--apps-file root array items must be app items, not package wrapper objects.",
|
|
693
|
+
fix_hint="Use one object {\"package_id\":1001,\"apps\":[...]} or a raw app array [{\"app_name\":\"...\",\"icon\":\"...\",\"color\":\"...\",\"add_fields\":[...]}]. Do not wrap multiple {package_id, apps} objects in an array.",
|
|
694
|
+
error_code="APPS_FILE_SHAPE_INVALID",
|
|
695
|
+
details=expected_shape_details,
|
|
673
696
|
)
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
697
|
+
return {"apps": payload}
|
|
698
|
+
if isinstance(payload, dict):
|
|
699
|
+
apps = payload.get("apps")
|
|
700
|
+
if not isinstance(apps, list):
|
|
701
|
+
raise_config_error(
|
|
702
|
+
"--apps-file JSON object requires an apps array.",
|
|
703
|
+
fix_hint="Use {\"package_id\":1001,\"apps\":[...]} or pass a raw JSON array.",
|
|
704
|
+
error_code="APPS_FILE_SHAPE_INVALID",
|
|
705
|
+
details=expected_shape_details,
|
|
706
|
+
)
|
|
707
|
+
result: dict[str, object] = {"apps": apps}
|
|
708
|
+
if payload.get("package_id") is not None:
|
|
709
|
+
result["package_id"] = payload.get("package_id")
|
|
710
|
+
if payload.get("create_if_missing") is not None:
|
|
711
|
+
result["create_if_missing"] = payload.get("create_if_missing")
|
|
712
|
+
if payload.get("publish") is not None:
|
|
713
|
+
result["publish"] = payload.get("publish")
|
|
714
|
+
return result
|
|
715
|
+
raise_config_error(
|
|
716
|
+
"--apps-file must be a JSON array or an object containing apps.",
|
|
717
|
+
fix_hint="Use [{...}] or {\"package_id\":1001,\"apps\":[...]}",
|
|
718
|
+
error_code="APPS_FILE_SHAPE_INVALID",
|
|
719
|
+
details=expected_shape_details,
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def _coerce_apps_file_package_id(value: object) -> int:
|
|
724
|
+
package_id: int
|
|
725
|
+
if isinstance(value, bool):
|
|
726
|
+
_raise_apps_file_package_id_invalid(value)
|
|
727
|
+
if isinstance(value, int):
|
|
728
|
+
package_id = value
|
|
729
|
+
elif isinstance(value, str):
|
|
730
|
+
stripped = value.strip()
|
|
731
|
+
try:
|
|
732
|
+
package_id = int(stripped)
|
|
733
|
+
except ValueError:
|
|
734
|
+
_raise_apps_file_package_id_invalid(value)
|
|
735
|
+
else:
|
|
736
|
+
_raise_apps_file_package_id_invalid(value)
|
|
737
|
+
if package_id <= 0:
|
|
738
|
+
_raise_apps_file_package_id_invalid(value)
|
|
739
|
+
return package_id
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def _raise_apps_file_package_id_invalid(value: object) -> None:
|
|
743
|
+
raise_config_error(
|
|
744
|
+
"--apps-file package_id must be a positive integer.",
|
|
745
|
+
fix_hint='Use a numeric package_id, for example {"package_id":1001,"apps":[...]}.',
|
|
746
|
+
error_code="APPS_FILE_PACKAGE_ID_INVALID",
|
|
747
|
+
details={
|
|
748
|
+
"field": "package_id",
|
|
749
|
+
"value": value,
|
|
750
|
+
"expected": "positive integer or numeric string",
|
|
751
|
+
},
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def _coerce_apps_file_bool(value: object, *, field_name: str, default: bool) -> bool:
|
|
756
|
+
if value is None:
|
|
757
|
+
return default
|
|
758
|
+
if isinstance(value, bool):
|
|
759
|
+
return value
|
|
760
|
+
if isinstance(value, str):
|
|
761
|
+
try:
|
|
762
|
+
return parse_bool_text(value)
|
|
763
|
+
except argparse.ArgumentTypeError:
|
|
764
|
+
pass
|
|
765
|
+
raise_config_error(
|
|
766
|
+
f"--apps-file {field_name} must be a boolean.",
|
|
767
|
+
fix_hint=f'Use "{field_name}": true or "{field_name}": false in --apps-file.',
|
|
768
|
+
error_code="APPS_FILE_BOOLEAN_INVALID",
|
|
769
|
+
details={
|
|
770
|
+
"field": field_name,
|
|
771
|
+
"value": value,
|
|
772
|
+
"expected": "boolean true/false or string true/false/1/0/yes/no",
|
|
773
|
+
},
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def _handle_layout_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
778
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
779
|
+
if apps is None and not (args.app_key or "").strip():
|
|
780
|
+
raise_config_error("builder layout apply requires --app-key unless --apps-file is provided")
|
|
781
|
+
if apps is None and not getattr(args, "sections_file", None):
|
|
782
|
+
raise_config_error(
|
|
783
|
+
"--sections-file is required",
|
|
784
|
+
fix_hint="Pass --sections-file for single-app layout apply, or use --apps-file for batch layout apply.",
|
|
785
|
+
error_code="ARGUMENT_ERROR",
|
|
786
|
+
details={
|
|
787
|
+
"prog": "qingflow builder layout apply",
|
|
788
|
+
"usage": "qingflow builder layout apply --app-key APP_KEY --sections-file SECTIONS.json",
|
|
789
|
+
},
|
|
682
790
|
)
|
|
683
|
-
if not args.app_key:
|
|
684
|
-
raise_config_error("layout apply requires --app-key or --apps-file", fix_hint="Pass --app-key + --sections-file for single-app mode, or --apps-file for batch mode.")
|
|
685
791
|
return context.builder.app_layout_apply(
|
|
686
792
|
profile=args.profile,
|
|
687
793
|
app_key=args.app_key,
|
|
688
794
|
mode=args.mode,
|
|
689
795
|
publish=bool(args.publish),
|
|
690
|
-
sections=require_list_arg(args.sections_file, option_name="--sections-file"),
|
|
796
|
+
sections=[] if apps is not None else require_list_arg(args.sections_file, option_name="--sections-file"),
|
|
797
|
+
apps=apps,
|
|
691
798
|
)
|
|
692
799
|
|
|
693
800
|
|
|
694
801
|
def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
695
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
696
|
-
if apps:
|
|
697
|
-
|
|
698
|
-
if single_app_args:
|
|
699
|
-
raise_config_error(
|
|
700
|
-
f"views apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
701
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
702
|
-
)
|
|
703
|
-
if apps:
|
|
704
|
-
return context.builder.app_views_apply(
|
|
705
|
-
profile=args.profile,
|
|
706
|
-
app_key="",
|
|
707
|
-
publish=bool(args.publish),
|
|
708
|
-
upsert_views=[],
|
|
709
|
-
patch_views=[],
|
|
710
|
-
remove_views=[],
|
|
711
|
-
apps=apps,
|
|
712
|
-
)
|
|
713
|
-
if not args.app_key:
|
|
714
|
-
raise_config_error("views apply requires --app-key or --apps-file", fix_hint="Pass --app-key for single-app mode, or --apps-file for batch mode.")
|
|
802
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
803
|
+
if apps is None and not (args.app_key or "").strip():
|
|
804
|
+
raise_config_error("builder views apply requires --app-key unless --apps-file is provided")
|
|
715
805
|
return context.builder.app_views_apply(
|
|
716
806
|
profile=args.profile,
|
|
717
807
|
app_key=args.app_key,
|
|
@@ -719,79 +809,65 @@ def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
719
809
|
upsert_views=load_list_arg(args.upsert_views_file, option_name="--upsert-views-file"),
|
|
720
810
|
patch_views=load_list_arg(args.patch_views_file, option_name="--patch-views-file"),
|
|
721
811
|
remove_views=load_list_arg(args.remove_views_file, option_name="--remove-views-file"),
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
def _handle_flow_schema(args: argparse.Namespace, context: CliContext) -> dict:
|
|
726
|
-
return context.builder.app_flow_get_schema(
|
|
727
|
-
profile=args.profile,
|
|
728
|
-
schema_version=args.schema_version or None,
|
|
729
|
-
)
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
def _handle_flow_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
733
|
-
return context.builder.app_get_flow(
|
|
734
|
-
profile=args.profile,
|
|
735
|
-
app_key=args.app_key,
|
|
736
|
-
version_id=args.version_id or None,
|
|
812
|
+
apps=apps,
|
|
737
813
|
)
|
|
738
814
|
|
|
739
815
|
|
|
740
816
|
def _handle_flow_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
741
817
|
patch_nodes = load_list_arg(getattr(args, "patch_nodes_file", None), option_name="--patch-nodes-file")
|
|
742
|
-
|
|
818
|
+
spec = load_object_arg(getattr(args, "spec_file", None), option_name="--spec-file")
|
|
819
|
+
has_legacy_nodes = bool(getattr(args, "nodes_file", None) or getattr(args, "transitions_file", None))
|
|
820
|
+
selected_modes = sum(1 for selected in (bool(patch_nodes), bool(spec), has_legacy_nodes) if selected)
|
|
821
|
+
if selected_modes != 1:
|
|
743
822
|
raise_config_error(
|
|
744
|
-
"flow apply --spec-file
|
|
745
|
-
fix_hint="Use --
|
|
823
|
+
"builder flow apply requires exactly one input mode: --spec-file, --patch-nodes-file, or --nodes-file with --transitions-file.",
|
|
824
|
+
fix_hint="Use --patch-nodes-file for local edits, --spec-file for full WorkflowSpec, or legacy --nodes-file plus --transitions-file.",
|
|
746
825
|
)
|
|
747
826
|
if patch_nodes:
|
|
748
827
|
return context.builder.app_flow_apply(
|
|
749
828
|
profile=args.profile,
|
|
750
829
|
app_key=args.app_key,
|
|
751
830
|
publish=bool(args.publish),
|
|
752
|
-
spec=None,
|
|
753
831
|
patch_nodes=patch_nodes,
|
|
754
|
-
idempotency_key=args.idempotency_key
|
|
755
|
-
schema_version=args.schema_version
|
|
832
|
+
idempotency_key=args.idempotency_key,
|
|
833
|
+
schema_version=args.schema_version,
|
|
834
|
+
)
|
|
835
|
+
if spec:
|
|
836
|
+
return context.builder.app_flow_apply(
|
|
837
|
+
profile=args.profile,
|
|
838
|
+
app_key=args.app_key,
|
|
839
|
+
publish=bool(args.publish),
|
|
840
|
+
spec=spec,
|
|
841
|
+
idempotency_key=args.idempotency_key,
|
|
842
|
+
schema_version=args.schema_version,
|
|
843
|
+
)
|
|
844
|
+
if not getattr(args, "nodes_file", None) or not getattr(args, "transitions_file", None):
|
|
845
|
+
raise_config_error(
|
|
846
|
+
"legacy builder flow apply requires both --nodes-file and --transitions-file.",
|
|
847
|
+
fix_hint="Pass both files, or use --spec-file / --patch-nodes-file.",
|
|
756
848
|
)
|
|
757
|
-
spec_payload = load_object_arg(args.spec_file, option_name="--spec-file") if args.spec_file else None
|
|
758
|
-
if not isinstance(spec_payload, dict):
|
|
759
|
-
raise_config_error("flow apply requires either --spec-file or --patch-nodes-file.", fix_hint="Pass --spec-file to replace the full flow spec, or --patch-nodes-file to patch specific nodes.")
|
|
760
|
-
if "spec" in spec_payload and isinstance(spec_payload.get("spec"), dict):
|
|
761
|
-
spec = spec_payload["spec"]
|
|
762
|
-
else:
|
|
763
|
-
spec = spec_payload
|
|
764
849
|
return context.builder.app_flow_apply(
|
|
765
850
|
profile=args.profile,
|
|
766
851
|
app_key=args.app_key,
|
|
852
|
+
mode=args.mode,
|
|
767
853
|
publish=bool(args.publish),
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
schema_version=args.schema_version or None,
|
|
854
|
+
nodes=require_list_arg(args.nodes_file, option_name="--nodes-file"),
|
|
855
|
+
transitions=require_list_arg(args.transitions_file, option_name="--transitions-file"),
|
|
771
856
|
)
|
|
772
857
|
|
|
773
858
|
|
|
859
|
+
def _handle_flow_schema(args: argparse.Namespace, context: CliContext) -> dict:
|
|
860
|
+
return context.builder.app_flow_get_schema(profile=args.profile, schema_version=args.schema_version)
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
def _handle_flow_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
864
|
+
return context.builder.app_flow_get(profile=args.profile, app_key=args.app_key, version_id=args.version_id)
|
|
865
|
+
|
|
866
|
+
|
|
774
867
|
def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
775
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
776
|
-
if apps:
|
|
777
|
-
|
|
778
|
-
if single_app_args:
|
|
779
|
-
raise_config_error(
|
|
780
|
-
f"charts apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
781
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
782
|
-
)
|
|
783
|
-
if apps:
|
|
784
|
-
return context.builder.app_charts_apply(
|
|
785
|
-
profile=args.profile,
|
|
786
|
-
app_key="",
|
|
787
|
-
upsert_charts=[],
|
|
788
|
-
patch_charts=[],
|
|
789
|
-
remove_chart_ids=[],
|
|
790
|
-
reorder_chart_ids=[],
|
|
791
|
-
apps=apps,
|
|
792
|
-
)
|
|
793
|
-
if not args.app_key:
|
|
794
|
-
raise_config_error("charts apply requires --app-key or --apps-file", fix_hint="Pass --app-key for single-app mode, or --apps-file for batch mode.")
|
|
868
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
869
|
+
if apps is None and not (args.app_key or "").strip():
|
|
870
|
+
raise_config_error("builder charts apply requires --app-key unless --apps-file is provided")
|
|
795
871
|
return context.builder.app_charts_apply(
|
|
796
872
|
profile=args.profile,
|
|
797
873
|
app_key=args.app_key,
|
|
@@ -799,30 +875,11 @@ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
799
875
|
patch_charts=load_list_arg(args.patch_file, option_name="--patch-file"),
|
|
800
876
|
remove_chart_ids=load_list_arg(args.remove_chart_ids_file, option_name="--remove-chart-ids-file"),
|
|
801
877
|
reorder_chart_ids=load_list_arg(args.reorder_chart_ids_file, option_name="--reorder-chart-ids-file"),
|
|
878
|
+
apps=apps,
|
|
802
879
|
)
|
|
803
880
|
|
|
804
881
|
|
|
805
882
|
def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
806
|
-
patch_sections = load_list_arg(getattr(args, "patch_sections_file", None), option_name="--patch-sections-file")
|
|
807
|
-
if patch_sections:
|
|
808
|
-
if not (args.dash_key or "").strip():
|
|
809
|
-
raise_config_error("portal apply --patch-sections-file requires --dash-key", fix_hint="Pass --dash-key DASH_KEY to identify the portal to patch")
|
|
810
|
-
has_sections_file = bool(getattr(args, "sections_file", None))
|
|
811
|
-
has_payload_file = bool(getattr(args, "payload_file", None))
|
|
812
|
-
if has_sections_file or has_payload_file:
|
|
813
|
-
raise_config_error(
|
|
814
|
-
"portal apply --patch-sections-file cannot be combined with --sections-file or --payload-file.",
|
|
815
|
-
fix_hint="Use --patch-sections-file alone to patch specific sections, or --sections-file to replace all sections.",
|
|
816
|
-
)
|
|
817
|
-
return context.builder.portal_apply(
|
|
818
|
-
profile=args.profile,
|
|
819
|
-
dash_key=args.dash_key,
|
|
820
|
-
dash_name="",
|
|
821
|
-
package_id=None,
|
|
822
|
-
publish=bool(args.publish),
|
|
823
|
-
sections=[],
|
|
824
|
-
patch_sections=patch_sections,
|
|
825
|
-
)
|
|
826
883
|
payload = load_object_arg(args.payload_file, option_name="--payload-file") if getattr(args, "payload_file", None) else None
|
|
827
884
|
payload_obj = payload if isinstance(payload, dict) else {}
|
|
828
885
|
effective_dash_name = (args.dash_name or str(payload_obj.get("dash_name") or payload_obj.get("dashName") or payload_obj.get("name") or "")).strip()
|
|
@@ -841,6 +898,7 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
841
898
|
fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-id --dash-name`.",
|
|
842
899
|
)
|
|
843
900
|
sections = [] if not args.sections_file else require_list_arg(args.sections_file, option_name="--sections-file")
|
|
901
|
+
patch_sections = load_list_arg(args.patch_sections_file, option_name="--patch-sections-file")
|
|
844
902
|
return context.builder.portal_apply(
|
|
845
903
|
profile=args.profile,
|
|
846
904
|
dash_key=args.dash_key,
|
|
@@ -857,17 +915,28 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
857
915
|
dash_global_config=load_object_arg(args.dash_global_config_file, option_name="--dash-global-config-file"),
|
|
858
916
|
config=load_object_arg(args.config_file, option_name="--config-file"),
|
|
859
917
|
payload=payload,
|
|
918
|
+
patch_sections=patch_sections,
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
def _handle_portal_delete(args: argparse.Namespace, context: CliContext) -> dict:
|
|
923
|
+
return context.builder.portal_delete(
|
|
924
|
+
profile=args.profile,
|
|
925
|
+
dash_key=args.dash_key,
|
|
860
926
|
)
|
|
861
927
|
|
|
862
928
|
|
|
863
929
|
def _handle_publish_verify(args: argparse.Namespace, context: CliContext) -> dict:
|
|
864
|
-
app_keys =
|
|
865
|
-
if app_keys:
|
|
930
|
+
app_keys = _app_keys_from_args(args)
|
|
931
|
+
if app_keys is not None:
|
|
866
932
|
return context.builder.app_publish_verify(
|
|
867
933
|
profile=args.profile,
|
|
934
|
+
app_key="",
|
|
868
935
|
app_keys=app_keys,
|
|
869
936
|
expected_package_id=args.expected_package_id,
|
|
870
937
|
)
|
|
938
|
+
if not (args.app_key or "").strip():
|
|
939
|
+
raise_config_error("builder publish verify requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
871
940
|
return context.builder.app_publish_verify(
|
|
872
941
|
profile=args.profile,
|
|
873
942
|
app_key=args.app_key,
|