@josephyan/qingflow-cli 1.1.4 → 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 +282 -102
- package/src/qingflow_mcp/builder_facade/service.py +4166 -929
- package/src/qingflow_mcp/cli/commands/builder.py +316 -298
- 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 +1780 -406
- 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,68 +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
7
|
from ..json_io import load_json_value
|
|
8
|
-
from .common import load_list_arg, load_object_arg, raise_config_error, require_list_arg
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _parse_app_keys_arg(raw: Any) -> list[str] | None:
|
|
12
|
-
"""Parse comma-separated --app-keys argument into a list, or return None if empty."""
|
|
13
|
-
if not raw:
|
|
14
|
-
return None
|
|
15
|
-
keys = [k.strip() for k in str(raw).split(",") if k.strip()]
|
|
16
|
-
return keys if keys else None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _load_schema_apps_payload(path: str | None, *, cli_package_id: int | None) -> tuple[list[Any], int | None]:
|
|
20
|
-
"""Load schema --apps-file.
|
|
21
|
-
|
|
22
|
-
Public docs recommend {"package_id": 123, "apps": [...]}; older payloads used
|
|
23
|
-
a raw apps array, and some agents accidentally wrapped the object in a
|
|
24
|
-
singleton array. Keep all three shapes on the same multi-app path.
|
|
25
|
-
"""
|
|
26
|
-
if not path:
|
|
27
|
-
return [], cli_package_id
|
|
28
|
-
payload = load_json_value(path, option_name="--apps-file")
|
|
29
|
-
package_id = cli_package_id
|
|
30
|
-
if isinstance(payload, dict):
|
|
31
|
-
if package_id is None:
|
|
32
|
-
raw_package_id = payload.get("package_id")
|
|
33
|
-
if isinstance(raw_package_id, int):
|
|
34
|
-
package_id = raw_package_id
|
|
35
|
-
elif raw_package_id is not None and str(raw_package_id).strip().isdigit():
|
|
36
|
-
package_id = int(str(raw_package_id).strip())
|
|
37
|
-
apps = payload.get("apps")
|
|
38
|
-
elif isinstance(payload, list):
|
|
39
|
-
if len(payload) == 1 and isinstance(payload[0], dict) and "apps" in payload[0]:
|
|
40
|
-
wrapper = payload[0]
|
|
41
|
-
if package_id is None:
|
|
42
|
-
raw_package_id = wrapper.get("package_id")
|
|
43
|
-
if isinstance(raw_package_id, int):
|
|
44
|
-
package_id = raw_package_id
|
|
45
|
-
elif raw_package_id is not None and str(raw_package_id).strip().isdigit():
|
|
46
|
-
package_id = int(str(raw_package_id).strip())
|
|
47
|
-
apps = wrapper.get("apps")
|
|
48
|
-
elif any(isinstance(item, dict) and "apps" in item for item in payload):
|
|
49
|
-
raise_config_error(
|
|
50
|
-
"APPS_FILE_SHAPE_INVALID: --apps-file may be {package_id, apps}, a raw apps array, or a singleton wrapper array only.",
|
|
51
|
-
fix_hint='Use {"package_id": 123, "apps": [{"client_key": "main", "app_name": "主表", "icon": "database", "color": "blue", "add_fields": []}]}',
|
|
52
|
-
)
|
|
53
|
-
else:
|
|
54
|
-
apps = payload
|
|
55
|
-
else:
|
|
56
|
-
raise_config_error(
|
|
57
|
-
"APPS_FILE_SHAPE_INVALID: --apps-file must be {package_id, apps} or a JSON array of app items.",
|
|
58
|
-
fix_hint='Use {"package_id": 123, "apps": [{"client_key": "main", "app_name": "主表", "icon": "database", "color": "blue", "add_fields": []}]}',
|
|
59
|
-
)
|
|
60
|
-
if not isinstance(apps, list):
|
|
61
|
-
raise_config_error(
|
|
62
|
-
"APPS_FILE_SHAPE_INVALID: --apps-file apps must be a JSON array.",
|
|
63
|
-
fix_hint='Use {"package_id": 123, "apps": [{"client_key": "main", "app_name": "主表", "icon": "database", "color": "blue", "add_fields": []}]}',
|
|
64
|
-
)
|
|
65
|
-
return apps, package_id
|
|
8
|
+
from .common import load_list_arg, load_object_arg, parse_bool_text, raise_config_error, require_list_arg
|
|
66
9
|
|
|
67
10
|
|
|
68
11
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
@@ -79,7 +22,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
79
22
|
file_upload_local.add_argument("--bucket-type")
|
|
80
23
|
file_upload_local.add_argument("--path-id", type=int)
|
|
81
24
|
file_upload_local.add_argument("--file-related-url")
|
|
82
|
-
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")
|
|
83
26
|
|
|
84
27
|
feedback = builder_subparsers.add_parser("feedback", help="builder 侧反馈提交")
|
|
85
28
|
feedback_subparsers = feedback.add_subparsers(dest="builder_feedback_command", required=True)
|
|
@@ -95,7 +38,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
95
38
|
feedback_submit.add_argument("--record-id")
|
|
96
39
|
feedback_submit.add_argument("--workflow-node-id")
|
|
97
40
|
feedback_submit.add_argument("--note")
|
|
98
|
-
feedback_submit.set_defaults(handler=_handle_feedback_submit, format_hint="
|
|
41
|
+
feedback_submit.set_defaults(handler=_handle_feedback_submit, format_hint="feedback_submit")
|
|
99
42
|
|
|
100
43
|
contract = builder_subparsers.add_parser("contract", help="读取 builder tool 合约")
|
|
101
44
|
contract.add_argument("--tool-name", required=True)
|
|
@@ -109,7 +52,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
109
52
|
member = builder_subparsers.add_parser("member", help="成员目录")
|
|
110
53
|
member_subparsers = member.add_subparsers(dest="builder_member_command", required=True)
|
|
111
54
|
member_search = member_subparsers.add_parser("search", help="搜索成员")
|
|
112
|
-
member_search.add_argument("--query",
|
|
55
|
+
member_search.add_argument("--query", required=True)
|
|
113
56
|
member_search.add_argument("--page-num", type=int, default=1)
|
|
114
57
|
member_search.add_argument("--page-size", type=int, default=20)
|
|
115
58
|
member_search.add_argument("--contain-disable", action=argparse.BooleanOptionalAction, default=False)
|
|
@@ -118,7 +61,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
118
61
|
role = builder_subparsers.add_parser("role", help="角色目录")
|
|
119
62
|
role_subparsers = role.add_subparsers(dest="builder_role_command", required=True)
|
|
120
63
|
role_search = role_subparsers.add_parser("search", help="搜索角色")
|
|
121
|
-
role_search.add_argument("--keyword",
|
|
64
|
+
role_search.add_argument("--keyword", required=True)
|
|
122
65
|
role_search.add_argument("--page-num", type=int, default=1)
|
|
123
66
|
role_search.add_argument("--page-size", type=int, default=20)
|
|
124
67
|
role_search.set_defaults(handler=_handle_role_search, format_hint="builder_summary")
|
|
@@ -178,7 +121,8 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
178
121
|
default="summary",
|
|
179
122
|
)
|
|
180
123
|
app_get.add_argument("--app-key", default="")
|
|
181
|
-
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")
|
|
182
126
|
app_get.set_defaults(handler=_handle_app_get, format_hint="builder_summary")
|
|
183
127
|
|
|
184
128
|
app_repair_code_blocks = app_subparsers.add_parser("repair-code-blocks", help="扫描或修复现有代码块配置")
|
|
@@ -195,34 +139,35 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
195
139
|
|
|
196
140
|
button_get = button_subparsers.add_parser("get", help="读取应用自定义按钮配置")
|
|
197
141
|
button_get.add_argument("--app-key", default="")
|
|
198
|
-
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 字符串数组;用于批量读取按钮配置")
|
|
199
144
|
button_get.set_defaults(handler=_handle_button_get, format_hint="builder_summary")
|
|
200
145
|
|
|
201
146
|
button_apply = button_subparsers.add_parser("apply", help="声明式创建、更新或删除自定义按钮")
|
|
202
147
|
button_apply.add_argument("--app-key", default="")
|
|
148
|
+
button_apply.add_argument("--apps-file", help="多应用按钮配置 JSON 数组;每项包含 app_key 和按钮 payload")
|
|
203
149
|
button_apply.add_argument("--upsert-buttons-file")
|
|
204
150
|
button_apply.add_argument("--patch-buttons-file")
|
|
205
151
|
button_apply.add_argument("--remove-buttons-file")
|
|
206
152
|
button_apply.add_argument("--view-configs-file")
|
|
207
|
-
button_apply.add_argument("--apps-file", help="多应用批量按钮 JSON 数组,每项含 app_key + upsert_buttons/patch_buttons/remove_buttons/view_configs")
|
|
208
153
|
button_apply.set_defaults(handler=_handle_button_apply, format_hint="builder_summary", force_json_output=True)
|
|
209
154
|
|
|
210
155
|
associated_resource = builder_subparsers.add_parser("associated-resource", aliases=["associated-resources"], help="关联视图/报表")
|
|
211
156
|
associated_resource_subparsers = associated_resource.add_subparsers(dest="builder_associated_resource_command", required=True)
|
|
212
|
-
|
|
213
157
|
associated_resource_get = associated_resource_subparsers.add_parser("get", help="读取应用关联资源配置")
|
|
214
158
|
associated_resource_get.add_argument("--app-key", default="")
|
|
215
|
-
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 字符串数组;用于批量读取关联资源配置")
|
|
216
161
|
associated_resource_get.set_defaults(handler=_handle_associated_resource_get, format_hint="builder_summary")
|
|
217
162
|
|
|
218
163
|
associated_resource_apply = associated_resource_subparsers.add_parser("apply", help="声明式管理应用关联资源池和视图展示配置")
|
|
219
164
|
associated_resource_apply.add_argument("--app-key", default="")
|
|
165
|
+
associated_resource_apply.add_argument("--apps-file", help="多应用关联资源配置 JSON 数组;每项包含 app_key 和资源 payload")
|
|
220
166
|
associated_resource_apply.add_argument("--upsert-resources-file")
|
|
221
167
|
associated_resource_apply.add_argument("--patch-resources-file")
|
|
222
168
|
associated_resource_apply.add_argument("--remove-associated-item-ids-file")
|
|
223
169
|
associated_resource_apply.add_argument("--reorder-associated-item-ids-file")
|
|
224
170
|
associated_resource_apply.add_argument("--view-configs-file")
|
|
225
|
-
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")
|
|
226
171
|
associated_resource_apply.set_defaults(handler=_handle_associated_resource_apply, format_hint="builder_summary", force_json_output=True)
|
|
227
172
|
|
|
228
173
|
portal = builder_subparsers.add_parser("portal", help="门户")
|
|
@@ -253,6 +198,10 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
253
198
|
portal_apply.add_argument("--patch-sections-file", help="门户组件局部更新 JSON 数组,每项含 chart_ref/view_ref/order + set/unset")
|
|
254
199
|
portal_apply.set_defaults(handler=_handle_portal_apply, format_hint="builder_summary", force_json_output=True)
|
|
255
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
|
+
|
|
256
205
|
schema_apply = builder_subparsers.add_parser("schema", help="字段搭建")
|
|
257
206
|
schema_apply_subparsers = schema_apply.add_subparsers(dest="builder_schema_command", required=True)
|
|
258
207
|
schema_apply_apply = schema_apply_subparsers.add_parser("apply", help="执行字段变更")
|
|
@@ -264,8 +213,8 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
264
213
|
schema_apply_apply.add_argument("--color")
|
|
265
214
|
schema_apply_apply.add_argument("--visibility-file")
|
|
266
215
|
schema_apply_apply.add_argument("--create-if-missing", action="store_true")
|
|
267
|
-
schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=
|
|
268
|
-
schema_apply_apply.add_argument("--apps-file", help="多应用 schema JSON
|
|
216
|
+
schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=None)
|
|
217
|
+
schema_apply_apply.add_argument("--apps-file", help="多应用 schema JSON 数组;每项可带 client_key/app_name/add_fields,支持 relation target_app_ref")
|
|
269
218
|
schema_apply_apply.add_argument("--add-fields-file", help="字段 JSON 数组;字段可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
270
219
|
schema_apply_apply.add_argument("--update-fields-file", help="字段更新 JSON 数组;set 内可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
271
220
|
schema_apply_apply.add_argument("--remove-fields-file")
|
|
@@ -275,60 +224,63 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
275
224
|
layout_apply_subparsers = layout_apply.add_subparsers(dest="builder_layout_command", required=True)
|
|
276
225
|
layout_apply_apply = layout_apply_subparsers.add_parser("apply", help="执行布局变更")
|
|
277
226
|
layout_apply_apply.add_argument("--app-key", default="")
|
|
227
|
+
layout_apply_apply.add_argument("--apps-file", help="多应用布局 JSON 数组;每项包含 app_key/mode?/publish?/sections")
|
|
278
228
|
layout_apply_apply.add_argument("--mode", choices=["merge", "replace"], default="merge")
|
|
279
229
|
layout_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
280
230
|
layout_apply_apply.add_argument("--sections-file")
|
|
281
|
-
layout_apply_apply.add_argument("--apps-file", help="多应用批量布局 JSON 数组,每项含 app_key + sections")
|
|
282
231
|
layout_apply_apply.set_defaults(handler=_handle_layout_apply, format_hint="builder_summary", force_json_output=True)
|
|
283
232
|
|
|
284
233
|
views_apply = builder_subparsers.add_parser("views", help="视图")
|
|
285
234
|
views_apply_subparsers = views_apply.add_subparsers(dest="builder_views_command", required=True)
|
|
286
235
|
views_apply_apply = views_apply_subparsers.add_parser("apply", help="执行视图变更")
|
|
287
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")
|
|
288
238
|
views_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
289
239
|
views_apply_apply.add_argument("--upsert-views-file")
|
|
290
240
|
views_apply_apply.add_argument("--patch-views-file")
|
|
291
241
|
views_apply_apply.add_argument("--remove-views-file")
|
|
292
|
-
views_apply_apply.add_argument("--apps-file", help="多应用批量视图 JSON 数组,每项含 app_key + upsert_views/patch_views/remove_views")
|
|
293
242
|
views_apply_apply.set_defaults(handler=_handle_views_apply, format_hint="builder_summary", force_json_output=True)
|
|
294
243
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
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")
|
|
299
247
|
flow_schema.add_argument("--schema-version", default="")
|
|
300
248
|
flow_schema.set_defaults(handler=_handle_flow_schema, format_hint="builder_summary")
|
|
301
249
|
|
|
302
|
-
flow_get =
|
|
250
|
+
flow_get = flow_apply_subparsers.add_parser("get", help="读取 WorkflowSpec")
|
|
303
251
|
flow_get.add_argument("--app-key", required=True)
|
|
304
252
|
flow_get.add_argument("--version-id", default="")
|
|
305
253
|
flow_get.set_defaults(handler=_handle_flow_get, format_hint="builder_summary")
|
|
306
254
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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)
|
|
315
266
|
|
|
316
267
|
charts_apply = builder_subparsers.add_parser("charts", help="报表")
|
|
317
268
|
charts_apply_subparsers = charts_apply.add_subparsers(dest="builder_charts_command", required=True)
|
|
318
269
|
charts_apply_apply = charts_apply_subparsers.add_parser("apply", help="执行报表变更")
|
|
319
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")
|
|
320
272
|
charts_apply_apply.add_argument("--upsert-file")
|
|
321
273
|
charts_apply_apply.add_argument("--patch-file")
|
|
322
274
|
charts_apply_apply.add_argument("--remove-chart-ids-file")
|
|
323
275
|
charts_apply_apply.add_argument("--reorder-chart-ids-file")
|
|
324
|
-
charts_apply_apply.add_argument("--apps-file", help="多应用批量报表 JSON 数组,每项含 app_key + upsert_charts/patch_charts/remove_chart_ids/reorder_chart_ids")
|
|
325
276
|
charts_apply_apply.set_defaults(handler=_handle_charts_apply, format_hint="builder_summary", force_json_output=True)
|
|
326
277
|
|
|
327
278
|
publish_verify = builder_subparsers.add_parser("publish", help="发布校验")
|
|
328
279
|
publish_verify_subparsers = publish_verify.add_subparsers(dest="builder_publish_command", required=True)
|
|
329
280
|
publish_verify_verify = publish_verify_subparsers.add_parser("verify", help="校验应用发布")
|
|
330
281
|
publish_verify_verify.add_argument("--app-key", default="")
|
|
331
|
-
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 字符串数组;用于批量发布校验")
|
|
332
284
|
publish_verify_verify.add_argument("--expected-package-id", type=int)
|
|
333
285
|
publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary", force_json_output=True)
|
|
334
286
|
|
|
@@ -476,26 +428,9 @@ def _handle_button_catalog(args: argparse.Namespace, context: CliContext) -> dic
|
|
|
476
428
|
|
|
477
429
|
|
|
478
430
|
def _handle_button_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
479
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
480
|
-
if apps:
|
|
481
|
-
|
|
482
|
-
if single_app_args:
|
|
483
|
-
raise_config_error(
|
|
484
|
-
f"button apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
485
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
486
|
-
)
|
|
487
|
-
if apps:
|
|
488
|
-
return context.builder.app_custom_buttons_apply(
|
|
489
|
-
profile=args.profile,
|
|
490
|
-
app_key="",
|
|
491
|
-
upsert_buttons=[],
|
|
492
|
-
patch_buttons=[],
|
|
493
|
-
remove_buttons=[],
|
|
494
|
-
view_configs=[],
|
|
495
|
-
apps=apps,
|
|
496
|
-
)
|
|
497
|
-
if not args.app_key:
|
|
498
|
-
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")
|
|
499
434
|
return context.builder.app_custom_buttons_apply(
|
|
500
435
|
profile=args.profile,
|
|
501
436
|
app_key=args.app_key,
|
|
@@ -503,40 +438,14 @@ def _handle_button_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
503
438
|
patch_buttons=load_list_arg(args.patch_buttons_file, option_name="--patch-buttons-file"),
|
|
504
439
|
remove_buttons=load_list_arg(args.remove_buttons_file, option_name="--remove-buttons-file"),
|
|
505
440
|
view_configs=load_list_arg(args.view_configs_file, option_name="--view-configs-file"),
|
|
441
|
+
apps=apps,
|
|
506
442
|
)
|
|
507
443
|
|
|
508
444
|
|
|
509
445
|
def _handle_associated_resource_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
510
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
511
|
-
if apps:
|
|
512
|
-
|
|
513
|
-
a for a in [
|
|
514
|
-
"--app-key" if args.app_key else None,
|
|
515
|
-
"--upsert-resources-file" if getattr(args, "upsert_resources_file", None) else None,
|
|
516
|
-
"--patch-resources-file" if getattr(args, "patch_resources_file", None) else None,
|
|
517
|
-
"--remove-associated-item-ids-file" if getattr(args, "remove_associated_item_ids_file", None) else None,
|
|
518
|
-
"--reorder-associated-item-ids-file" if getattr(args, "reorder_associated_item_ids_file", None) else None,
|
|
519
|
-
"--view-configs-file" if getattr(args, "view_configs_file", None) else None,
|
|
520
|
-
] if a
|
|
521
|
-
]
|
|
522
|
-
if single_app_args:
|
|
523
|
-
raise_config_error(
|
|
524
|
-
f"associated-resource apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
525
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
526
|
-
)
|
|
527
|
-
if apps:
|
|
528
|
-
return context.builder.app_associated_resources_apply(
|
|
529
|
-
profile=args.profile,
|
|
530
|
-
app_key="",
|
|
531
|
-
upsert_resources=[],
|
|
532
|
-
patch_resources=[],
|
|
533
|
-
remove_associated_item_ids=[],
|
|
534
|
-
reorder_associated_item_ids=[],
|
|
535
|
-
view_configs=[],
|
|
536
|
-
apps=apps,
|
|
537
|
-
)
|
|
538
|
-
if not args.app_key:
|
|
539
|
-
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")
|
|
540
449
|
return context.builder.app_associated_resources_apply(
|
|
541
450
|
profile=args.profile,
|
|
542
451
|
app_key=args.app_key,
|
|
@@ -545,27 +454,26 @@ def _handle_associated_resource_apply(args: argparse.Namespace, context: CliCont
|
|
|
545
454
|
remove_associated_item_ids=load_list_arg(args.remove_associated_item_ids_file, option_name="--remove-associated-item-ids-file"),
|
|
546
455
|
reorder_associated_item_ids=load_list_arg(args.reorder_associated_item_ids_file, option_name="--reorder-associated-item-ids-file"),
|
|
547
456
|
view_configs=load_list_arg(args.view_configs_file, option_name="--view-configs-file"),
|
|
457
|
+
apps=apps,
|
|
548
458
|
)
|
|
549
459
|
|
|
550
460
|
|
|
551
461
|
def _handle_button_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
552
|
-
app_keys =
|
|
553
|
-
if app_keys:
|
|
554
|
-
return context.builder.app_get_buttons(profile=args.profile, app_keys=app_keys)
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
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)
|
|
559
468
|
|
|
560
469
|
|
|
561
470
|
def _handle_associated_resource_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
562
|
-
app_keys =
|
|
563
|
-
if app_keys:
|
|
564
|
-
return context.builder.app_get_associated_resources(profile=args.profile, app_keys=app_keys)
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
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)
|
|
569
477
|
|
|
570
478
|
|
|
571
479
|
def _handle_button_create(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -589,32 +497,18 @@ def _handle_button_delete(args: argparse.Namespace, context: CliContext) -> dict
|
|
|
589
497
|
return context.builder.app_custom_button_delete(profile=args.profile, app_key=args.app_key, button_id=args.button_id)
|
|
590
498
|
|
|
591
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
|
+
|
|
592
511
|
def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
593
|
-
section = args.builder_app_get_section
|
|
594
|
-
app_keys = _parse_app_keys_arg(getattr(args, "app_keys", None))
|
|
595
|
-
app_key = str(getattr(args, "app_key", "") or "").strip()
|
|
596
|
-
if not app_keys and not app_key:
|
|
597
|
-
raise_config_error("app get requires --app-key or --app-keys", fix_hint="Pass --app-key APP_KEY or --app-keys KEY1,KEY2")
|
|
598
|
-
if section == "buttons":
|
|
599
|
-
if app_keys:
|
|
600
|
-
return context.builder.app_get_buttons(profile=args.profile, app_keys=app_keys)
|
|
601
|
-
return context.builder.app_get_buttons(profile=args.profile, app_key=app_key)
|
|
602
|
-
if section == "associated-resources":
|
|
603
|
-
if app_keys:
|
|
604
|
-
return context.builder.app_get_associated_resources(profile=args.profile, app_keys=app_keys)
|
|
605
|
-
return context.builder.app_get_associated_resources(profile=args.profile, app_key=app_key)
|
|
606
|
-
if app_keys:
|
|
607
|
-
batch_handlers = {
|
|
608
|
-
"summary": context.builder.app_get,
|
|
609
|
-
"fields": context.builder.app_get_fields,
|
|
610
|
-
"layout": context.builder.app_get_layout,
|
|
611
|
-
"views": context.builder.app_get_views,
|
|
612
|
-
"flow": context.builder.app_get_flow,
|
|
613
|
-
"charts": context.builder.app_get_charts,
|
|
614
|
-
}
|
|
615
|
-
if section not in batch_handlers:
|
|
616
|
-
raise_config_error(f"app get --app-keys does not support section '{section}'", fix_hint="Batch reads support: summary, fields, layout, views, flow, charts, buttons, associated-resources")
|
|
617
|
-
return batch_handlers[section](profile=args.profile, app_keys=app_keys)
|
|
618
512
|
handlers = {
|
|
619
513
|
"summary": context.builder.app_get,
|
|
620
514
|
"fields": context.builder.app_get_fields,
|
|
@@ -622,8 +516,15 @@ def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
622
516
|
"views": context.builder.app_get_views,
|
|
623
517
|
"flow": context.builder.app_get_flow,
|
|
624
518
|
"charts": context.builder.app_get_charts,
|
|
519
|
+
"buttons": context.builder.app_get_buttons,
|
|
520
|
+
"associated-resources": context.builder.app_get_associated_resources,
|
|
625
521
|
}
|
|
626
|
-
|
|
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)
|
|
627
528
|
|
|
628
529
|
|
|
629
530
|
def _handle_app_repair_code_blocks(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -652,34 +553,57 @@ def _handle_chart_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
652
553
|
|
|
653
554
|
|
|
654
555
|
def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
655
|
-
|
|
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"])
|
|
656
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)
|
|
657
575
|
if not apps:
|
|
658
576
|
raise_config_error(
|
|
659
577
|
"schema apply multi-app mode requires a non-empty --apps-file.",
|
|
660
|
-
fix_hint=
|
|
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",
|
|
661
580
|
)
|
|
662
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:
|
|
663
582
|
raise_config_error(
|
|
664
583
|
"schema apply multi-app mode accepts --package-id/--create-if-missing plus --apps-file only.",
|
|
665
584
|
fix_hint="Use `--apps-file` for batch mode, or remove `--apps-file` and use the single-app arguments.",
|
|
666
585
|
)
|
|
667
|
-
if
|
|
586
|
+
if package_id is None:
|
|
668
587
|
raise_config_error(
|
|
669
588
|
"schema apply multi-app mode requires --package-id.",
|
|
670
|
-
fix_hint="Pass
|
|
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.",
|
|
671
590
|
)
|
|
672
|
-
|
|
591
|
+
result = context.builder.app_schema_apply(
|
|
673
592
|
profile=args.profile,
|
|
674
|
-
package_id=
|
|
593
|
+
package_id=package_id,
|
|
675
594
|
visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
|
|
676
|
-
create_if_missing=
|
|
677
|
-
publish=
|
|
595
|
+
create_if_missing=effective_create_if_missing,
|
|
596
|
+
publish=effective_publish,
|
|
678
597
|
apps=apps,
|
|
679
598
|
add_fields=[],
|
|
680
599
|
update_fields=[],
|
|
681
600
|
remove_fields=[],
|
|
682
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
|
|
683
607
|
has_app_key = bool((args.app_key or "").strip())
|
|
684
608
|
has_app_name = bool((args.app_name or "").strip())
|
|
685
609
|
has_app_title = bool((args.app_title or "").strip())
|
|
@@ -706,63 +630,178 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
706
630
|
color=args.color,
|
|
707
631
|
visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
|
|
708
632
|
create_if_missing=bool(args.create_if_missing),
|
|
709
|
-
publish=bool(args.publish),
|
|
633
|
+
publish=True if args.publish is None else bool(args.publish),
|
|
710
634
|
add_fields=load_list_arg(args.add_fields_file, option_name="--add-fields-file"),
|
|
711
635
|
update_fields=load_list_arg(args.update_fields_file, option_name="--update-fields-file"),
|
|
712
636
|
remove_fields=load_list_arg(args.remove_fields_file, option_name="--remove-fields-file"),
|
|
713
637
|
)
|
|
714
638
|
|
|
715
639
|
|
|
716
|
-
def
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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):
|
|
721
691
|
raise_config_error(
|
|
722
|
-
|
|
723
|
-
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,
|
|
724
696
|
)
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
+
},
|
|
733
790
|
)
|
|
734
|
-
if not args.app_key:
|
|
735
|
-
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.")
|
|
736
791
|
return context.builder.app_layout_apply(
|
|
737
792
|
profile=args.profile,
|
|
738
793
|
app_key=args.app_key,
|
|
739
794
|
mode=args.mode,
|
|
740
795
|
publish=bool(args.publish),
|
|
741
|
-
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,
|
|
742
798
|
)
|
|
743
799
|
|
|
744
800
|
|
|
745
801
|
def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
746
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
747
|
-
if apps:
|
|
748
|
-
|
|
749
|
-
if single_app_args:
|
|
750
|
-
raise_config_error(
|
|
751
|
-
f"views apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
752
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
753
|
-
)
|
|
754
|
-
if apps:
|
|
755
|
-
return context.builder.app_views_apply(
|
|
756
|
-
profile=args.profile,
|
|
757
|
-
app_key="",
|
|
758
|
-
publish=bool(args.publish),
|
|
759
|
-
upsert_views=[],
|
|
760
|
-
patch_views=[],
|
|
761
|
-
remove_views=[],
|
|
762
|
-
apps=apps,
|
|
763
|
-
)
|
|
764
|
-
if not args.app_key:
|
|
765
|
-
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")
|
|
766
805
|
return context.builder.app_views_apply(
|
|
767
806
|
profile=args.profile,
|
|
768
807
|
app_key=args.app_key,
|
|
@@ -770,79 +809,65 @@ def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
770
809
|
upsert_views=load_list_arg(args.upsert_views_file, option_name="--upsert-views-file"),
|
|
771
810
|
patch_views=load_list_arg(args.patch_views_file, option_name="--patch-views-file"),
|
|
772
811
|
remove_views=load_list_arg(args.remove_views_file, option_name="--remove-views-file"),
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
def _handle_flow_schema(args: argparse.Namespace, context: CliContext) -> dict:
|
|
777
|
-
return context.builder.app_flow_get_schema(
|
|
778
|
-
profile=args.profile,
|
|
779
|
-
schema_version=args.schema_version or None,
|
|
780
|
-
)
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
def _handle_flow_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
784
|
-
return context.builder.app_get_flow(
|
|
785
|
-
profile=args.profile,
|
|
786
|
-
app_key=args.app_key,
|
|
787
|
-
version_id=args.version_id or None,
|
|
812
|
+
apps=apps,
|
|
788
813
|
)
|
|
789
814
|
|
|
790
815
|
|
|
791
816
|
def _handle_flow_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
792
817
|
patch_nodes = load_list_arg(getattr(args, "patch_nodes_file", None), option_name="--patch-nodes-file")
|
|
793
|
-
|
|
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:
|
|
794
822
|
raise_config_error(
|
|
795
|
-
"flow apply --spec-file
|
|
796
|
-
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.",
|
|
797
825
|
)
|
|
798
826
|
if patch_nodes:
|
|
799
827
|
return context.builder.app_flow_apply(
|
|
800
828
|
profile=args.profile,
|
|
801
829
|
app_key=args.app_key,
|
|
802
830
|
publish=bool(args.publish),
|
|
803
|
-
spec=None,
|
|
804
831
|
patch_nodes=patch_nodes,
|
|
805
|
-
idempotency_key=args.idempotency_key
|
|
806
|
-
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.",
|
|
807
848
|
)
|
|
808
|
-
spec_payload = load_object_arg(args.spec_file, option_name="--spec-file") if args.spec_file else None
|
|
809
|
-
if not isinstance(spec_payload, dict):
|
|
810
|
-
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.")
|
|
811
|
-
if "spec" in spec_payload and isinstance(spec_payload.get("spec"), dict):
|
|
812
|
-
spec = spec_payload["spec"]
|
|
813
|
-
else:
|
|
814
|
-
spec = spec_payload
|
|
815
849
|
return context.builder.app_flow_apply(
|
|
816
850
|
profile=args.profile,
|
|
817
851
|
app_key=args.app_key,
|
|
852
|
+
mode=args.mode,
|
|
818
853
|
publish=bool(args.publish),
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
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"),
|
|
822
856
|
)
|
|
823
857
|
|
|
824
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
|
+
|
|
825
867
|
def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
826
|
-
apps = load_list_arg(getattr(args, "apps_file", None)
|
|
827
|
-
if apps:
|
|
828
|
-
|
|
829
|
-
if single_app_args:
|
|
830
|
-
raise_config_error(
|
|
831
|
-
f"charts apply --apps-file cannot be combined with {', '.join(single_app_args)}.",
|
|
832
|
-
fix_hint="Use --apps-file for batch mode (each item contains app_key + per-app params), or remove --apps-file for single-app mode.",
|
|
833
|
-
)
|
|
834
|
-
if apps:
|
|
835
|
-
return context.builder.app_charts_apply(
|
|
836
|
-
profile=args.profile,
|
|
837
|
-
app_key="",
|
|
838
|
-
upsert_charts=[],
|
|
839
|
-
patch_charts=[],
|
|
840
|
-
remove_chart_ids=[],
|
|
841
|
-
reorder_chart_ids=[],
|
|
842
|
-
apps=apps,
|
|
843
|
-
)
|
|
844
|
-
if not args.app_key:
|
|
845
|
-
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")
|
|
846
871
|
return context.builder.app_charts_apply(
|
|
847
872
|
profile=args.profile,
|
|
848
873
|
app_key=args.app_key,
|
|
@@ -850,30 +875,11 @@ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
850
875
|
patch_charts=load_list_arg(args.patch_file, option_name="--patch-file"),
|
|
851
876
|
remove_chart_ids=load_list_arg(args.remove_chart_ids_file, option_name="--remove-chart-ids-file"),
|
|
852
877
|
reorder_chart_ids=load_list_arg(args.reorder_chart_ids_file, option_name="--reorder-chart-ids-file"),
|
|
878
|
+
apps=apps,
|
|
853
879
|
)
|
|
854
880
|
|
|
855
881
|
|
|
856
882
|
def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
857
|
-
patch_sections = load_list_arg(getattr(args, "patch_sections_file", None), option_name="--patch-sections-file")
|
|
858
|
-
if patch_sections:
|
|
859
|
-
if not (args.dash_key or "").strip():
|
|
860
|
-
raise_config_error("portal apply --patch-sections-file requires --dash-key", fix_hint="Pass --dash-key DASH_KEY to identify the portal to patch")
|
|
861
|
-
has_sections_file = bool(getattr(args, "sections_file", None))
|
|
862
|
-
has_payload_file = bool(getattr(args, "payload_file", None))
|
|
863
|
-
if has_sections_file or has_payload_file:
|
|
864
|
-
raise_config_error(
|
|
865
|
-
"portal apply --patch-sections-file cannot be combined with --sections-file or --payload-file.",
|
|
866
|
-
fix_hint="Use --patch-sections-file alone to patch specific sections, or --sections-file to replace all sections.",
|
|
867
|
-
)
|
|
868
|
-
return context.builder.portal_apply(
|
|
869
|
-
profile=args.profile,
|
|
870
|
-
dash_key=args.dash_key,
|
|
871
|
-
dash_name="",
|
|
872
|
-
package_id=None,
|
|
873
|
-
publish=bool(args.publish),
|
|
874
|
-
sections=[],
|
|
875
|
-
patch_sections=patch_sections,
|
|
876
|
-
)
|
|
877
883
|
payload = load_object_arg(args.payload_file, option_name="--payload-file") if getattr(args, "payload_file", None) else None
|
|
878
884
|
payload_obj = payload if isinstance(payload, dict) else {}
|
|
879
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()
|
|
@@ -892,6 +898,7 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
892
898
|
fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-id --dash-name`.",
|
|
893
899
|
)
|
|
894
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")
|
|
895
902
|
return context.builder.portal_apply(
|
|
896
903
|
profile=args.profile,
|
|
897
904
|
dash_key=args.dash_key,
|
|
@@ -908,17 +915,28 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
908
915
|
dash_global_config=load_object_arg(args.dash_global_config_file, option_name="--dash-global-config-file"),
|
|
909
916
|
config=load_object_arg(args.config_file, option_name="--config-file"),
|
|
910
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,
|
|
911
926
|
)
|
|
912
927
|
|
|
913
928
|
|
|
914
929
|
def _handle_publish_verify(args: argparse.Namespace, context: CliContext) -> dict:
|
|
915
|
-
app_keys =
|
|
916
|
-
if app_keys:
|
|
930
|
+
app_keys = _app_keys_from_args(args)
|
|
931
|
+
if app_keys is not None:
|
|
917
932
|
return context.builder.app_publish_verify(
|
|
918
933
|
profile=args.profile,
|
|
934
|
+
app_key="",
|
|
919
935
|
app_keys=app_keys,
|
|
920
936
|
expected_package_id=args.expected_package_id,
|
|
921
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")
|
|
922
940
|
return context.builder.app_publish_verify(
|
|
923
941
|
profile=args.profile,
|
|
924
942
|
app_key=args.app_key,
|