@shundoo-ai/dsh-cosmic 1.0.19 → 1.0.21
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/cordis.patch.yml +5 -5
- package/package.json +1 -1
- package/skills/cosmic-build/CHANGELOG.md +4 -0
- package/skills/cosmic-build/LOCAL-PATCHES.md +91 -40
- package/skills/cosmic-build/SKILL.md +46 -13
- package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +169 -0
- package/skills/cosmic-build/assets/platform-report-filters/platform.patch +98 -0
- package/skills/cosmic-build/assets/report-algo-benchmark/ReportAlgoBenchmark.java +48 -0
- package/skills/cosmic-build/assets/report-algo-benchmark/ReportBenchmarkLogFactory.java +7 -0
- package/skills/cosmic-build/examples/java-rules/CosmicEntryQuery.java +26 -0
- package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +35 -0
- package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +77 -0
- package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapter.java +25 -0
- package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapterCheck.java +26 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/api/algo/GroupbyDataSet.md +47 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterInfo.md +12 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterItemInfo.md +2 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +2 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/java-type-contracts.md +11 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +18 -14
- package/skills/cosmic-build/knowledge/cosmic/platform/operation-plugin.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +17 -9
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/cross-entity-aggregate-writeback.md +2 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/pre-post-operation.md +15 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +24 -68
- package/skills/cosmic-build/knowledge/cosmic/platform/workflow-plugin.md +2 -7
- package/skills/cosmic-build/references/application-context.md +35 -1
- package/skills/cosmic-build/references/common-reference.md +1 -1
- package/skills/cosmic-build/references/compile-guide.md +55 -68
- package/skills/cosmic-build/references/delivery.md +59 -7
- package/skills/cosmic-build/references/execution-contract.md +38 -0
- package/skills/cosmic-build/references/form-discovery.md +7 -1
- package/skills/cosmic-build/references/java-plugin-delivery.md +32 -162
- package/skills/cosmic-build/references/operation-dispatch.md +17 -0
- package/skills/cosmic-build/references/plugin-handoff-input.md +131 -0
- package/skills/cosmic-build/references/plugin-runtime-routing.md +1 -1
- package/skills/cosmic-build/references/report-filter-contract.md +78 -0
- package/skills/cosmic-build/references/sdk-inspection.md +23 -0
- package/skills/cosmic-build/schemas/data-model-authoring.schema.json +2 -3
- package/skills/cosmic-build/scripts/_http_client.py +55 -55
- package/skills/cosmic-build/scripts/appconfig_load.py +2 -2
- package/skills/cosmic-build/scripts/apply_report_filter_extension.py +39 -0
- package/skills/cosmic-build/scripts/bootstrap_cosmic_context.py +37 -2
- package/skills/cosmic-build/scripts/compile_plugin_source.py +279 -156
- package/skills/cosmic-build/scripts/execution_binding.py +40 -0
- package/skills/cosmic-build/scripts/inspect_platform_sdk.py +169 -0
- package/skills/cosmic-build/scripts/kd_api.py +23 -22
- package/skills/cosmic-build/scripts/kd_auth.py +1 -1
- package/skills/cosmic-build/scripts/kd_env_loader.py +19 -10
- package/skills/cosmic-build/scripts/kd_token_cache.py +2 -2
- package/skills/cosmic-build/scripts/kingscript_sdk_sync.py +350 -0
- package/skills/cosmic-build/scripts/kingscript_source_gate.py +11 -8
- package/skills/cosmic-build/scripts/platform_api_contract.py +246 -11
- package/skills/cosmic-build/scripts/plugin_corpus/build_index.py +403 -0
- package/skills/cosmic-build/scripts/plugin_corpus/jvm_classfile.py +281 -0
- package/skills/cosmic-build/scripts/plugin_corpus/measure_api_usage.py +446 -0
- package/skills/cosmic-build/scripts/plugin_source_gate.py +48 -7
- package/skills/cosmic-build/scripts/plugin_target_sdk.py +45 -0
- package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +493 -47
- package/skills/cosmic-build/scripts/preview_url.py +35 -6
- package/skills/cosmic-build/scripts/query_plugin_handoff.py +250 -0
- package/skills/cosmic-build/scripts/register_plugins.py +28 -2
- package/skills/cosmic-build/scripts/render_output_cards.py +42 -9
- package/skills/cosmic-build/scripts/report_algo_benchmark.py +46 -0
- package/skills/cosmic-build/scripts/run.py +2 -2
- package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +43 -0
- package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +64 -0
- package/skills/cosmic-build/scripts/test/fixtures/java_context_booking_native.json +541 -0
- package/skills/cosmic-build/scripts/test/test_add_rule_business_contract.py +85 -14
- package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +12 -3
- package/skills/cosmic-build/scripts/test/test_build_meta_order.py +0 -16
- package/skills/cosmic-build/scripts/test/test_cli_continuation.py +87 -0
- package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +224 -3
- package/skills/cosmic-build/scripts/test/test_control_authoring.py +123 -4
- package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +141 -0
- package/skills/cosmic-build/scripts/test/test_data_model_contract.py +30 -5
- package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +2 -10
- package/skills/cosmic-build/scripts/test/test_dym_sync_background.py +11 -0
- package/skills/cosmic-build/scripts/test/test_dynamic_form_metadata_only_contract.py +59 -1
- package/skills/cosmic-build/scripts/test/test_guide_skeleton_contract.py +87 -43
- package/skills/cosmic-build/scripts/test/test_inspect_platform_sdk.py +71 -0
- package/skills/cosmic-build/scripts/test/test_kd_api.py +67 -19
- package/skills/cosmic-build/scripts/test/test_kingscript_sdk_sync.py +221 -0
- package/skills/cosmic-build/scripts/test/test_kingscript_source_gate.py +10 -0
- package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +18 -0
- package/skills/cosmic-build/scripts/test/test_long_session_regressions.py +116 -0
- package/skills/cosmic-build/scripts/test/test_mcp_cli.py +524 -0
- package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +141 -0
- package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +36 -2
- package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +161 -1
- package/skills/cosmic-build/scripts/test/test_plugin_business_contract.py +91 -0
- package/skills/cosmic-build/scripts/test/test_plugin_corpus.py +195 -0
- package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +346 -1
- package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +70 -0
- package/skills/cosmic-build/scripts/test/test_preview_url.py +44 -1
- package/skills/cosmic-build/scripts/test/test_property_batch.py +22 -0
- package/skills/cosmic-build/scripts/test/test_property_input_discovery.py +13 -11
- package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +160 -0
- package/skills/cosmic-build/scripts/test/test_register_plugins.py +31 -1
- package/skills/cosmic-build/scripts/test/test_render_output_cards.py +50 -4
- package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -0
- package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +5 -4
- package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +36 -0
- package/skills/cosmic-build/scripts/test/test_rule_batch_evidence.py +1 -1
- package/skills/cosmic-build/scripts/test/test_rule_domain_contract.py +20 -0
- package/skills/cosmic-build/scripts/test/test_rule_function_formula.py +16 -6
- package/skills/cosmic-build/scripts/test/test_skill_entry_contract.py +1 -0
- package/skills/cosmic-build/scripts/test/test_test_environment.py +45 -0
- package/skills/cosmic-build/scripts/test/test_verify_plugin_loading.py +79 -0
- package/skills/cosmic-build/scripts/test_environment.py +49 -0
- package/skills/cosmic-build/scripts/verify_plugin_loading.py +71 -0
- package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +86 -162
- package/skills/cosmic-build/skills/cosmic-coding-script/ATTRIBUTION.md +1 -1
- package/skills/cosmic-build/skills/cosmic-coding-script/SKILL.md +45 -14
- package/skills/cosmic-build/skills/cosmic-coding-script/references/kwc-controller-authoring.md +6 -0
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/README.md +29 -19
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/docs//350/204/232/346/234/254SDK.md +3 -4
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/indexes/package-index.md +1 -1
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/strategy.md +27 -18
- package/skills/cosmic-build/skills/cosmic-meta-api/SKILL.md +11 -17
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/app_menu.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/meta_log.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/control-authoring.json +45 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/form-control-traits.json +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/layout-ui.json +18 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/menu-publish.json +8 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-batch.json +7 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-input-index.json +3 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +1 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +37 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-event-contract.json +24 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/run-build-meta.json +61 -12
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/dropdown-menu.schema.json +69 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +2 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-set.schema.json +1 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-write.schema.json +14 -7
- package/skills/cosmic-build/skills/cosmic-meta-api/docs/api-summary.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/app-action/SKILL.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/develop/SKILL.md +1 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/export/SKILL.md +5 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/init/SKILL.md +13 -36
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/SKILL.md +8 -9
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/config.json.example +5 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/reset/SKILL.md +6 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/usage/SKILL.md +20 -148
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/build_receipts.py +171 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +25 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_controls.py +55 -21
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_fields.py +10 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +63 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_forms.py +59 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +8 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +36 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_plugins.py +115 -90
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +666 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +1415 -87
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/commands.py +6 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +28 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +40 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +32 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_policy_contract.py +6 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +113 -17
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/reset.py +3 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/rule_contract.py +107 -26
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/build_meta_order.py +50 -49
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +856 -150
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +47 -14
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +374 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_cli_failure_contract.py +24 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_creation_identity_boundary.py +4 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_dropdown_menu_contract.py +382 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_existing_form_ui.py +0 -25
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_field_decorator.py +50 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_functional_group_resolution.py +83 -16
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_identity_template_resolution.py +103 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +28 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +22 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +65 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +1041 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +65 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +17 -0
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/api-param-checklist.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/rule-modeling-guide.md +7 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +61 -13
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/app-menu/SKILL.md +8 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/SKILL.md +5 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/dynamic-form-artifact-schema.md +5 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +21 -9
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-entity-graphs.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-storage-constraints.md +5 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-ui-composition.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/creation-recovery.md +13 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/parse-core.md +3 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +6 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +135 -87
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/field_type_registry.py +11 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/isv_resolver.py +1 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +29 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +4 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/context/SKILL.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/execute/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/SKILL.md +5 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/operation-write.md +6 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +20 -5
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/complex-apis.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/policy-grammar.md +4 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +9 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +61 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/raw-rule-authoring.md +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-actions.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-reference.md +6 -6
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/state.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/unit/SKILL.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +4 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +4 -1
- package/skills/cosmic-build/skills/cosmic-meta-ui/SKILL.md +9 -1
- package/skills/cosmic-build/skills/cosmic-meta-ui/modules/button/SKILL.md +3 -0
- package/skills/cosmic-build/skills/mcp-server/SKILL.md +210 -0
- package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +431 -0
- package/skills/cosmic-build/skills/openapi-gen/SKILL.md +3 -3
- package/skills/cosmic-build/skills/openapi-gen/scripts/api/gen_runner.py +2 -2
- package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +19 -9
- package/skills/cosmic-dev-best-practices/references/cosmic-meta-api.md +14 -11
- package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +1 -1
- package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/AssistantField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/BasedataField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/BasedataPropField.md +3 -3
- package/skills/cosmic-dev-best-practices/references/elements/CreaterField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/CurrencyField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/FlexField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/MaterielField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/OrgField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/UnitField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/UserField.md +3 -3
- package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +16 -11
- package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +5 -4
- package/skills/erp-analysis/references/delivery-contract.md +4 -2
- package/skills/erp-analysis/templates/AGENTS.template.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/.dsh/cosmic/.kd/config.json +0 -21
|
@@ -51,6 +51,8 @@ import json
|
|
|
51
51
|
import argparse
|
|
52
52
|
import builtins
|
|
53
53
|
import hashlib
|
|
54
|
+
import shlex
|
|
55
|
+
import subprocess
|
|
54
56
|
from datetime import datetime, timezone
|
|
55
57
|
|
|
56
58
|
# Windows default stdout is GBK, cannot output ⚠️ and other Unicode chars, force UTF-8
|
|
@@ -95,6 +97,7 @@ from _core.validators import ( # noqa: F401
|
|
|
95
97
|
)
|
|
96
98
|
|
|
97
99
|
from field_decorator import ( # noqa: F401
|
|
100
|
+
make_entity_key_for,
|
|
98
101
|
decorate_artifact,
|
|
99
102
|
decorate_ops,
|
|
100
103
|
validate_dynamic_form_number,
|
|
@@ -307,6 +310,14 @@ def _append_meta_event(phase, **payload):
|
|
|
307
310
|
|
|
308
311
|
|
|
309
312
|
def _summarize_build_meta_result(result, artifact=None):
|
|
313
|
+
if isinstance(result, dict) and result.get("status") is False and result.get("writeAttempted") is False:
|
|
314
|
+
summary = {"ok": False, "successCount": 0, "failedCount": 1, "formNumbers": [],
|
|
315
|
+
"errors": [_extract_error_message(result) or result.get("errorCode")],
|
|
316
|
+
"errorCode": result.get("errorCode"), "writeAttempted": False}
|
|
317
|
+
if result.get("errorCode") == "TARGET_ALREADY_EXISTS":
|
|
318
|
+
_attach_target_exists_contract(summary, (result.get("data") or {}).get("conflicts", []))
|
|
319
|
+
summary["writeAttempted"] = False
|
|
320
|
+
return summary
|
|
310
321
|
data = result.get("data") if isinstance(result, dict) else {}
|
|
311
322
|
data = data if isinstance(data, dict) else {}
|
|
312
323
|
entity_results = data.get("entityResults") or []
|
|
@@ -673,6 +684,79 @@ def _dynamic_form_identity_preflight(artifact):
|
|
|
673
684
|
}
|
|
674
685
|
|
|
675
686
|
|
|
687
|
+
def _resolve_dynamic_reference_fields(artifact, *, app_root=None, isv="", query_forms=None, entity_exists=None):
|
|
688
|
+
"""Use the entity-authoring resolver for dynamic-form reference fields."""
|
|
689
|
+
entities = artifact.get("entities") or []
|
|
690
|
+
if not entities or entities[0].get("type") != "MainEntity":
|
|
691
|
+
return None
|
|
692
|
+
modeling = os.path.abspath(os.path.join(SCRIPT_DIR, "../../cosmic-meta-modeling/scripts"))
|
|
693
|
+
if modeling not in sys.path:
|
|
694
|
+
sys.path.insert(0, modeling)
|
|
695
|
+
from basedata_resolver import BasedataResolver
|
|
696
|
+
resolver = BasedataResolver(app_root=app_root, isv=isv, query_forms=query_forms, entity_exists=entity_exists)
|
|
697
|
+
resolved = {}
|
|
698
|
+
pending = []
|
|
699
|
+
for entity in entities:
|
|
700
|
+
for field in entity.get("fields") or []:
|
|
701
|
+
if field.get("dataType") not in {"BasedataField", "MulBasedataField"}:
|
|
702
|
+
continue
|
|
703
|
+
reference = field.get("reference") or {}
|
|
704
|
+
selector = str(reference.get("formNumber") or field.get("prdRef") or field.get("basedataNumber") or "").strip()
|
|
705
|
+
if reference and reference.get("mode") != "platform":
|
|
706
|
+
pending.append({"field": field.get("bizWord"), "reason": "dynamic_reference_requires_platform_target"})
|
|
707
|
+
continue
|
|
708
|
+
if not selector:
|
|
709
|
+
pending.append({"field": field.get("bizWord"), "reason": "reference_target_missing"})
|
|
710
|
+
continue
|
|
711
|
+
if selector not in resolved:
|
|
712
|
+
resolved[selector] = resolver.resolve(prd_ref=selector)
|
|
713
|
+
target = resolved[selector]
|
|
714
|
+
if target.is_ok:
|
|
715
|
+
field["basedataNumber"] = target.number
|
|
716
|
+
continue
|
|
717
|
+
pending.append({"field": field.get("bizWord"), "selector": selector, "reason": target.reason,
|
|
718
|
+
"status": target.status, "candidates": [item.to_dict() for item in target.candidates]})
|
|
719
|
+
if not pending:
|
|
720
|
+
return None
|
|
721
|
+
return {
|
|
722
|
+
"status": False, "errorCode": "DYNAMIC_REFERENCE_TARGET_UNRESOLVED",
|
|
723
|
+
"message": "Resolve declared reference targets before creation; preserve the requested reference field types.",
|
|
724
|
+
"writeAttempted": False, "metadataChanged": False, "retryAdvice": "resolve_reference_target",
|
|
725
|
+
"data": {"unresolvedReferences": pending},
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
def _dynamic_creation_receipt(app_root, artifact_path, result=None):
|
|
730
|
+
"""Retain a partial creation receipt for the same request across input changes."""
|
|
731
|
+
if not app_root:
|
|
732
|
+
return None
|
|
733
|
+
identity = os.path.normcase(os.path.abspath(artifact_path))
|
|
734
|
+
key = hashlib.sha256(identity.encode("utf-8")).hexdigest()[:24]
|
|
735
|
+
receipt = os.path.join(app_root, "_runtime", "cosmic-metadata", "output", "retained-run-" + key + ".json")
|
|
736
|
+
if result is not None:
|
|
737
|
+
if result.get("metadataRetained") is not True or result.get("writeAttempted") is not True:
|
|
738
|
+
return None
|
|
739
|
+
retained = dict(result)
|
|
740
|
+
retained.update(status=False, errorCode="DYNAMIC_FORM_RECOVERY_REQUIRED", doNotRetryRun=True,
|
|
741
|
+
terminal=True, retryable=False, retryAdvice="inspect_retained_form", artifactPath=identity,
|
|
742
|
+
allowedNextActions=["inspect_retained_form"])
|
|
743
|
+
rows = (result.get("data") or {}).get("entityResults") or []
|
|
744
|
+
retained["reconciliation"] = [{
|
|
745
|
+
"command": "getFormSchema", "formId": row["formId"],
|
|
746
|
+
"arguments": ["--stdout", "getFormSchema", "--app-root", os.path.abspath(app_root), "--formId", row["formId"]],
|
|
747
|
+
} for row in rows if isinstance(row, dict) and row.get("formId")]
|
|
748
|
+
retained["modelInstruction"] = "This creation request already wrote metadata. Inspect the returned formId and repair the existing form; changing its artifact does not authorize another creation."
|
|
749
|
+
os.makedirs(os.path.dirname(receipt), exist_ok=True)
|
|
750
|
+
with open(receipt, "w", encoding="utf-8") as stream:
|
|
751
|
+
json.dump(retained, stream, ensure_ascii=False, indent=2)
|
|
752
|
+
return retained
|
|
753
|
+
if not os.path.isfile(receipt):
|
|
754
|
+
return None
|
|
755
|
+
with open(receipt, "r", encoding="utf-8") as stream:
|
|
756
|
+
retained = json.load(stream)
|
|
757
|
+
return {**retained, "writeAttempted": False, "metadataChanged": False}
|
|
758
|
+
|
|
759
|
+
|
|
676
760
|
def _dynamic_form_artifact_preflight(artifact):
|
|
677
761
|
"""The public run command enforces the same dynamic contract as the authoring validator."""
|
|
678
762
|
roots = artifact.get("entities") or []
|
|
@@ -765,6 +849,7 @@ def _apply_build_failure_contract(payload, summary):
|
|
|
765
849
|
if summary.get("errorCode") not in (
|
|
766
850
|
"TARGET_ALREADY_EXISTS", "FUNCTIONAL_GROUP_MOUNT_INCOMPLETE",
|
|
767
851
|
"DYNAMIC_FORM_IDENTITY_DRIFT", "POST_BUILD_CONTINUATION_REQUIRED",
|
|
852
|
+
"POST_BUILD_PHASE_FAILED",
|
|
768
853
|
):
|
|
769
854
|
return payload
|
|
770
855
|
payload["status"] = False
|
|
@@ -776,12 +861,58 @@ def _apply_build_failure_contract(payload, summary):
|
|
|
776
861
|
"doNotRetryBuildMeta", "targetConflicts", "unitMountFailures",
|
|
777
862
|
"requestedFormNumber", "actualFormNumbers",
|
|
778
863
|
"allowedNextActions", "continuation", "continuations", "modelInstruction",
|
|
864
|
+
"retainedTargets", "reconciliation", "repairPlans", "failedPhase",
|
|
865
|
+
"notStartedArtifacts",
|
|
779
866
|
):
|
|
780
867
|
if key in summary:
|
|
781
868
|
payload[key] = summary[key]
|
|
782
869
|
return payload
|
|
783
870
|
|
|
784
871
|
|
|
872
|
+
class _PostBuildWriteError(ValueError):
|
|
873
|
+
def __init__(self, message, result, pending_write):
|
|
874
|
+
super().__init__(message)
|
|
875
|
+
self.result = result
|
|
876
|
+
self.pending_write = pending_write
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def _attach_post_build_phase_failure(summary, payload, *, phase, app_root,
|
|
880
|
+
artifact_path, pending_write=None):
|
|
881
|
+
"""A successful root save survives a later write or verification failure."""
|
|
882
|
+
targets = [
|
|
883
|
+
{"formId": row["formId"],
|
|
884
|
+
"formNumber": row.get("formNumber") or row.get("entityKey"),
|
|
885
|
+
"formName": row.get("formName") or row.get("displayName"),
|
|
886
|
+
"failedPhase": phase}
|
|
887
|
+
for row in ((payload or {}).get("data") or {}).get("entityResults", [])
|
|
888
|
+
if isinstance(row, dict) and row.get("formId")
|
|
889
|
+
and (row.get("formNumber") or row.get("entityKey"))
|
|
890
|
+
]
|
|
891
|
+
if not targets:
|
|
892
|
+
return summary
|
|
893
|
+
summary.update({
|
|
894
|
+
"ok": False, "errorCode": "POST_BUILD_PHASE_FAILED",
|
|
895
|
+
"message": f"The form exists; phase {phase} requires reconciliation before repair",
|
|
896
|
+
"failedPhase": phase, "terminal": True, "retryable": False,
|
|
897
|
+
"writeAttempted": True, "metadataChanged": True, "metadataRetained": True,
|
|
898
|
+
"doNotRetryBuildMeta": True, "retryAdvice": "inspect_retained_form",
|
|
899
|
+
"retainedTargets": targets,
|
|
900
|
+
"reconciliation": [{"command": "getFormSchema", "appRoot": app_root,
|
|
901
|
+
"input": {"formId": target["formId"],
|
|
902
|
+
"formNumber": target["formNumber"]}}
|
|
903
|
+
for target in targets],
|
|
904
|
+
"repairPlans": [{
|
|
905
|
+
"failedPhase": phase, "appRoot": app_root,
|
|
906
|
+
"artifactPath": os.path.abspath(artifact_path),
|
|
907
|
+
"pendingWrites": [pending_write] if pending_write else [],
|
|
908
|
+
"precondition": "Read the retained form first. Reuse matching elements; execute only writes proven absent. Stop on mismatched identities or unknown write outcomes.",
|
|
909
|
+
}],
|
|
910
|
+
"allowedNextActions": ["getFormSchema", "getProperties", "listElements"],
|
|
911
|
+
"modelInstruction": "Preserve the saved form and the original artifact. Inspect the exact retained target before a narrow existing-form repair; never rerun emit, runBatch or buildMeta for this artifact. Reconcile every failed or unknown post-write before retrying. The batch results use ok and phase, not success.",
|
|
912
|
+
})
|
|
913
|
+
return summary
|
|
914
|
+
|
|
915
|
+
|
|
785
916
|
def _apply_dynamic_ui_failure_contract(payload, summary, error):
|
|
786
917
|
"""Expose a retained post-build UI failure on the public command envelope."""
|
|
787
918
|
if not isinstance(payload, dict) or not isinstance(summary, dict):
|
|
@@ -995,6 +1126,7 @@ def _summarize_run_batch(summary):
|
|
|
995
1126
|
target_conflicts = []
|
|
996
1127
|
unit_mount_failures = []
|
|
997
1128
|
continuations = []
|
|
1129
|
+
post_build_failures = []
|
|
998
1130
|
for item in results:
|
|
999
1131
|
if not isinstance(item, dict):
|
|
1000
1132
|
continue
|
|
@@ -1013,6 +1145,8 @@ def _summarize_run_batch(summary):
|
|
|
1013
1145
|
continuation = one.get("continuation")
|
|
1014
1146
|
if isinstance(continuation, dict):
|
|
1015
1147
|
continuations.append(continuation)
|
|
1148
|
+
if one.get("errorCode") == "POST_BUILD_PHASE_FAILED":
|
|
1149
|
+
post_build_failures.append(one)
|
|
1016
1150
|
if not item.get("ok"):
|
|
1017
1151
|
errors.extend(str(e) for e in (item.get("errors") or [])[:3 - len(errors)])
|
|
1018
1152
|
if continuation is None:
|
|
@@ -1053,8 +1187,23 @@ def _summarize_run_batch(summary):
|
|
|
1053
1187
|
}
|
|
1054
1188
|
result = _attach_functional_group_mount_contract(result, unit_mount_failures)
|
|
1055
1189
|
result = _attach_target_exists_contract(result, target_conflicts)
|
|
1056
|
-
if
|
|
1190
|
+
if post_build_failures and result.get("errorCode") not in {
|
|
1057
1191
|
"TARGET_ALREADY_EXISTS", "FUNCTIONAL_GROUP_MOUNT_INCOMPLETE",
|
|
1192
|
+
}:
|
|
1193
|
+
result.update({key: value for key, value in post_build_failures[0].items()
|
|
1194
|
+
if key in {"errorCode", "message", "terminal", "retryable",
|
|
1195
|
+
"retryAdvice", "writeAttempted", "metadataChanged",
|
|
1196
|
+
"metadataRetained", "doNotRetryBuildMeta",
|
|
1197
|
+
"allowedNextActions", "modelInstruction"}})
|
|
1198
|
+
for key in ("retainedTargets", "reconciliation", "repairPlans"):
|
|
1199
|
+
result[key] = [value for failure in post_build_failures for value in failure.get(key, [])]
|
|
1200
|
+
result["notStartedArtifacts"] = [item["artifactPath"] for item in results
|
|
1201
|
+
if item.get("writeAttempted") is False
|
|
1202
|
+
and item.get("artifactPath")]
|
|
1203
|
+
if continuations:
|
|
1204
|
+
result["continuations"] = continuations
|
|
1205
|
+
if continuations and result.get("errorCode") not in {
|
|
1206
|
+
"TARGET_ALREADY_EXISTS", "FUNCTIONAL_GROUP_MOUNT_INCOMPLETE", "POST_BUILD_PHASE_FAILED",
|
|
1058
1207
|
}:
|
|
1059
1208
|
result.update({
|
|
1060
1209
|
"ok": False,
|
|
@@ -1079,6 +1228,11 @@ def _summarize_run_batch(summary):
|
|
|
1079
1228
|
"once against its retained form, then continue the remaining metadata scope."
|
|
1080
1229
|
),
|
|
1081
1230
|
})
|
|
1231
|
+
attempts = [item.get("writeAttempted", (item.get("summary") or {}).get("writeAttempted",
|
|
1232
|
+
(item.get("result") or {}).get("writeAttempted"))) for item in results if isinstance(item, dict)]
|
|
1233
|
+
if attempts:
|
|
1234
|
+
result["writeAttempted"] = (True if any(value is True for value in attempts)
|
|
1235
|
+
else False if all(value is False for value in attempts) else None)
|
|
1082
1236
|
return result
|
|
1083
1237
|
|
|
1084
1238
|
|
|
@@ -1242,6 +1396,108 @@ def _resolve_app_root_arg(args):
|
|
|
1242
1396
|
return os.path.abspath(raw)
|
|
1243
1397
|
|
|
1244
1398
|
|
|
1399
|
+
def _normalize_key_property(args, properties):
|
|
1400
|
+
result = dict(properties)
|
|
1401
|
+
selector = "Key" if "Key" in result else "key" if "key" in result else None
|
|
1402
|
+
if not selector:
|
|
1403
|
+
return result
|
|
1404
|
+
workspace = _load_workspace_at(_resolve_app_root_arg(args)) or {}
|
|
1405
|
+
standard = workspace.get("standard")
|
|
1406
|
+
if isinstance(standard, str):
|
|
1407
|
+
standard = standard.strip().lower() == "true"
|
|
1408
|
+
result[selector] = make_entity_key_for(
|
|
1409
|
+
str(result[selector] or "").strip(), str(workspace.get("isv") or ""),
|
|
1410
|
+
standard=standard)
|
|
1411
|
+
return result
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
def _rename_element_key(cfg, token, args):
|
|
1415
|
+
"""Rename one metadata key and migrate observable rule string references."""
|
|
1416
|
+
old_key = resolve_element_selector(cfg, token, args.formNumber, args.element)
|
|
1417
|
+
normalized = _normalize_key_property(args, {"Key": args.new_key})
|
|
1418
|
+
new_key = str(normalized["Key"] or "").strip()
|
|
1419
|
+
if not new_key or new_key == old_key:
|
|
1420
|
+
return {
|
|
1421
|
+
"status": True, "errorCode": "0", "writeAttempted": False,
|
|
1422
|
+
"data": {"alreadySatisfied": True, "changed": False, "formNumber": args.formNumber,
|
|
1423
|
+
"oldKey": old_key, "newKey": new_key},
|
|
1424
|
+
"verification": {"verified": True, "preservationVerified": True},
|
|
1425
|
+
}
|
|
1426
|
+
before_elements = do_list_elements(cfg, token, args.formNumber)
|
|
1427
|
+
before_rows = before_elements.get("data") if _is_api_success(before_elements) else []
|
|
1428
|
+
before = next((row for row in before_rows if isinstance(row, dict)
|
|
1429
|
+
and str(row.get("key") or "") == old_key), None)
|
|
1430
|
+
if before is None:
|
|
1431
|
+
return {"status": False, "errorCode": "RENAME_TARGET_NOT_FOUND",
|
|
1432
|
+
"message": "The exact current element is absent; nothing was written",
|
|
1433
|
+
"writeAttempted": False}
|
|
1434
|
+
rules_result = do_list_rules(cfg, token, args.formNumber)
|
|
1435
|
+
if not _is_api_success(rules_result):
|
|
1436
|
+
return {"status": False, "errorCode": "RENAME_REFERENCE_READ_FAILED",
|
|
1437
|
+
"message": "Cannot inspect rule references before renaming; nothing was written",
|
|
1438
|
+
"writeAttempted": False, "data": {"cause": rules_result}}
|
|
1439
|
+
pattern = re.compile(rf"(?<![A-Za-z0-9_]){re.escape(old_key)}(?![A-Za-z0-9_])")
|
|
1440
|
+
patches = []
|
|
1441
|
+
for rule in (rules_result.get("data") or {}).get("rules") or []:
|
|
1442
|
+
if not isinstance(rule, dict):
|
|
1443
|
+
continue
|
|
1444
|
+
patch = {
|
|
1445
|
+
"ruleType": rule.get("ruleType"), "ruleId": rule.get("ruleId"),
|
|
1446
|
+
}
|
|
1447
|
+
for source in ("preCondition", "triggerField"):
|
|
1448
|
+
value = rule.get(source)
|
|
1449
|
+
if isinstance(value, str) and pattern.search(value):
|
|
1450
|
+
patch[source] = pattern.sub(new_key, value)
|
|
1451
|
+
if len(patch) > 2:
|
|
1452
|
+
patches.append(patch)
|
|
1453
|
+
written = do_set_properties_verified(
|
|
1454
|
+
cfg, token, args.formNumber, old_key, {"Key": new_key})
|
|
1455
|
+
if not _is_api_success(written) or not (written.get("verification") or {}).get("verified"):
|
|
1456
|
+
return written
|
|
1457
|
+
migrated = []
|
|
1458
|
+
for patch in patches:
|
|
1459
|
+
updated = do_update_rule_verified(cfg, token, args.formNumber, patch)
|
|
1460
|
+
if not _is_api_success(updated) or not (updated.get("verification") or {}).get("verified"):
|
|
1461
|
+
rollback = do_set_properties_verified(
|
|
1462
|
+
cfg, token, args.formNumber, new_key, {"Key": old_key})
|
|
1463
|
+
return {
|
|
1464
|
+
"status": False, "errorCode": "RENAME_REFERENCE_MIGRATION_FAILED",
|
|
1465
|
+
"message": "Key changed but a rule reference migration failed; rollback was attempted",
|
|
1466
|
+
"writeAttempted": True, "metadataRetained": not (
|
|
1467
|
+
_is_api_success(rollback) and (rollback.get("verification") or {}).get("verified")),
|
|
1468
|
+
"data": {"failedRule": patch, "ruleResult": updated, "rollback": rollback},
|
|
1469
|
+
}
|
|
1470
|
+
migrated.append(str(patch.get("ruleId") or ""))
|
|
1471
|
+
after_elements = do_list_elements(cfg, token, args.formNumber)
|
|
1472
|
+
after_rows = after_elements.get("data") if _is_api_success(after_elements) else []
|
|
1473
|
+
after = next((row for row in after_rows if isinstance(row, dict)
|
|
1474
|
+
and str(row.get("key") or "") == new_key), None)
|
|
1475
|
+
preserved = bool(after) and all(
|
|
1476
|
+
before.get(key) == after.get(key) for key in ("name", "type", "parentKey", "index")
|
|
1477
|
+
)
|
|
1478
|
+
final_rules = do_list_rules(cfg, token, args.formNumber)
|
|
1479
|
+
stale = [
|
|
1480
|
+
str(rule.get("ruleId") or "") for rule in ((final_rules.get("data") or {}).get("rules") or [])
|
|
1481
|
+
if isinstance(rule, dict) and any(
|
|
1482
|
+
isinstance(rule.get(key), str) and pattern.search(rule[key])
|
|
1483
|
+
for key in ("preCondition", "triggerField")
|
|
1484
|
+
)
|
|
1485
|
+
] if _is_api_success(final_rules) else ["<read-failed>"]
|
|
1486
|
+
verified = after is not None and preserved and not stale
|
|
1487
|
+
return {
|
|
1488
|
+
"status": verified, "errorCode": "0" if verified else "RENAME_READBACK_MISMATCH",
|
|
1489
|
+
"message": None if verified else "Renamed key did not preserve the requested observable state",
|
|
1490
|
+
"writeAttempted": True, "metadataRetained": True,
|
|
1491
|
+
"data": {"changed": True, "verified": verified, "formNumber": args.formNumber,
|
|
1492
|
+
"oldKey": old_key, "newKey": new_key, "migratedRuleIds": migrated,
|
|
1493
|
+
"staleRuleIds": stale},
|
|
1494
|
+
"verification": {"verified": verified, "preservationVerified": preserved,
|
|
1495
|
+
"oldKeyAbsent": not any(isinstance(row, dict)
|
|
1496
|
+
and str(row.get("key") or "") == old_key for row in after_rows),
|
|
1497
|
+
"newKeyPresent": after is not None, "staleRuleIds": stale},
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
|
|
1245
1501
|
def _assert_workspace_matches_archive(app_root, workspace):
|
|
1246
1502
|
"""Fail closed before a platform write if appRoot and workspace identities drift."""
|
|
1247
1503
|
if not app_root:
|
|
@@ -1311,6 +1567,15 @@ def _assert_platform_target_matches_archive(
|
|
|
1311
1567
|
)
|
|
1312
1568
|
|
|
1313
1569
|
|
|
1570
|
+
def _unit_app_target(app_root, biz_app_id=None, biz_app_number=None):
|
|
1571
|
+
if not app_root:
|
|
1572
|
+
return biz_app_id, biz_app_number
|
|
1573
|
+
workspace = _load_workspace_at(app_root)
|
|
1574
|
+
_assert_platform_target_matches_archive(app_root, workspace, biz_app_id=biz_app_id, biz_app_number=biz_app_number)
|
|
1575
|
+
app_id = biz_app_id or workspace.get("bizAppId")
|
|
1576
|
+
return app_id, biz_app_number or (None if app_id else workspace.get("appNumber"))
|
|
1577
|
+
|
|
1578
|
+
|
|
1314
1579
|
def _retarget_runtime_dir(app_root):
|
|
1315
1580
|
"""Re-point global RUNTIME_DIR/WORKSPACE_JSON to `{app_root}/_runtime/cosmic-metadata`.
|
|
1316
1581
|
|
|
@@ -1323,6 +1588,9 @@ def _retarget_runtime_dir(app_root):
|
|
|
1323
1588
|
if not app_root:
|
|
1324
1589
|
return
|
|
1325
1590
|
resolved = os.path.abspath(os.path.expanduser(app_root))
|
|
1591
|
+
# kd_token_cache resolves its workspace independently from _core.config.
|
|
1592
|
+
# Keep authentication state under the same explicit appRoot as metadata state.
|
|
1593
|
+
os.environ["PROJECT_ROOT"] = resolved
|
|
1326
1594
|
from _core import config as _cfg
|
|
1327
1595
|
_cfg._init_paths(app_root=resolved)
|
|
1328
1596
|
RUNTIME_DIR = _cfg.RUNTIME_DIR
|
|
@@ -1708,6 +1976,9 @@ def _resolve_owned_report_target(cfg, token, args, command):
|
|
|
1708
1976
|
|
|
1709
1977
|
def _attach_report_readback(cfg, token, form_id, section, expected, result_data):
|
|
1710
1978
|
schema = do_get_report_schema(cfg, token, form_id=form_id)
|
|
1979
|
+
if section == "filters" and any(item.get("source") for item in expected):
|
|
1980
|
+
from _core.report_ops import report_filter_source_expectations
|
|
1981
|
+
schema["filterSourceExpectations"] = report_filter_source_expectations(cfg, token, expected)
|
|
1711
1982
|
if not schema.get("readBackAvailable"):
|
|
1712
1983
|
# No canonical read-back on this deployment; the write already returned platform
|
|
1713
1984
|
# success, so report succeeded-but-unverified, not a mismatch.
|
|
@@ -2658,11 +2929,14 @@ from _core.formatters import ( # noqa: F401
|
|
|
2658
2929
|
_print_forms, _print_schema, _print_templates,
|
|
2659
2930
|
_print_form_layout_tree, _print_panels,
|
|
2660
2931
|
_print_find_results, _print_biz_hierarchy,
|
|
2661
|
-
pjson, _transform_build_meta_result,
|
|
2932
|
+
pjson, _transform_build_meta_result, save_run_batch_result,
|
|
2662
2933
|
set_output_file, set_stdout_mode, set_current_command, _is_en,
|
|
2663
2934
|
)
|
|
2664
2935
|
|
|
2665
2936
|
|
|
2937
|
+
from execution_binding import execution_binding, validate_execution_binding
|
|
2938
|
+
from _core.execution_errors import attach_execution_error
|
|
2939
|
+
|
|
2666
2940
|
_FORMATTER_PJSON = pjson
|
|
2667
2941
|
_CLI_MACHINE_MODE = False
|
|
2668
2942
|
_CLI_RESULT_EMITTED = False
|
|
@@ -2675,6 +2949,27 @@ class _CliUsageError(ValueError):
|
|
|
2675
2949
|
self.recovery = recovery
|
|
2676
2950
|
|
|
2677
2951
|
|
|
2952
|
+
def _load_mapping_request(args):
|
|
2953
|
+
request = _load_json_arg(args.input_file)
|
|
2954
|
+
if not isinstance(request, dict):
|
|
2955
|
+
raise _CliUsageError("--input must contain a JSON object with mappings")
|
|
2956
|
+
overrides = {
|
|
2957
|
+
"entity": args.entity,
|
|
2958
|
+
"conditionField": args.condition_field,
|
|
2959
|
+
"operator": args.operator,
|
|
2960
|
+
"value": args.value,
|
|
2961
|
+
"events": args.events,
|
|
2962
|
+
"addEvents": args.add_events,
|
|
2963
|
+
}
|
|
2964
|
+
for key, value in overrides.items():
|
|
2965
|
+
if value is None or value == "":
|
|
2966
|
+
continue
|
|
2967
|
+
if key in request and request[key] != value:
|
|
2968
|
+
raise _CliUsageError(f"--input conflicts with --{key}: {request[key]!r} != {value!r}")
|
|
2969
|
+
request[key] = value
|
|
2970
|
+
return request
|
|
2971
|
+
|
|
2972
|
+
|
|
2678
2973
|
class _CliResultFailure(Exception):
|
|
2679
2974
|
def __init__(self, exit_code=1):
|
|
2680
2975
|
super().__init__("structured command failure")
|
|
@@ -2707,6 +3002,14 @@ class _CliArgumentParser(argparse.ArgumentParser):
|
|
|
2707
3002
|
def error(self, message):
|
|
2708
3003
|
if "--stdout" in sys.argv[1:]:
|
|
2709
3004
|
command = self.prog.rsplit(" ", 1)[-1]
|
|
3005
|
+
selected_parser = self
|
|
3006
|
+
for action in self._actions:
|
|
3007
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
3008
|
+
selected = next((arg for arg in sys.argv[1:] if arg in action.choices), None)
|
|
3009
|
+
if selected:
|
|
3010
|
+
command = selected
|
|
3011
|
+
selected_parser = action.choices[selected]
|
|
3012
|
+
message = re.sub(r"\s*\(choose from .+\)\s*$", "", str(message))
|
|
2710
3013
|
if _shell_split_inline_json(message):
|
|
2711
3014
|
# Prefixed so `_exception_error_code` publishes the transport code: the caller
|
|
2712
3015
|
# must change how the payload is passed, not what the payload says.
|
|
@@ -2718,8 +3021,8 @@ class _CliArgumentParser(argparse.ArgumentParser):
|
|
|
2718
3021
|
" that path as the same argument. argparse reported: " + str(message or "")
|
|
2719
3022
|
)
|
|
2720
3023
|
raise _CliUsageError(message, {
|
|
2721
|
-
"usage":
|
|
2722
|
-
"describeCommand": f"describeCommand {command}" if command in _CATALOG_COMMAND_POLICIES else "
|
|
3024
|
+
"usage": selected_parser.format_usage().strip() if command in _CATALOG_COMMAND_POLICIES else f"{self.prog} <command> [arguments]",
|
|
3025
|
+
"describeCommand": f"describeCommand {command}" if command in _CATALOG_COMMAND_POLICIES else "describeCommand <selected-command>",
|
|
2723
3026
|
})
|
|
2724
3027
|
super().error(message)
|
|
2725
3028
|
|
|
@@ -2731,7 +3034,7 @@ def _is_machine_failure(result):
|
|
|
2731
3034
|
def pjson(obj, force_stdout=False):
|
|
2732
3035
|
"""Emit one structured result and make semantic failure affect the exit code."""
|
|
2733
3036
|
global _CLI_RESULT_EMITTED
|
|
2734
|
-
obj = _attach_verified_form_delivery(obj, _CLI_DELIVERY_ARGS)
|
|
3037
|
+
obj = attach_execution_error(_attach_verified_form_delivery(obj, _CLI_DELIVERY_ARGS))
|
|
2735
3038
|
_FORMATTER_PJSON(obj, force_stdout=force_stdout)
|
|
2736
3039
|
_CLI_RESULT_EMITTED = True
|
|
2737
3040
|
if _CLI_MACHINE_MODE and _is_machine_failure(obj):
|
|
@@ -2739,14 +3042,16 @@ def pjson(obj, force_stdout=False):
|
|
|
2739
3042
|
|
|
2740
3043
|
|
|
2741
3044
|
def _persist_domain_model(result, output_file, model_type):
|
|
2742
|
-
if _is_machine_failure(result):
|
|
3045
|
+
if _is_machine_failure(result):
|
|
3046
|
+
return result
|
|
2743
3047
|
raw = (result or {}).get("data")
|
|
2744
3048
|
if not isinstance(raw, str) or not raw.strip():
|
|
2745
|
-
raise ValueError("DOMAIN_MODEL_DATA_INVALID:
|
|
3049
|
+
raise ValueError("DOMAIN_MODEL_DATA_INVALID: platform returned no JSON text")
|
|
2746
3050
|
path = os.path.abspath(output_file)
|
|
2747
3051
|
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
|
2748
3052
|
with open(path, "w", encoding="utf-8", newline="\n") as stream:
|
|
2749
|
-
stream.write(raw)
|
|
3053
|
+
stream.write(raw)
|
|
3054
|
+
stream.write("\n")
|
|
2750
3055
|
return {"status": True, "errorCode": "0", "message": None, "data": {
|
|
2751
3056
|
"modelType": model_type, "path": path, "bytes": os.path.getsize(path),
|
|
2752
3057
|
"sha256": hashlib.sha256(raw.encode("utf-8")).hexdigest(),
|
|
@@ -2760,6 +3065,10 @@ def _exception_error_code(exc):
|
|
|
2760
3065
|
return match.group(1), match.group(2) or text
|
|
2761
3066
|
if isinstance(exc, _CliUsageError):
|
|
2762
3067
|
return "CLI_ARGUMENT_ERROR", text
|
|
3068
|
+
if isinstance(exc, ImportError):
|
|
3069
|
+
return "SKILL_INSTALLATION_ERROR", text
|
|
3070
|
+
if isinstance(exc, (TimeoutError, ConnectionError)):
|
|
3071
|
+
return "ENVIRONMENT_RUNTIME_ERROR", text
|
|
2763
3072
|
return "CLI_EXECUTION_ERROR", text or exc.__class__.__name__
|
|
2764
3073
|
|
|
2765
3074
|
|
|
@@ -2771,6 +3080,13 @@ def _emit_machine_error(error_code, message, recovery=None):
|
|
|
2771
3080
|
"errorCode": str(error_code or "CLI_EXECUTION_ERROR"),
|
|
2772
3081
|
"message": str(message or "Command failed"),
|
|
2773
3082
|
"data": None,
|
|
3083
|
+
"errorCategory": attach_execution_error({"status": False, "errorCode": error_code})["errorCategory"],
|
|
3084
|
+
"capabilityLimitConfirmed": error_code == "FULL_METADATA_API_UNAVAILABLE",
|
|
3085
|
+
"executionBinding": execution_binding(),
|
|
3086
|
+
**({"writeAttempted": False} if error_code in {
|
|
3087
|
+
"SKILL_SOURCE_MISMATCH", "SKILL_REVISION_MISMATCH", "ENVIRONMENT_BINDING_MISMATCH",
|
|
3088
|
+
"CONDITIONAL_CARRY_SOURCE_PROPERTY_UNAVAILABLE",
|
|
3089
|
+
} else {}),
|
|
2774
3090
|
**({"recovery": recovery} if recovery else {}),
|
|
2775
3091
|
},
|
|
2776
3092
|
force_stdout=True,
|
|
@@ -2886,12 +3202,16 @@ from _core.commands import ( # noqa: F401
|
|
|
2886
3202
|
do_ensure_conditional_entry_style,
|
|
2887
3203
|
do_ensure_conditional_control_state,
|
|
2888
3204
|
do_ensure_conditional_clear_fields,
|
|
3205
|
+
do_ensure_conditional_field_change,
|
|
2889
3206
|
do_ensure_conditional_copy_field_value,
|
|
3207
|
+
do_ensure_copy_field_mappings,
|
|
2890
3208
|
do_ensure_conditional_carry_reference_property,
|
|
3209
|
+
do_ensure_carry_reference_mappings,
|
|
2891
3210
|
do_ensure_conditional_calculation,
|
|
2892
3211
|
do_ensure_quantity_conversion,
|
|
2893
3212
|
do_ensure_conditional_field_state,
|
|
2894
3213
|
do_ensure_field_calculation,
|
|
3214
|
+
do_ensure_time_difference_calculation,
|
|
2895
3215
|
do_ensure_rule_function_formula,
|
|
2896
3216
|
do_ensure_rule_set,
|
|
2897
3217
|
do_update_rule, do_update_rule_verified,
|
|
@@ -2906,7 +3226,7 @@ from _core.commands import ( # noqa: F401
|
|
|
2906
3226
|
do_describe_property_input,
|
|
2907
3227
|
do_set_properties, do_set_properties_verified,
|
|
2908
3228
|
do_apply_property_policy, validate_property_policy_input,
|
|
2909
|
-
control_catalog, validate_control_batch_input, do_add_controls,
|
|
3229
|
+
control_catalog, control_input_schema, validate_control_batch_input, do_add_controls,
|
|
2910
3230
|
do_set_summary_to_field_resolved,
|
|
2911
3231
|
resolve_element_rows, resolve_element_selector, resolve_element_for_properties,
|
|
2912
3232
|
compile_must_input_equality,
|
|
@@ -2917,7 +3237,7 @@ from _core.commands import ( # noqa: F401
|
|
|
2917
3237
|
do_set_combo_items, do_set_ref_props, do_set_display_prop,
|
|
2918
3238
|
read_ref_props, ref_props_names,
|
|
2919
3239
|
do_set_permission_control, do_set_business_control,
|
|
2920
|
-
do_set_complex_property, do_set_custom_style, do_set_collapse_summary, do_list_parameter_forms,
|
|
3240
|
+
do_set_complex_property, do_configure_dropdown_menu, do_set_custom_style, do_set_collapse_summary, do_list_parameter_forms,
|
|
2921
3241
|
do_ensure_tab_page_operations,
|
|
2922
3242
|
do_search_basedata, do_get_basedata_entity_id,
|
|
2923
3243
|
# List/filter/toolbar shortcut commands (ported from bos-designer copy)
|
|
@@ -3021,7 +3341,7 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3021
3341
|
"""
|
|
3022
3342
|
if not isinstance(cmd_args, dict):
|
|
3023
3343
|
raise ValueError("serve args must be an object")
|
|
3024
|
-
if cmd in {"modifyMeta", "batchModify", "deleteForm", "enableDisableForm", "inheritForm", "layoutForm"}:
|
|
3344
|
+
if cmd in {"modifyMeta", "batchModify", "deleteForm", "enableDisableForm", "inheritForm", "layoutForm", "registerPlugin"}:
|
|
3025
3345
|
app_root = str(cmd_args.get("appRoot") or "").strip()
|
|
3026
3346
|
if not app_root:
|
|
3027
3347
|
raise ValueError(
|
|
@@ -3186,9 +3506,13 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3186
3506
|
)
|
|
3187
3507
|
|
|
3188
3508
|
elif cmd == "getDomainModel":
|
|
3189
|
-
return do_get_domain_model(
|
|
3190
|
-
|
|
3191
|
-
|
|
3509
|
+
return do_get_domain_model(
|
|
3510
|
+
cfg, token, cmd_args["modelType"],
|
|
3511
|
+
only_entity=cmd_args.get("onlyEntity"),
|
|
3512
|
+
ext=cmd_args.get("ext"),
|
|
3513
|
+
bizext=cmd_args.get("bizext"),
|
|
3514
|
+
app_id=cmd_args.get("appId"),
|
|
3515
|
+
)
|
|
3192
3516
|
|
|
3193
3517
|
elif cmd == "queryPublicTemplates":
|
|
3194
3518
|
return do_query_public_templates(
|
|
@@ -3312,7 +3636,8 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3312
3636
|
return do_register_plugin(
|
|
3313
3637
|
cfg, token,
|
|
3314
3638
|
form_number=cmd_args.get("formNumber"),
|
|
3315
|
-
plugin_config=cmd_args.get("pluginConfig", {})
|
|
3639
|
+
plugin_config=normalize_plugin_registration_input(cmd_args.get("pluginConfig", {})),
|
|
3640
|
+
app_root=cmd_args.get("appRoot"),
|
|
3316
3641
|
)
|
|
3317
3642
|
|
|
3318
3643
|
elif cmd == "updatePlugin":
|
|
@@ -3370,6 +3695,16 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3370
3695
|
right=cmd_args.get("right"),
|
|
3371
3696
|
)
|
|
3372
3697
|
|
|
3698
|
+
elif cmd == "ensureTimeDifferenceCalculation":
|
|
3699
|
+
return do_ensure_time_difference_calculation(
|
|
3700
|
+
cfg, token,
|
|
3701
|
+
form_number=cmd_args.get("formNumber"),
|
|
3702
|
+
entity=cmd_args.get("entity"),
|
|
3703
|
+
target=cmd_args.get("target"),
|
|
3704
|
+
start=cmd_args.get("start"),
|
|
3705
|
+
end=cmd_args.get("end"),
|
|
3706
|
+
)
|
|
3707
|
+
|
|
3373
3708
|
elif cmd == "ensureRuleFunctionFormula":
|
|
3374
3709
|
return do_ensure_rule_function_formula(
|
|
3375
3710
|
cfg, token,
|
|
@@ -3389,6 +3724,8 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3389
3724
|
value=cmd_args.get("value"),
|
|
3390
3725
|
target=cmd_args.get("target"),
|
|
3391
3726
|
behavior=cmd_args.get("behavior"),
|
|
3727
|
+
add_events=cmd_args.get("addEvents"),
|
|
3728
|
+
events=cmd_args.get("events"),
|
|
3392
3729
|
)
|
|
3393
3730
|
|
|
3394
3731
|
elif cmd == "ensureConditionalEntryStyle":
|
|
@@ -3423,9 +3760,24 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3423
3760
|
form_number=cmd_args.get("formNumber"), entity=cmd_args.get("entity"),
|
|
3424
3761
|
condition_field=cmd_args.get("conditionField"), operator=cmd_args.get("operator"),
|
|
3425
3762
|
value=cmd_args.get("value"), targets=cmd_args.get("targets"),
|
|
3763
|
+
add_events=cmd_args.get("addEvents"),
|
|
3764
|
+
)
|
|
3765
|
+
|
|
3766
|
+
elif cmd == "ensureConditionalFieldChange":
|
|
3767
|
+
return do_ensure_conditional_field_change(
|
|
3768
|
+
cfg, token, form_number=cmd_args.get("formNumber"),
|
|
3769
|
+
entity=cmd_args.get("entity"),
|
|
3770
|
+
condition_field=cmd_args.get("conditionField"),
|
|
3771
|
+
operator=cmd_args.get("operator"), value=cmd_args.get("value"),
|
|
3772
|
+
target=cmd_args.get("target"), add_events=cmd_args.get("addEvents"),
|
|
3773
|
+
events=cmd_args.get("events"),
|
|
3426
3774
|
)
|
|
3427
3775
|
|
|
3428
3776
|
elif cmd == "ensureConditionalCopyFieldValue":
|
|
3777
|
+
if isinstance(cmd_args.get("input"), dict):
|
|
3778
|
+
return do_ensure_copy_field_mappings(
|
|
3779
|
+
cfg, token, cmd_args.get("formNumber"), cmd_args["input"],
|
|
3780
|
+
)
|
|
3429
3781
|
return do_ensure_conditional_copy_field_value(
|
|
3430
3782
|
cfg, token,
|
|
3431
3783
|
form_number=cmd_args.get("formNumber"), entity=cmd_args.get("entity"),
|
|
@@ -3435,6 +3787,10 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3435
3787
|
)
|
|
3436
3788
|
|
|
3437
3789
|
elif cmd == "ensureConditionalCarryReferenceProperty":
|
|
3790
|
+
if isinstance(cmd_args.get("input"), dict):
|
|
3791
|
+
return do_ensure_carry_reference_mappings(
|
|
3792
|
+
cfg, token, cmd_args.get("formNumber"), cmd_args["input"],
|
|
3793
|
+
)
|
|
3438
3794
|
return do_ensure_conditional_carry_reference_property(
|
|
3439
3795
|
cfg, token,
|
|
3440
3796
|
form_number=cmd_args.get("formNumber"), entity=cmd_args.get("entity"),
|
|
@@ -3903,9 +4259,10 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
|
|
|
3903
4259
|
elif cmd == "getUnit":
|
|
3904
4260
|
return do_get_unit(cfg, token, cmd_args.get("bizAppId"), cmd_args.get("unitId"))
|
|
3905
4261
|
elif cmd == "addUnit":
|
|
4262
|
+
unit_app_id, unit_app_number = _unit_app_target(cmd_args.get("appRoot"), cmd_args.get("bizAppId"), cmd_args.get("bizAppNumber"))
|
|
3906
4263
|
return do_add_unit(cfg, token,
|
|
3907
|
-
biz_app_id=
|
|
3908
|
-
biz_app_number=
|
|
4264
|
+
biz_app_id=unit_app_id,
|
|
4265
|
+
biz_app_number=unit_app_number,
|
|
3909
4266
|
name=cmd_args.get("name"),
|
|
3910
4267
|
number=cmd_args.get("number"),
|
|
3911
4268
|
description=cmd_args.get("description"),
|
|
@@ -4320,7 +4677,8 @@ def _form_delivery(app_root, numbers):
|
|
|
4320
4677
|
"instruction": (
|
|
4321
4678
|
"写入及回读完成,产物交付尚未完成。若当前请求没有其他明确修改,下一步执行 command,"
|
|
4322
4679
|
"按 references/delivery.md 完成本轮交付;否则先完成剩余修改,再按应用汇总本轮目标收口。"
|
|
4323
|
-
"
|
|
4680
|
+
"这是 app-build 的领域交付,不代表业务验收完成。独立 app-build 在最终展示卡片时调用 output_check;"
|
|
4681
|
+
"专家团委托实施时保留卡片与目标引用,继续负责人安排的 E2E,再由负责人统一最终交付。"
|
|
4324
4682
|
"此前轮次已展示该表单不代表本轮修改已交付。"
|
|
4325
4683
|
"不要重放已完成的写入,不在中间步骤同步 DYM。"
|
|
4326
4684
|
),
|
|
@@ -4592,7 +4950,7 @@ def _serve_loop(cfg):
|
|
|
4592
4950
|
token = do_auth(cfg)
|
|
4593
4951
|
|
|
4594
4952
|
# Ready signal
|
|
4595
|
-
print("###SERVE_READY###", flush=True)
|
|
4953
|
+
print("###SERVE_READY###", file=sys.stdout, flush=True)
|
|
4596
4954
|
|
|
4597
4955
|
for line in sys.stdin:
|
|
4598
4956
|
line = line.strip()
|
|
@@ -4601,7 +4959,7 @@ def _serve_loop(cfg):
|
|
|
4601
4959
|
try:
|
|
4602
4960
|
request = json.loads(line)
|
|
4603
4961
|
except (json.JSONDecodeError, ValueError) as e:
|
|
4604
|
-
print(json.dumps({"success": False, "error": "Invalid JSON: %s" % str(e)}, ensure_ascii=False), flush=True)
|
|
4962
|
+
print(json.dumps({"success": False, "error": "Invalid JSON: %s" % str(e)}, ensure_ascii=False), file=sys.stdout, flush=True)
|
|
4605
4963
|
continue
|
|
4606
4964
|
|
|
4607
4965
|
cmd = request.get("cmd", "")
|
|
@@ -4610,14 +4968,14 @@ def _serve_loop(cfg):
|
|
|
4610
4968
|
if cmd == "exit":
|
|
4611
4969
|
break
|
|
4612
4970
|
if cmd == "ping":
|
|
4613
|
-
print(json.dumps({"success": True, "data": "pong"}, ensure_ascii=False), flush=True)
|
|
4971
|
+
print(json.dumps({"success": True, "data": "pong"}, ensure_ascii=False), file=sys.stdout, flush=True)
|
|
4614
4972
|
continue
|
|
4615
4973
|
|
|
4616
4974
|
try:
|
|
4617
4975
|
result = _dispatch_serve(cfg, token, cmd, cmd_args)
|
|
4618
|
-
print(json.dumps({"success": _serve_result_success(result), "data": result}, ensure_ascii=False), flush=True)
|
|
4976
|
+
print(json.dumps({"success": _serve_result_success(result), "data": result}, ensure_ascii=False), file=sys.stdout, flush=True)
|
|
4619
4977
|
except Exception as e:
|
|
4620
|
-
print(json.dumps({"success": False, "error": str(e)}, ensure_ascii=False), flush=True)
|
|
4978
|
+
print(json.dumps({"success": False, "error": str(e)}, ensure_ascii=False), file=sys.stdout, flush=True)
|
|
4621
4979
|
|
|
4622
4980
|
|
|
4623
4981
|
# ─── Report Pipeline ─────────────────────────────────────────────────────────
|
|
@@ -4933,6 +5291,9 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
|
|
|
4933
5291
|
if columns:
|
|
4934
5292
|
checks.append(report_readback_verification("columns", columns, final_schema))
|
|
4935
5293
|
if filters:
|
|
5294
|
+
if any(item.get("source") for item in filters):
|
|
5295
|
+
from _core.report_ops import report_filter_source_expectations
|
|
5296
|
+
final_schema["filterSourceExpectations"] = report_filter_source_expectations(cfg, token, filters)
|
|
4936
5297
|
checks.append(report_readback_verification("filters", filters, final_schema))
|
|
4937
5298
|
if sorts and "reportSorts" not in unverifiable:
|
|
4938
5299
|
checks.append(report_readback_verification("sorts", sorts, final_schema))
|
|
@@ -5022,7 +5383,8 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
|
|
|
5022
5383
|
_plugin_contract = (
|
|
5023
5384
|
None if (query_data_source and not plugins)
|
|
5024
5385
|
else compile_report_plugin_contract(
|
|
5025
|
-
{"reportColumns": columns, "reportFilters": filters, "reportPlugins": plugins
|
|
5386
|
+
{"reportColumns": columns, "reportFilters": filters, "reportPlugins": plugins,
|
|
5387
|
+
"reportProperties": artifact.get("reportProperties") or {}},
|
|
5026
5388
|
form_number,
|
|
5027
5389
|
)
|
|
5028
5390
|
)
|
|
@@ -5081,12 +5443,14 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
|
|
|
5081
5443
|
|
|
5082
5444
|
_CATALOG_INTERNAL_COMMANDS = {"catalog", "describeCommand"}
|
|
5083
5445
|
_CATALOG_PAYLOAD_SCHEMA_REFS = {
|
|
5446
|
+
"addControls": "skills/cosmic-meta-api/contracts/cli/control-authoring.json#/inputSchema",
|
|
5084
5447
|
"modifyMeta": "skills/cosmic-meta-api/contracts/schema/metaops-structural.schema.json",
|
|
5085
5448
|
"batchModify": "skills/cosmic-meta-api/contracts/schema/batch-modify-manifest.schema.json",
|
|
5086
5449
|
"applyContainerLayout": "skills/cosmic-meta-api/contracts/schema/container-layout.schema.json",
|
|
5087
5450
|
"applyFormComposition": "skills/cosmic-meta-api/contracts/schema/form-composition.schema.json",
|
|
5088
5451
|
"applyPanelOrder": "skills/cosmic-meta-api/contracts/schema/panel-order.schema.json",
|
|
5089
5452
|
"configureEntryGrid": "skills/cosmic-meta-api/contracts/schema/entry-grid.schema.json",
|
|
5453
|
+
"configureDropdownMenu": "skills/cosmic-meta-api/contracts/schema/dropdown-menu.schema.json",
|
|
5090
5454
|
"applyFormUi": "skills/cosmic-meta-api/contracts/schema/existing-form-ui.schema.json",
|
|
5091
5455
|
"applyFormGrid": "skills/cosmic-meta-api/contracts/schema/form-grid.schema.json",
|
|
5092
5456
|
"ensureOperationValidation": "skills/cosmic-meta-api/contracts/schema/operation-validation.schema.json",
|
|
@@ -5135,6 +5499,7 @@ _CATALOG_COMMAND_CONTRACT_REFS = {
|
|
|
5135
5499
|
"applyFormComposition": "skills/cosmic-meta-api/contracts/cli/layout-ui.json#commands/applyFormComposition",
|
|
5136
5500
|
"applyPanelOrder": "skills/cosmic-meta-api/contracts/cli/layout-ui.json#commands/applyPanelOrder",
|
|
5137
5501
|
"configureEntryGrid": "skills/cosmic-meta-api/contracts/cli/layout-ui.json#commands/configureEntryGrid",
|
|
5502
|
+
"configureDropdownMenu": "skills/cosmic-meta-api/contracts/cli/layout-ui.json#commands/configureDropdownMenu",
|
|
5138
5503
|
"applyFormUi": "skills/cosmic-meta-api/contracts/cli/layout-ui.json#commands/applyFormUi",
|
|
5139
5504
|
"ensureTabPage": "skills/cosmic-meta-api/contracts/cli/layout-ui.json#commands/ensureTabPage",
|
|
5140
5505
|
"moveFormElement": "skills/cosmic-meta-api/contracts/cli/layout-ui.json#commands/moveFormElement",
|
|
@@ -5156,6 +5521,7 @@ _CATALOG_COMMAND_CONTRACT_REFS = {
|
|
|
5156
5521
|
"deleteRule": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/deleteRule",
|
|
5157
5522
|
"listRuleFunctions": "skills/cosmic-meta-api/contracts/cli/rule-functions.json",
|
|
5158
5523
|
"ensureFieldCalculation": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureFieldCalculation",
|
|
5524
|
+
"ensureTimeDifferenceCalculation": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureTimeDifferenceCalculation",
|
|
5159
5525
|
"ensureRuleFunctionFormula": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureRuleFunctionFormula",
|
|
5160
5526
|
"ensureRuleSet": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureRuleSet",
|
|
5161
5527
|
"ensureConditionalFieldState": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalFieldState",
|
|
@@ -5163,6 +5529,7 @@ _CATALOG_COMMAND_CONTRACT_REFS = {
|
|
|
5163
5529
|
"ensureConditionalEntryStyle": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalEntryStyle",
|
|
5164
5530
|
"ensureConditionalControlState": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalControlState",
|
|
5165
5531
|
"ensureConditionalClearFields": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalClearFields",
|
|
5532
|
+
"ensureConditionalFieldChange": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalFieldChange",
|
|
5166
5533
|
"ensureConditionalCopyFieldValue": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalCopyFieldValue",
|
|
5167
5534
|
"ensureConditionalCarryReferenceProperty": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalCarryReferenceProperty",
|
|
5168
5535
|
"ensureConditionalCalculation": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/ensureConditionalCalculation",
|
|
@@ -5178,6 +5545,7 @@ _CATALOG_COMMAND_CONTRACT_REFS = {
|
|
|
5178
5545
|
"setReportRowBackColor": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/setReportRowBackColor",
|
|
5179
5546
|
"setReportFilterFieldConfig": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/setReportFilterFieldConfig",
|
|
5180
5547
|
"setReportDefaultHiddenFields": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/setReportDefaultHiddenFields",
|
|
5548
|
+
"renameElementKey": "skills/cosmic-meta-api/contracts/cli/property-batch.json#commands/renameElementKey",
|
|
5181
5549
|
"registerReportPluginExt": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/registerReportPluginExt",
|
|
5182
5550
|
"setBasedataDefault": "skills/cosmic-meta-api/contracts/cli/property-value-encoding.json#defaultValueModel",
|
|
5183
5551
|
"setCollapseSummary": "skills/cosmic-meta-api/contracts/cli/collapse-summary.json",
|
|
@@ -5223,10 +5591,10 @@ _CATALOG_COMMANDS_BY_EFFECT = {
|
|
|
5223
5591
|
"resetEnv", "serve", "setConfig", "setInitialized", "setPdmConfig", "syncDym",
|
|
5224
5592
|
}),
|
|
5225
5593
|
"write": frozenset({
|
|
5226
|
-
"addAdvancedContainer", "addButton", "addControls", "addEntryEntity", "addField", "addFieldPanel", "addFilterColumn", "addListColumn", "applyContainerLayout", "applyFormComposition", "applyPanelOrder", "applyFormUi", "applyFormGrid", "applyFormSplit", "applyFormTabs", "configureEntryGrid", "configureList", "configureSelectionFields", "ensureTabPage", "moveFormElement",
|
|
5594
|
+
"addAdvancedContainer", "addButton", "addControls", "addEntryEntity", "addField", "addFieldPanel", "addFilterColumn", "addListColumn", "applyContainerLayout", "applyFormComposition", "applyPanelOrder", "applyFormUi", "applyFormGrid", "applyFormSplit", "applyFormTabs", "configureDropdownMenu", "configureEntryGrid", "configureList", "configureSelectionFields", "ensureTabPage", "moveFormElement",
|
|
5227
5595
|
"addReportColumns", "addSimpleField",
|
|
5228
5596
|
"addMenu", "addOperation", "addRule", "addUnit", "batchModify",
|
|
5229
|
-
"ensureConditionalCalculation", "ensureConditionalCarryReferenceProperty", "ensureConditionalClearFields", "ensureConditionalCopyFieldValue", "ensureConditionalControlState", "ensureConditionalClientAppearance", "ensureConditionalEntryStyle", "ensureConditionalFieldState", "ensureFieldCalculation", "ensureQuantityConversion", "ensureRuleFunctionFormula", "ensureRuleSet", "ensureTabPageOperations",
|
|
5597
|
+
"ensureConditionalCalculation", "ensureConditionalCarryReferenceProperty", "ensureConditionalClearFields", "ensureConditionalFieldChange", "ensureConditionalCopyFieldValue", "ensureConditionalControlState", "ensureConditionalClientAppearance", "ensureConditionalEntryStyle", "ensureConditionalFieldState", "ensureFieldCalculation", "ensureTimeDifferenceCalculation", "ensureQuantityConversion", "ensureRuleFunctionFormula", "ensureRuleSet", "ensureTabPageOperations",
|
|
5230
5598
|
"bootstrapWorkspace", "buildMeta", "copyForm", "createPage", "createScript",
|
|
5231
5599
|
"enableDisableForm", "extendApp", "extendForm", "extendFormFlow",
|
|
5232
5600
|
"importForm", "inheritForm", "layoutForm", "modifyMeta", "moveForm",
|
|
@@ -5238,7 +5606,7 @@ _CATALOG_COMMANDS_BY_EFFECT = {
|
|
|
5238
5606
|
"setDisplayProp", "setEditSearchProp", "setFeatures", "setFilter", "setFormBackground",
|
|
5239
5607
|
"setFreezeList", "setInitField", "setMustInputCondition", "setNetworkControl",
|
|
5240
5608
|
"setSplitTables", "setInstantSync",
|
|
5241
|
-
"setOrgFuncs", "setOrgViewScheme", "setPermissionControl", "setProperties",
|
|
5609
|
+
"setOrgFuncs", "setOrgViewScheme", "setPermissionControl", "setProperties", "renameElementKey",
|
|
5242
5610
|
"setRefLayout", "setRefProps", "setReportColumns",
|
|
5243
5611
|
"setReportDefaultHiddenFields", "setReportFilterFieldConfig", "setReportFilters",
|
|
5244
5612
|
"setReportProperties", "setReportQueryDataSource", "setReportRowBackColor",
|
|
@@ -5664,7 +6032,7 @@ _CATALOG_ARGUMENT_CONSTRAINTS = {
|
|
|
5664
6032
|
"addField": [
|
|
5665
6033
|
{
|
|
5666
6034
|
"kind": "batch_fields",
|
|
5667
|
-
"message": "--fields accepts name,bizWord,fieldType,combo,comboJson,required,fullLine
|
|
6035
|
+
"message": "--fields accepts name,bizWord,fieldType,combo,comboJson,required,fullLine,panel,before,after,basedata,mulTable,unitField,flexType,refBaseField,refDisplayProp,asstTypeId,asstType. BasedataField/MulBasedataField also accept reference:{mode:platform,formNumber:<exact number>} instead of basedata. comboJson uses stable value/label objects. --validate-only checks all specs without authentication; ordered writes share target preparation and stop at the first failure",
|
|
5668
6036
|
},
|
|
5669
6037
|
{
|
|
5670
6038
|
"kind": "header_placement",
|
|
@@ -5750,7 +6118,7 @@ _CATALOG_ARGUMENT_CONSTRAINTS = {
|
|
|
5750
6118
|
for _property_command in (
|
|
5751
6119
|
"setProperties", "setMustInputCondition", "setDefValueDesign",
|
|
5752
6120
|
"setBasedataDefault", "setRefProps", "setRefLayout", "setOrgFuncs",
|
|
5753
|
-
"setOrgViewScheme", "setAsstTypeId", "setComplexProperty", "setCustomStyle", "setCollapseSummary", "ensureTabPageOperations",
|
|
6121
|
+
"setOrgViewScheme", "setAsstTypeId", "setComplexProperty", "configureDropdownMenu", "setCustomStyle", "setCollapseSummary", "ensureTabPageOperations",
|
|
5754
6122
|
"setFeatures", "setFilter", "setDisplayProp", "setEditSearchProp", "setFormBackground",
|
|
5755
6123
|
"setFreezeList", "setInitField", "setCompareTypeConfig",
|
|
5756
6124
|
"setNetworkControl", "setPermissionControl", "setBusinessControl",
|
|
@@ -5778,6 +6146,8 @@ for _lifecycle_command in (
|
|
|
5778
6146
|
"message": "the command resolves and verifies one exact existing form in the same invocation",
|
|
5779
6147
|
})
|
|
5780
6148
|
_CATALOG_CONDITIONAL_EFFECTS = {
|
|
6149
|
+
"runBatch": [{"when": {"reconcile_only": True}, "effect": "local", "risk": "low",
|
|
6150
|
+
"summary": "Reads platform state and updates only local recovery receipts; never creates metadata."}],
|
|
5781
6151
|
"getApp": [
|
|
5782
6152
|
{
|
|
5783
6153
|
"when": {"sync_identity": True},
|
|
@@ -6015,9 +6385,14 @@ def _command_descriptor(subparsers, command, *, detailed=False):
|
|
|
6015
6385
|
result_contract = "JSONL stdio" if command == "serve" else "JSON stdout"
|
|
6016
6386
|
descriptor["execution"] = {
|
|
6017
6387
|
"entry": os.path.abspath(__file__),
|
|
6388
|
+
"python": sys.executable,
|
|
6389
|
+
"argvPrefix": [sys.executable, os.path.abspath(__file__), "--stdout", command],
|
|
6390
|
+
"binding": execution_binding(),
|
|
6018
6391
|
"template": (
|
|
6019
|
-
|
|
6020
|
-
|
|
6392
|
+
(subprocess.list2cmdline([sys.executable, os.path.abspath(__file__), "--stdout", command])
|
|
6393
|
+
if os.name == "nt" else shlex.join([sys.executable, os.path.abspath(__file__), "--stdout", command])) + " "
|
|
6394
|
+
+ ('--app-root "<appRoot>" ' if requires_app_root else '')
|
|
6395
|
+
+ '<arguments>'
|
|
6021
6396
|
),
|
|
6022
6397
|
"result": result_contract,
|
|
6023
6398
|
}
|
|
@@ -6067,7 +6442,10 @@ def _command_descriptor(subparsers, command, *, detailed=False):
|
|
|
6067
6442
|
"humanGate": "use the token only after a later user message confirms the exact returned targets",
|
|
6068
6443
|
}
|
|
6069
6444
|
if payload_schema_ref:
|
|
6070
|
-
descriptor["payloadSchema"] =
|
|
6445
|
+
descriptor["payloadSchema"] = (
|
|
6446
|
+
control_input_schema() if command == "addControls"
|
|
6447
|
+
else _catalog_payload_schema(payload_schema_ref)
|
|
6448
|
+
)
|
|
6071
6449
|
return descriptor
|
|
6072
6450
|
|
|
6073
6451
|
|
|
@@ -6245,6 +6623,10 @@ def _main_impl():
|
|
|
6245
6623
|
help="Exact existing application code; verifies and synchronizes a trusted appRoot identity before any build write")
|
|
6246
6624
|
prb.add_argument("--emit-manifest", default=None, dest="emit_manifest",
|
|
6247
6625
|
help="Exact _emit_manifest.json produced for this workNo; prevents cross-run artifact reuse")
|
|
6626
|
+
prb.add_argument("--reconcile-only", action="store_true",
|
|
6627
|
+
help="Read-only recovery of --resume-result: verify retained exact identities or confirm absence before a later resume")
|
|
6628
|
+
prb.add_argument("--resume-result", default=None,
|
|
6629
|
+
help="Original durable runBatch result; reuse verified objects and execute only confirmed unwritten inputs")
|
|
6248
6630
|
prb.add_argument("--verify", action="store_true",
|
|
6249
6631
|
help="After each successful buildMeta, exactly read back entities, fields and parent links")
|
|
6250
6632
|
prb.add_argument("--bizAppId", required=False, default=None,
|
|
@@ -6276,13 +6658,13 @@ def _main_impl():
|
|
|
6276
6658
|
pi.add_argument("--force", action="store_true",
|
|
6277
6659
|
help="Force refresh, even if config.md is already initialized")
|
|
6278
6660
|
pi.add_argument("--isv", default=None,
|
|
6279
|
-
help="[Bootstrap mode] Temporarily override isv
|
|
6661
|
+
help="[Bootstrap mode] Temporarily override isv from ~/.kd/config.json (for first-time initialization)")
|
|
6280
6662
|
pi.add_argument("--app-number", default=None, dest="app_number_override",
|
|
6281
|
-
help="[Bootstrap mode] Temporarily override app_number
|
|
6663
|
+
help="[Bootstrap mode] Temporarily override app_number from ~/.kd/config.json (for first-time initialization)")
|
|
6282
6664
|
|
|
6283
|
-
# setConfig (write selected values to config.json during AI-guided initialization)
|
|
6665
|
+
# setConfig (write selected values to ~/.kd/config.json / workspace.json during AI-guided initialization)
|
|
6284
6666
|
psc = sub.add_parser("setConfig",
|
|
6285
|
-
help="Write fields to
|
|
6667
|
+
help="Write fields to ~/.kd/config.json default environment and workspace.json (no auth needed); credential fields and business fields can be written in one go")
|
|
6286
6668
|
psc.add_argument("--app-number", default=None, dest="app_number",
|
|
6287
6669
|
help="App number (written to workspace.json), e.g., uhyl_aireqdecomp")
|
|
6288
6670
|
psc.add_argument("--biz-cloud-name", default=None, dest="biz_cloud_name",
|
|
@@ -6364,7 +6746,7 @@ def _main_impl():
|
|
|
6364
6746
|
|
|
6365
6747
|
# queryFormsByApp (query within specified app/cloud scope)
|
|
6366
6748
|
pqfa = sub.add_parser("queryFormsByApp", help="Query explicit app/cloud scope or the selected app-root; ignores ambient defaults. ZERO rows means no matches in that scope, not global absence. Use queryForms for global discovery.")
|
|
6367
|
-
pqfa.add_argument("--appNumber",
|
|
6749
|
+
pqfa.add_argument("--appNumber", "--appCode", dest="appNumber", default=None,
|
|
6368
6750
|
help="App number exact filter (e.g., uhyl_aireqdecomp)")
|
|
6369
6751
|
pqfa.add_argument("--cloudNumber", default=None,
|
|
6370
6752
|
help="Business cloud number exact filter (e.g., uhyl_bfyTest1)")
|
|
@@ -6372,9 +6754,9 @@ def _main_impl():
|
|
|
6372
6754
|
"--keyword", "--form-name", "--form", "--formNumber",
|
|
6373
6755
|
dest="keyword", default=None,
|
|
6374
6756
|
help=(
|
|
6375
|
-
"
|
|
6376
|
-
"
|
|
6377
|
-
"--formNumber
|
|
6757
|
+
"Optional filter by form name or number; omit it to list all forms in the selected scope. "
|
|
6758
|
+
"Do not pass an empty or invented placeholder when no filter is intended; --form-name, "
|
|
6759
|
+
"--form and --formNumber are compatible selector spellings"
|
|
6378
6760
|
),
|
|
6379
6761
|
)
|
|
6380
6762
|
pqfa.add_argument("--json", action="store_true", dest="raw_json",
|
|
@@ -6399,15 +6781,25 @@ def _main_impl():
|
|
|
6399
6781
|
pgs.add_argument("--json", action="store_true", dest="raw_json",
|
|
6400
6782
|
help="Output raw JSON")
|
|
6401
6783
|
|
|
6402
|
-
pgdm = sub.add_parser(
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
pgdm.add_argument("--
|
|
6407
|
-
|
|
6784
|
+
pgdm = sub.add_parser(
|
|
6785
|
+
"getDomainModel",
|
|
6786
|
+
help="Read one platform domain-model JSON by exact model type",
|
|
6787
|
+
)
|
|
6788
|
+
pgdm.add_argument("--modelType", required=True,
|
|
6789
|
+
help="Exact platform model type, for example BillFormModel")
|
|
6790
|
+
pgdm.add_argument("--onlyEntity", default=None,
|
|
6791
|
+
help="Optional platform onlyEntity query value, passed through unchanged")
|
|
6792
|
+
pgdm.add_argument("--ext", default=None,
|
|
6793
|
+
help="Optional platform extension-designer query value, passed through unchanged")
|
|
6794
|
+
pgdm.add_argument("--bizext", default=None,
|
|
6795
|
+
help="Optional platform business-object-extension query value, passed through unchanged")
|
|
6796
|
+
pgdm.add_argument("--appId", default=None,
|
|
6797
|
+
help="Optional platform application code carried in the appId query key")
|
|
6408
6798
|
pgdm_output = pgdm.add_mutually_exclusive_group(required=True)
|
|
6409
|
-
pgdm_output.add_argument("--output-file", dest="domain_output_file",
|
|
6410
|
-
|
|
6799
|
+
pgdm_output.add_argument("--output-file", dest="domain_output_file",
|
|
6800
|
+
help="Write data JSON text to this UTF-8 file and print only a compact receipt")
|
|
6801
|
+
pgdm_output.add_argument("--stdout-data", action="store_true",
|
|
6802
|
+
help="Explicitly print the complete response; BillFormModel can exceed 1 MB")
|
|
6411
6803
|
|
|
6412
6804
|
# discover (zero cold-start one-click discovery: locate form/app + get main entity + existing bizWord styles)
|
|
6413
6805
|
pdv = sub.add_parser(
|
|
@@ -6438,7 +6830,7 @@ def _main_impl():
|
|
|
6438
6830
|
paf_selectors = paf.add_mutually_exclusive_group(required=True)
|
|
6439
6831
|
paf_selectors.add_argument("--field-name", dest="field_name",
|
|
6440
6832
|
help="New field display name")
|
|
6441
|
-
paf_selectors.add_argument("--fields", help="JSON array/file: name,
|
|
6833
|
+
paf_selectors.add_argument("--fields", help="JSON array/file: name,bizWord,fieldType; optional comboJson,required,fullLine,panel,before,after,basedata,reference,mulTable,unitField,flexType,refBaseField,refDisplayProp,asstTypeId,asstType; one ordered batch")
|
|
6442
6834
|
paf.add_argument("--validate-only", action="store_true", help="Validate --fields completely without platform writes")
|
|
6443
6835
|
paf.add_argument("--biz-word", dest="biz_word",
|
|
6444
6836
|
help="Concise semantic English business word without ISV prefix")
|
|
@@ -6707,15 +7099,15 @@ def _main_impl():
|
|
|
6707
7099
|
|
|
6708
7100
|
# queryPublicTemplates
|
|
6709
7101
|
ppt = sub.add_parser("queryPublicTemplates", help="Query public template list")
|
|
6710
|
-
ppt.add_argument("--modelType", default="
|
|
6711
|
-
help="Model type:
|
|
7102
|
+
ppt.add_argument("--modelType", default="BillFormModel",
|
|
7103
|
+
help="Model type: BillFormModel (Bill) / BaseFormModel (Base Data)")
|
|
6712
7104
|
ppt.add_argument("--json", action="store_true", dest="raw_json",
|
|
6713
7105
|
help="Output raw JSON")
|
|
6714
7106
|
|
|
6715
7107
|
# queryBizTemplates
|
|
6716
7108
|
pbt = sub.add_parser("queryBizTemplates", help="Query business template list (specified app scope)")
|
|
6717
|
-
pbt.add_argument("--modelType", default="
|
|
6718
|
-
help="Model type:
|
|
7109
|
+
pbt.add_argument("--modelType", default="BillFormModel",
|
|
7110
|
+
help="Model type: BillFormModel / BaseFormModel")
|
|
6719
7111
|
pbt.add_argument("--bizAppId", default=None, help="App ID")
|
|
6720
7112
|
pbt.add_argument("--bizAppNumber", default=None,
|
|
6721
7113
|
help="App number (mutually exclusive with bizAppId)")
|
|
@@ -6935,6 +7327,17 @@ def _main_impl():
|
|
|
6935
7327
|
help="Binary arithmetic operator")
|
|
6936
7328
|
p_efc.add_argument("--right", required=True, help="Right numeric operand field")
|
|
6937
7329
|
|
|
7330
|
+
p_etdc = sub.add_parser(
|
|
7331
|
+
"ensureTimeDifferenceCalculation",
|
|
7332
|
+
help="Idempotently calculate decimal hours between two same-entity TimeFields")
|
|
7333
|
+
p_etdc_target = p_etdc.add_mutually_exclusive_group(required=True)
|
|
7334
|
+
p_etdc_target.add_argument("--formNumber", help="Exact form number")
|
|
7335
|
+
p_etdc_target.add_argument("--form-name", dest="form_name", help="Exact form name or number")
|
|
7336
|
+
p_etdc.add_argument("--entity", help="Optional exact entity; inferred from fields when omitted")
|
|
7337
|
+
p_etdc.add_argument("--target", required=True, help="Numeric target field key or caption")
|
|
7338
|
+
p_etdc.add_argument("--start", required=True, help="Start TimeField key or caption")
|
|
7339
|
+
p_etdc.add_argument("--end", required=True, help="End TimeField key or caption")
|
|
7340
|
+
|
|
6938
7341
|
p_erff = sub.add_parser(
|
|
6939
7342
|
"ensureRuleFunctionFormula",
|
|
6940
7343
|
help="Compile one released FX function call into a same-entity formula rule",
|
|
@@ -6948,6 +7351,11 @@ def _main_impl():
|
|
|
6948
7351
|
"--input", required=True, dest="input_file",
|
|
6949
7352
|
help="Inline JSON or file with {call:{functionId,args:[{field}|{literal}|{call}|{condition}]}}",
|
|
6950
7353
|
)
|
|
7354
|
+
p_erff_timing = p_erff.add_mutually_exclusive_group()
|
|
7355
|
+
p_erff_timing.add_argument("--event", action="append", dest="events",
|
|
7356
|
+
help="Select exact trigger timings for a new rule")
|
|
7357
|
+
p_erff_timing.add_argument("--add-event", action="append", dest="add_events",
|
|
7358
|
+
help="Append timing without clearing existing events")
|
|
6951
7359
|
|
|
6952
7360
|
p_ecfs = sub.add_parser(
|
|
6953
7361
|
"ensureConditionalFieldState",
|
|
@@ -6968,6 +7376,11 @@ def _main_impl():
|
|
|
6968
7376
|
help="Target field key or caption; repeat to control multiple fields in one rule")
|
|
6969
7377
|
p_ecfs.add_argument("--behavior", required=True, choices=["show", "hide", "lock", "unlock"],
|
|
6970
7378
|
help="Field state while the condition is true")
|
|
7379
|
+
p_ecfs_timing = p_ecfs.add_mutually_exclusive_group()
|
|
7380
|
+
p_ecfs_timing.add_argument("--add-event", action="append", dest="add_events",
|
|
7381
|
+
help="Append a named timing to both branches while preserving existing timings")
|
|
7382
|
+
p_ecfs_timing.add_argument("--event", action="append", dest="events",
|
|
7383
|
+
help="Select the exact trigger timings for a new rule; repeat per timing")
|
|
6971
7384
|
|
|
6972
7385
|
p_eces = sub.add_parser(
|
|
6973
7386
|
"ensureConditionalEntryStyle",
|
|
@@ -7026,7 +7439,32 @@ def _main_impl():
|
|
|
7026
7439
|
choices=["equals", "not-equals", "greater-than", "greater-or-equal", "less-than", "less-or-equal"])
|
|
7027
7440
|
p_eccf.add_argument("--value", required=True)
|
|
7028
7441
|
p_eccf.add_argument("--target", required=True, dest="targets", nargs="+",
|
|
7029
|
-
|
|
7442
|
+
action="extend",
|
|
7443
|
+
help="One or more target field keys/captions; repeat --target or list them after one flag")
|
|
7444
|
+
p_eccf_timing = p_eccf.add_mutually_exclusive_group()
|
|
7445
|
+
p_eccf_timing.add_argument("--add-event", action="append", dest="add_events",
|
|
7446
|
+
help="Append a named timing, e.g. AfterCopy, preserving existing event bits")
|
|
7447
|
+
p_eccf_timing.add_argument("--event", action="append", dest="events",
|
|
7448
|
+
help="Select exact timings for a new rule; repeat per timing")
|
|
7449
|
+
|
|
7450
|
+
p_ecfc = sub.add_parser(
|
|
7451
|
+
"ensureConditionalFieldChange",
|
|
7452
|
+
help="Ensure one conditional field value-update service with optional trigger timings")
|
|
7453
|
+
p_ecfc_target = p_ecfc.add_mutually_exclusive_group(required=True)
|
|
7454
|
+
p_ecfc_target.add_argument("--formNumber", help="Exact form number")
|
|
7455
|
+
p_ecfc_target.add_argument("--form-name", dest="form_name", help="Exact form name or number")
|
|
7456
|
+
p_ecfc.add_argument("--entity", help="Optional exact entity; inferred from the fields")
|
|
7457
|
+
p_ecfc.add_argument("--condition-field", required=True, dest="condition_field")
|
|
7458
|
+
p_ecfc.add_argument("--operator", required=True,
|
|
7459
|
+
choices=["equals", "not-equals", "greater-than", "greater-or-equal",
|
|
7460
|
+
"less-than", "less-or-equal", "empty", "not-empty"])
|
|
7461
|
+
p_ecfc.add_argument("--value", help="Omit for empty/not-empty")
|
|
7462
|
+
p_ecfc.add_argument("--target", required=True, help="Field whose value-update service is called")
|
|
7463
|
+
p_ecfc_timing = p_ecfc.add_mutually_exclusive_group()
|
|
7464
|
+
p_ecfc_timing.add_argument("--add-event", action="append", dest="add_events",
|
|
7465
|
+
help="Append a named timing, e.g. AfterCopy")
|
|
7466
|
+
p_ecfc_timing.add_argument("--event", action="append", dest="events",
|
|
7467
|
+
help="Select the exact trigger timings for a new rule; repeat per timing")
|
|
7030
7468
|
|
|
7031
7469
|
p_eccv = sub.add_parser(
|
|
7032
7470
|
"ensureConditionalCopyFieldValue",
|
|
@@ -7035,13 +7473,15 @@ def _main_impl():
|
|
|
7035
7473
|
p_eccv_target.add_argument("--formNumber", help="Exact form number")
|
|
7036
7474
|
p_eccv_target.add_argument("--form-name", dest="form_name", help="Exact form name or number")
|
|
7037
7475
|
p_eccv.add_argument("--entity", help="Optional exact entity; inferred from fields when omitted")
|
|
7038
|
-
p_eccv.add_argument("--condition-field",
|
|
7476
|
+
p_eccv.add_argument("--condition-field", dest="condition_field")
|
|
7039
7477
|
p_eccv.add_argument(
|
|
7040
|
-
"--operator",
|
|
7478
|
+
"--operator",
|
|
7041
7479
|
choices=["equals", "not-equals", "greater-than", "greater-or-equal", "less-than", "less-or-equal"])
|
|
7042
|
-
p_eccv.add_argument("--value"
|
|
7043
|
-
p_eccv.add_argument("--source",
|
|
7044
|
-
p_eccv.add_argument("--target",
|
|
7480
|
+
p_eccv.add_argument("--value")
|
|
7481
|
+
p_eccv.add_argument("--source", help="Source field key or caption")
|
|
7482
|
+
p_eccv.add_argument("--target", help="Target field key or caption")
|
|
7483
|
+
p_eccv.add_argument("--input", dest="input_file",
|
|
7484
|
+
help="JSON {mappings:[{source,target}], optional conditionField/operator/value/ruleId/branch}; branch defaults to true (condition met), false executes when not met")
|
|
7045
7485
|
p_eccv.add_argument("--only-empty-target", action="store_true", dest="only_empty_target")
|
|
7046
7486
|
|
|
7047
7487
|
p_ecca = sub.add_parser(
|
|
@@ -7076,10 +7516,12 @@ def _main_impl():
|
|
|
7076
7516
|
"--operator",
|
|
7077
7517
|
choices=["equals", "not-equals", "greater-than", "greater-or-equal", "less-than", "less-or-equal", "empty", "not-empty"])
|
|
7078
7518
|
p_eccr.add_argument("--value")
|
|
7079
|
-
p_eccr.add_argument("--source-reference",
|
|
7080
|
-
p_eccr.add_argument("--source-property",
|
|
7519
|
+
p_eccr.add_argument("--source-reference", dest="source_reference")
|
|
7520
|
+
p_eccr.add_argument("--source-property", dest="source_property",
|
|
7081
7521
|
help="Exact property key already declared in the source field RefProps")
|
|
7082
|
-
p_eccr.add_argument("--target",
|
|
7522
|
+
p_eccr.add_argument("--target", help="Same-entity target field")
|
|
7523
|
+
p_eccr.add_argument("--input", dest="input_file",
|
|
7524
|
+
help="JSON {mappings:[{sourceReference,sourceProperty,target}], optional conditionField/operator/value/ruleId/branch}; branch defaults to true (condition met), false executes when not met")
|
|
7083
7525
|
p_eccr.add_argument("--only-empty-target", action="store_true", dest="only_empty_target")
|
|
7084
7526
|
|
|
7085
7527
|
p_eqc = sub.add_parser(
|
|
@@ -7093,6 +7535,12 @@ def _main_impl():
|
|
|
7093
7535
|
help="Source QtyField key or caption")
|
|
7094
7536
|
p_eqc.add_argument("--target-qty", required=True, action="append", dest="target_qty",
|
|
7095
7537
|
help="Target QtyField key or caption; repeat for multiple targets")
|
|
7538
|
+
for service_parser in (p_eccs, p_eccv, p_ecca, p_eccr, p_eqc):
|
|
7539
|
+
timing = service_parser.add_mutually_exclusive_group()
|
|
7540
|
+
timing.add_argument("--event", action="append", dest="events",
|
|
7541
|
+
help="Select exact timings for a new rule; repeat per timing")
|
|
7542
|
+
timing.add_argument("--add-event", action="append", dest="add_events",
|
|
7543
|
+
help="Append a timing while preserving existing timings")
|
|
7096
7544
|
|
|
7097
7545
|
p_ur = sub.add_parser("updateRule", help="Modify rule")
|
|
7098
7546
|
p_ur_target = p_ur.add_mutually_exclusive_group(required=True)
|
|
@@ -7231,6 +7679,19 @@ def _main_impl():
|
|
|
7231
7679
|
p_sp_input.add_argument("--input", dest="input_file",
|
|
7232
7680
|
help='Legacy one-element property map, e.g. {"MustInput":true}')
|
|
7233
7681
|
|
|
7682
|
+
p_rename = sub.add_parser(
|
|
7683
|
+
"renameElementKey",
|
|
7684
|
+
help="Rename one existing field/control/entry key and migrate verified rule references",
|
|
7685
|
+
)
|
|
7686
|
+
p_rename_form = p_rename.add_mutually_exclusive_group(required=True)
|
|
7687
|
+
p_rename_form.add_argument("--formNumber", help="Exact existing form number")
|
|
7688
|
+
p_rename_form.add_argument("--form-name", dest="form_name", help="Exact form name or number")
|
|
7689
|
+
p_rename.add_argument("--app-code", "--appCode", default=None, dest="app_code")
|
|
7690
|
+
p_rename.add_argument("--app-name", "--appName", default=None, dest="app_name")
|
|
7691
|
+
p_rename.add_argument("--element", required=True, help="Current exact key or unique display name")
|
|
7692
|
+
p_rename.add_argument("--new-key", required=True, dest="new_key",
|
|
7693
|
+
help="Requested new logical key; developer prefix is applied once")
|
|
7694
|
+
|
|
7234
7695
|
p_sf = sub.add_parser("setFeatures", help="Set feature control (complex property)")
|
|
7235
7696
|
p_sf_form = p_sf.add_mutually_exclusive_group(required=True)
|
|
7236
7697
|
p_sf_form.add_argument("--formNumber", help="Exact existing form number")
|
|
@@ -7312,6 +7773,21 @@ def _main_impl():
|
|
|
7312
7773
|
p_scp.add_argument("--input", required=True, dest="input_file",
|
|
7313
7774
|
help="Property data JSON file path or inline JSON string")
|
|
7314
7775
|
|
|
7776
|
+
p_cdm = sub.add_parser(
|
|
7777
|
+
"configureDropdownMenu",
|
|
7778
|
+
help="Edit one button dropdown menu, preserving existing identities in one save",
|
|
7779
|
+
)
|
|
7780
|
+
p_cdm_form = p_cdm.add_mutually_exclusive_group(required=True)
|
|
7781
|
+
p_cdm_form.add_argument("--formNumber", help="Exact existing form number")
|
|
7782
|
+
p_cdm_form.add_argument("--form-name", dest="form_name", help="Exact form name or number")
|
|
7783
|
+
p_cdm_target = p_cdm.add_mutually_exclusive_group()
|
|
7784
|
+
p_cdm_target.add_argument("--elementKey", help="Persisted button/toolbar-item key")
|
|
7785
|
+
p_cdm_target.add_argument("--element", help="Exact key or unique display name")
|
|
7786
|
+
p_cdm.add_argument("--input", required=True, dest="input_file",
|
|
7787
|
+
help="Dropdown-menu intent JSON or UTF-8 JSON file")
|
|
7788
|
+
p_cdm.add_argument("--locale",
|
|
7789
|
+
help="Exact language key returned for an ambiguous multilingual menu title")
|
|
7790
|
+
|
|
7315
7791
|
p_scs = sub.add_parser(
|
|
7316
7792
|
"setCustomStyle",
|
|
7317
7793
|
help="Set one element's custom CSS; the CLI owns designer-compatible encoding",
|
|
@@ -7463,7 +7939,7 @@ def _main_impl():
|
|
|
7463
7939
|
help=("Must-input condition JSON with RuleCondition and RuleDescription; "
|
|
7464
7940
|
"the CLI supplies _Type_=PreCondition; RuleTransExpr and Id are optional"))
|
|
7465
7941
|
p_smic_condition.add_argument(
|
|
7466
|
-
"--when-element",
|
|
7942
|
+
"--when-element", "--condition-field",
|
|
7467
7943
|
help="Exact trigger-field key or business display name for a simple comparison")
|
|
7468
7944
|
p_smic_condition.add_argument(
|
|
7469
7945
|
"--clear", action="store_true",
|
|
@@ -7474,6 +7950,8 @@ def _main_impl():
|
|
|
7474
7950
|
"typed from the trigger field, while selection labels resolve to persisted values"))
|
|
7475
7951
|
p_smic.add_argument(
|
|
7476
7952
|
"--operator", choices=["=", "<>", ">", ">=", "<", "<=", "empty", "not-empty"],
|
|
7953
|
+
type=lambda value: {"equals": "=", "not-equals": "<>", "greater-than": ">",
|
|
7954
|
+
"less-than": "<", "greater-or-equal": ">=", "less-or-equal": "<="}.get(value, value),
|
|
7477
7955
|
help="Comparison operator used with --when-element; empty/not-empty take no value")
|
|
7478
7956
|
p_smic.add_argument(
|
|
7479
7957
|
"--value", dest="when_value",
|
|
@@ -7792,12 +8270,12 @@ def _main_impl():
|
|
|
7792
8270
|
p_au.add_argument("--bizAppId", default=None, help="App ID")
|
|
7793
8271
|
p_au.add_argument("--bizAppNumber", default=None, help="应forNumber")
|
|
7794
8272
|
p_au.add_argument("--name", required=True, help="Feature group名称")
|
|
7795
|
-
p_au.add_argument("--number",
|
|
7796
|
-
help="Feature
|
|
8273
|
+
p_au.add_argument("--number", required=True,
|
|
8274
|
+
help="Feature group code (required by the platform; preserve the requested code)")
|
|
7797
8275
|
p_au.add_argument("--description", default=None,
|
|
7798
|
-
help="Feature group
|
|
8276
|
+
help="Feature group description (defaults to the group name)")
|
|
7799
8277
|
p_au.add_argument("--seq", type=int, default=None,
|
|
7800
|
-
help="序号 [
|
|
8278
|
+
help="序号 [1, 999];省略时按现有分组的已验证序号追加")
|
|
7801
8279
|
p_au.add_argument("--parentUnitId", default=None, help="上级Feature groupID(空=一级分组)")
|
|
7802
8280
|
p_au.add_argument("--isLeaf", default=None, choices=["true", "false"], help="是否叶子节点(Defaulttrue)")
|
|
7803
8281
|
|
|
@@ -7973,7 +8451,7 @@ def _main_impl():
|
|
|
7973
8451
|
p_lf.add_argument("--pageType", required=True, choices=["Empty", "Inherit"],
|
|
7974
8452
|
help="页面类型:Empty(空白)/ Inherit(继承)")
|
|
7975
8453
|
|
|
7976
|
-
# KSQL
|
|
8454
|
+
# KSQL 导出/查询(预置数据/基础数据,管理员/子管理员)—— 本地补丁 #14
|
|
7977
8455
|
p_ksql_r = sub.add_parser("ksqlListDbRoutes", help="KSQL 分库列表(sys/secd/scm...)")
|
|
7978
8456
|
p_ksql_dt = sub.add_parser("ksqlListDbTypes", help="KSQL 支持的数据库类型列表")
|
|
7979
8457
|
p_ksql_tr = sub.add_parser("ksqlTranslate", help="翻译 KSQL 语句")
|
|
@@ -8246,7 +8724,7 @@ def _main_impl():
|
|
|
8246
8724
|
p_acs_target.add_argument("--form-name", dest="form_name", help="Exact form name or number")
|
|
8247
8725
|
p_acs.add_argument(
|
|
8248
8726
|
"--input", dest="input_file", required=True,
|
|
8249
|
-
help="
|
|
8727
|
+
help="JSON object with controls [{type, name}]; optional parent defaults to the live form root",
|
|
8250
8728
|
)
|
|
8251
8729
|
p_acs.add_argument(
|
|
8252
8730
|
"--app-root", default=None, dest="app_root",
|
|
@@ -8714,7 +9192,11 @@ def _main_impl():
|
|
|
8714
9192
|
_load_ops(args.input_file)
|
|
8715
9193
|
)
|
|
8716
9194
|
except (OSError, ValueError, TypeError, json.JSONDecodeError) as exc:
|
|
8717
|
-
raise _CliUsageError(str(exc)
|
|
9195
|
+
raise _CliUsageError(str(exc), {
|
|
9196
|
+
**(getattr(exc, "recovery", None) or {}),
|
|
9197
|
+
"describeCommand": "describeCommand addControls",
|
|
9198
|
+
"payloadSchemaRef": _CATALOG_PAYLOAD_SCHEMA_REFS["addControls"],
|
|
9199
|
+
}) from exc
|
|
8718
9200
|
|
|
8719
9201
|
if args.cmd == "setMustInputCondition":
|
|
8720
9202
|
if args.when_element and args.when_equals is None and args.operator is None:
|
|
@@ -8739,6 +9221,7 @@ def _main_impl():
|
|
|
8739
9221
|
_route_unstructured_prints_to_stderr()
|
|
8740
9222
|
app_root = _resolve_app_root_arg(args)
|
|
8741
9223
|
if app_root:
|
|
9224
|
+
validate_execution_binding(app_root)
|
|
8742
9225
|
_retarget_runtime_dir(app_root)
|
|
8743
9226
|
|
|
8744
9227
|
# 设置全局输出文件(--output)
|
|
@@ -8782,7 +9265,7 @@ def _main_impl():
|
|
|
8782
9265
|
raise _CliUsageError("--fields carries each field specification; do not combine single-field specifications")
|
|
8783
9266
|
if args.field_type != "TextField" or args.required or any(getattr(args, name, None) for name in (
|
|
8784
9267
|
"basedata", "ref_bill", "mul_table", "unit_field", "flex_type", "ref_base_field", "ref_display_prop", "asstTypeId", "asst_type", "extAppId")):
|
|
8785
|
-
raise _CliUsageError("--fields
|
|
9268
|
+
raise _CliUsageError("--fields carries reference/type options on each item; do not combine single-field or selected-extension flags")
|
|
8786
9269
|
validate_add_fields_input(args.fields)
|
|
8787
9270
|
if args.validate_only:
|
|
8788
9271
|
pjson({"status": True, "data": {"validated": len(args.fields), "writeAttempted": False,
|
|
@@ -9001,11 +9484,23 @@ def _main_impl():
|
|
|
9001
9484
|
pjson(_dynamic_identity_failure)
|
|
9002
9485
|
sys.exit(8)
|
|
9003
9486
|
|
|
9487
|
+
if (raw_artifact.get("entities") or [{}])[0].get("type") == "MainEntity":
|
|
9488
|
+
_retained_creation = _dynamic_creation_receipt(app_root, artifact_file)
|
|
9489
|
+
if _retained_creation:
|
|
9490
|
+
pjson(_retained_creation)
|
|
9491
|
+
return 3
|
|
9492
|
+
|
|
9004
9493
|
_dynamic_input_failure = _dynamic_form_artifact_preflight(raw_artifact)
|
|
9005
9494
|
if _dynamic_input_failure:
|
|
9006
9495
|
pjson(_dynamic_input_failure)
|
|
9007
9496
|
return 1
|
|
9008
9497
|
|
|
9498
|
+
_dynamic_reference_failure = _resolve_dynamic_reference_fields(
|
|
9499
|
+
raw_artifact, app_root=app_root, isv=(_load_workspace_at(app_root) or {}).get("isv", ""))
|
|
9500
|
+
if _dynamic_reference_failure:
|
|
9501
|
+
pjson(_dynamic_reference_failure)
|
|
9502
|
+
return 1
|
|
9503
|
+
|
|
9009
9504
|
# 2) workspace 回退:优先 --app-root 下 workspace;其次全局 RUNTIME_DIR。
|
|
9010
9505
|
# 显式名称尚未解析时只允许身份自举,禁止自动创建默认分组。
|
|
9011
9506
|
ws = _load_workspace_for_write(
|
|
@@ -9016,6 +9511,12 @@ def _main_impl():
|
|
|
9016
9511
|
)
|
|
9017
9512
|
_isv = ws.get("isv", "")
|
|
9018
9513
|
_app = ws.get("appNumber", "")
|
|
9514
|
+
if _explicit_dynamic_identity:
|
|
9515
|
+
_standard = ws.get("standard")
|
|
9516
|
+
if isinstance(_standard, str):
|
|
9517
|
+
_standard = _standard.strip().lower() == "true"
|
|
9518
|
+
_explicit_dynamic_identity = make_entity_key_for(
|
|
9519
|
+
_explicit_dynamic_identity, _isv, standard=_standard)
|
|
9019
9520
|
|
|
9020
9521
|
# 3) 模板resolve:CLI > artifact.templateNumber > manifest 对应项
|
|
9021
9522
|
_tpl = _resolve_template_number(args.template, artifact_file, manifest)
|
|
@@ -9229,6 +9730,12 @@ def _main_impl():
|
|
|
9229
9730
|
except Exception as exc:
|
|
9230
9731
|
_apply_dynamic_ui_failure_contract(result, _summary_run, exc)
|
|
9231
9732
|
_apply_build_failure_contract(result, _summary_run)
|
|
9733
|
+
if ents0 and ents0[0].get("type") == "MainEntity":
|
|
9734
|
+
_retained_creation = _dynamic_creation_receipt(app_root, artifact_file, result)
|
|
9735
|
+
if _retained_creation:
|
|
9736
|
+
result.update(doNotRetryRun=True, retryable=False, retryAdvice="inspect_retained_form",
|
|
9737
|
+
reconciliation=_retained_creation["reconciliation"],
|
|
9738
|
+
modelInstruction=_retained_creation["modelInstruction"])
|
|
9232
9739
|
_append_meta_event(
|
|
9233
9740
|
"buildMeta.finished",
|
|
9234
9741
|
command="run",
|
|
@@ -9334,6 +9841,7 @@ def _main_impl():
|
|
|
9334
9841
|
cfg,
|
|
9335
9842
|
app_root,
|
|
9336
9843
|
identity_only=bool(args.bizUnitId or requested_biz_unit_name),
|
|
9844
|
+
app_code=args.app_code,
|
|
9337
9845
|
)
|
|
9338
9846
|
_isv = ws.get("isv", "")
|
|
9339
9847
|
_app = ws.get("appNumber", "")
|
|
@@ -9370,14 +9878,19 @@ def _main_impl():
|
|
|
9370
9878
|
BuildDependencyError,
|
|
9371
9879
|
bind_ref_bill_ids,
|
|
9372
9880
|
collect_ref_bill_targets,
|
|
9373
|
-
|
|
9881
|
+
collect_build_dependencies,
|
|
9882
|
+
partition_build_wave,
|
|
9374
9883
|
describe_build_plan,
|
|
9375
9884
|
plan_build_waves,
|
|
9376
9885
|
split_deferred_ref_bill_writes,
|
|
9377
9886
|
split_deferred_property_fields,
|
|
9378
9887
|
validate_deferred_property_field_sources,
|
|
9888
|
+
validate_ref_bill_owner,
|
|
9379
9889
|
)
|
|
9380
9890
|
|
|
9891
|
+
dependencies = collect_build_dependencies(artifact_files, manifest=manifest, isv=_isv)
|
|
9892
|
+
if flat_parallel and any(dependencies.values()):
|
|
9893
|
+
raise _CliUsageError("BATCH_INPUT_DEPENDENCIES_REQUIRE_WAVES: referenced objects must finish before dependent writes")
|
|
9381
9894
|
if flat_parallel or len(artifact_files) <= 1:
|
|
9382
9895
|
build_waves = [artifact_files]
|
|
9383
9896
|
else:
|
|
@@ -9386,6 +9899,7 @@ def _main_impl():
|
|
|
9386
9899
|
artifact_files,
|
|
9387
9900
|
manifest=manifest,
|
|
9388
9901
|
isv=_isv,
|
|
9902
|
+
dependencies=dependencies,
|
|
9389
9903
|
)
|
|
9390
9904
|
except BuildDependencyError as exc:
|
|
9391
9905
|
pjson(exc.to_dict())
|
|
@@ -9418,7 +9932,7 @@ def _main_impl():
|
|
|
9418
9932
|
]
|
|
9419
9933
|
continue
|
|
9420
9934
|
prepared = decorated
|
|
9421
|
-
artifact_errors = validate_deferred_property_field_sources(prepared)
|
|
9935
|
+
artifact_errors = validate_deferred_property_field_sources(prepared) + validate_ref_bill_owner(prepared)
|
|
9422
9936
|
validation_copy = copy.deepcopy(prepared)
|
|
9423
9937
|
validation_copy.pop("listConfiguration", None)
|
|
9424
9938
|
validation_copy.pop("formComposition", None)
|
|
@@ -9463,6 +9977,20 @@ def _main_impl():
|
|
|
9463
9977
|
})
|
|
9464
9978
|
sys.exit(3)
|
|
9465
9979
|
|
|
9980
|
+
from _core.build_receipts import BuildReceipts
|
|
9981
|
+
from kd_env_loader import load_environment_identity
|
|
9982
|
+
progress = BuildReceipts(
|
|
9983
|
+
app_root, biz_app_id, load_environment_identity(), prepared_artifacts,
|
|
9984
|
+
resume_result=args.resume_result, reconcile_only=args.reconcile_only,
|
|
9985
|
+
options=["--bizAppId", str(biz_app_id)]
|
|
9986
|
+
+ (["--bizUnitId", str(biz_unit_id)] if biz_unit_id else [])
|
|
9987
|
+
+ (["--template", args.template] if args.template is not None else []),
|
|
9988
|
+
) if app_root else None
|
|
9989
|
+
if args.reconcile_only and not args.resume_result:
|
|
9990
|
+
raise _CliUsageError("BATCH_INPUT_RESUME_REQUIRED: --reconcile-only requires --resume-result")
|
|
9991
|
+
if args.resume_result and progress is None:
|
|
9992
|
+
raise _CliUsageError("BATCH_INPUT_APP_ROOT_REQUIRED: resume requires --app-root")
|
|
9993
|
+
|
|
9466
9994
|
from threading import Lock
|
|
9467
9995
|
ref_bill_id_cache = {}
|
|
9468
9996
|
ref_bill_id_lock = Lock()
|
|
@@ -9475,36 +10003,39 @@ def _main_impl():
|
|
|
9475
10003
|
cached = ref_bill_id_cache.get(key)
|
|
9476
10004
|
if cached:
|
|
9477
10005
|
return cached
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
if
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
if
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
10006
|
+
queried = do_query_forms(cfg, token, keyword=target_number)
|
|
10007
|
+
if not _is_api_success(queried):
|
|
10008
|
+
raise RuntimeError("REF_BILL_LOOKUP_UNCONFIRMED: " + (_extract_error_message(queried) or "queryForms failed"))
|
|
10009
|
+
rows = (queried or {}).get("data") or []
|
|
10010
|
+
exact = [
|
|
10011
|
+
row for row in rows
|
|
10012
|
+
if isinstance(row, dict)
|
|
10013
|
+
and str(row.get("formNumber") or "").strip().lower() == key
|
|
10014
|
+
]
|
|
10015
|
+
unique_ids = {
|
|
10016
|
+
str(row.get("formId") or "").strip()
|
|
10017
|
+
for row in exact
|
|
10018
|
+
if str(row.get("formId") or "").strip()
|
|
10019
|
+
}
|
|
10020
|
+
if len(unique_ids) != 1:
|
|
10021
|
+
raise ValueError(
|
|
10022
|
+
f"REF_BILL_TARGET_UNRESOLVED: formNumber={target_number!r} "
|
|
10023
|
+
f"matched {len(unique_ids)} exact form IDs"
|
|
10024
|
+
)
|
|
10025
|
+
non_bill = [
|
|
10026
|
+
str(row.get("modelType") or "").strip()
|
|
10027
|
+
for row in exact
|
|
10028
|
+
if str(row.get("modelType") or "").strip() != "BillFormModel"
|
|
10029
|
+
]
|
|
10030
|
+
if non_bill:
|
|
10031
|
+
raise ValueError(
|
|
10032
|
+
f"REF_BILL_TARGET_TYPE_MISMATCH: formNumber={target_number!r} "
|
|
10033
|
+
f"is not a BillFormModel"
|
|
10034
|
+
)
|
|
10035
|
+
form_id = next(iter(unique_ids))
|
|
10036
|
+
with ref_bill_id_lock:
|
|
9506
10037
|
ref_bill_id_cache[key] = form_id
|
|
9507
|
-
|
|
10038
|
+
return form_id
|
|
9508
10039
|
|
|
9509
10040
|
def _bind_ref_bill_targets(artifact):
|
|
9510
10041
|
resolved = {}
|
|
@@ -9588,8 +10119,16 @@ def _main_impl():
|
|
|
9588
10119
|
ops=decorated_ops,
|
|
9589
10120
|
)
|
|
9590
10121
|
if not _is_api_success(result):
|
|
9591
|
-
raise
|
|
9592
|
-
"REF_BILL_ADD_FAILED: " + (_extract_error_message(result) or "modifyMeta failed")
|
|
10122
|
+
raise _PostBuildWriteError(
|
|
10123
|
+
"REF_BILL_ADD_FAILED: " + (_extract_error_message(result) or "modifyMeta failed"),
|
|
10124
|
+
result,
|
|
10125
|
+
{"command": "modifyMeta", "appRoot": app_root,
|
|
10126
|
+
"input": {"formId": str(receipt.get("formId") or "").strip(),
|
|
10127
|
+
"formNumber": root_number, "ops": raw_ops},
|
|
10128
|
+
"expectedFields": [{"fieldKey": item["field"]["fieldKey"],
|
|
10129
|
+
"fieldType": "RefBillField",
|
|
10130
|
+
"billEntityId": item["field"].get("billEntityId")}
|
|
10131
|
+
for item in deferred]},
|
|
9593
10132
|
)
|
|
9594
10133
|
verified = []
|
|
9595
10134
|
for item in deferred:
|
|
@@ -9948,7 +10487,33 @@ def _main_impl():
|
|
|
9948
10487
|
)
|
|
9949
10488
|
|
|
9950
10489
|
def _one(afile):
|
|
10490
|
+
reused = progress.reused(afile) if progress else None
|
|
10491
|
+
if reused:
|
|
10492
|
+
return reused
|
|
9951
10493
|
t0 = _time.time()
|
|
10494
|
+
if args.reconcile_only:
|
|
10495
|
+
previous = progress.current(afile)
|
|
10496
|
+
if previous.get("writeAttempted") is False:
|
|
10497
|
+
return previous
|
|
10498
|
+
receipt = previous.get("result") or {}
|
|
10499
|
+
targets = (receipt.get("data") or {}).get("entityResults", [])
|
|
10500
|
+
if targets and all(row.get("formId") for row in targets) and len(targets) == len(previous["targetNumbers"]):
|
|
10501
|
+
verification = _verify_build_meta_readback(cfg, token, prepared_artifacts[afile], receipt)
|
|
10502
|
+
return {**previous, "ok": False, "phase": "retainedRepairRequired",
|
|
10503
|
+
"reconciliationVerification": verification,
|
|
10504
|
+
"errorCode": "BATCH_WRITE_RECONCILIATION_REQUIRED",
|
|
10505
|
+
"errors": ["Retained identity read back; finish the original repair plan through existing-form writers. Creation remains blocked."]}
|
|
10506
|
+
observed = [do_query_forms(cfg, token, keyword=number) for number in previous["targetNumbers"]]
|
|
10507
|
+
absent = all(_is_api_success(result) and isinstance(result.get("data"), list)
|
|
10508
|
+
and not any(str(row.get("formNumber") or "").casefold() == number.casefold()
|
|
10509
|
+
for row in result["data"] if isinstance(row, dict))
|
|
10510
|
+
for number, result in zip(previous["targetNumbers"], observed))
|
|
10511
|
+
return {**previous, "ok": False, "phase": "reconciledAbsent" if absent else "writeUnknown",
|
|
10512
|
+
"writeAttempted": False if absent else previous.get("writeAttempted"),
|
|
10513
|
+
"absenceEvidence": observed if absent else None,
|
|
10514
|
+
"errorCode": "BATCH_INPUT_READY_TO_RESUME" if absent else "BATCH_WRITE_RECONCILIATION_REQUIRED",
|
|
10515
|
+
"errors": ["Absence confirmed; use the returned resume command" if absent
|
|
10516
|
+
else "Read did not prove absence; do not adopt a same-number object as this batch's success"]}
|
|
9952
10517
|
raw = copy.deepcopy(prepared_artifacts[afile])
|
|
9953
10518
|
tpl_one = tpl_per_artifact.get(afile, "bos_billtpl")
|
|
9954
10519
|
dec = raw
|
|
@@ -9957,12 +10522,16 @@ def _main_impl():
|
|
|
9957
10522
|
try:
|
|
9958
10523
|
ref_bill_errors = _bind_ref_bill_targets(dec)
|
|
9959
10524
|
except Exception as exc:
|
|
9960
|
-
|
|
10525
|
+
return {"artifact": os.path.basename(afile), "ok": False, "phase": "resolveRefBill",
|
|
10526
|
+
"writeAttempted": False, "errorCode": _exception_error_code(exc)[0],
|
|
10527
|
+
"errors": [str(exc)], "elapsed_sec": round(_time.time() - t0, 2)}
|
|
9961
10528
|
if ref_bill_errors:
|
|
9962
10529
|
return {
|
|
9963
10530
|
"artifact": os.path.basename(afile),
|
|
9964
10531
|
"ok": False,
|
|
9965
10532
|
"phase": "resolveRefBill",
|
|
10533
|
+
"writeAttempted": False,
|
|
10534
|
+
"errorCode": "REF_BILL_TARGET_UNRESOLVED",
|
|
9966
10535
|
"errors": ref_bill_errors,
|
|
9967
10536
|
"elapsed_sec": round(_time.time() - t0, 2),
|
|
9968
10537
|
}
|
|
@@ -9976,19 +10545,39 @@ def _main_impl():
|
|
|
9976
10545
|
"artifact": os.path.basename(afile),
|
|
9977
10546
|
"ok": False,
|
|
9978
10547
|
"phase": "preValidate",
|
|
10548
|
+
"writeAttempted": False,
|
|
10549
|
+
"errorCode": "ARTIFACT_INPUT_INVALID",
|
|
9979
10550
|
"errors": errs,
|
|
9980
10551
|
"elapsed_sec": round(_time.time() - t0, 2),
|
|
9981
10552
|
}
|
|
10553
|
+
if progress:
|
|
10554
|
+
try:
|
|
10555
|
+
progress.start(afile)
|
|
10556
|
+
except ValueError as exc:
|
|
10557
|
+
return {"artifact": os.path.basename(afile), "ok": False,
|
|
10558
|
+
"phase": "preflight", "writeAttempted": False,
|
|
10559
|
+
"errorCode": "BATCH_WRITE_RECONCILIATION_REQUIRED", "errors": [str(exc)],
|
|
10560
|
+
"elapsed_sec": round(_time.time() - t0, 2)}
|
|
9982
10561
|
try:
|
|
9983
10562
|
raw_result = do_build_meta(
|
|
9984
10563
|
cfg, token, dec,
|
|
9985
10564
|
biz_app_id=biz_app_id,
|
|
9986
10565
|
biz_unit_id=biz_unit_id,
|
|
9987
10566
|
template_number=tpl_one,
|
|
10567
|
+
on_write_result=(lambda response: progress.finish(afile, {
|
|
10568
|
+
"ok": False, "phase": "rootReceipt", "writeAttempted": True,
|
|
10569
|
+
"result": _transform_build_meta_result(response),
|
|
10570
|
+
})) if progress else None,
|
|
9988
10571
|
)
|
|
9989
10572
|
transformed = _transform_build_meta_result(raw_result)
|
|
9990
10573
|
# Derive write success from entity counts, never transport status alone.
|
|
9991
10574
|
summary_one = _summarize_build_meta_result(transformed, artifact=dec)
|
|
10575
|
+
if progress:
|
|
10576
|
+
progress.finish(afile, {"ok": False, "phase": "buildMeta",
|
|
10577
|
+
"writeAttempted": raw_result.get("writeAttempted", True),
|
|
10578
|
+
"result": transformed, "summary": summary_one,
|
|
10579
|
+
"metadataRetained": any(row.get("formId") for row in
|
|
10580
|
+
(transformed.get("data") or {}).get("entityResults", []))})
|
|
9992
10581
|
verification = None
|
|
9993
10582
|
final_ok = bool(summary_one.get("ok"))
|
|
9994
10583
|
phase = "buildMeta"
|
|
@@ -10000,6 +10589,7 @@ def _main_impl():
|
|
|
10000
10589
|
list_configuration_result = None
|
|
10001
10590
|
dynamic_ui_result = None
|
|
10002
10591
|
form_composition_result = None
|
|
10592
|
+
pending_write = None
|
|
10003
10593
|
property_partial_only = (
|
|
10004
10594
|
not final_ok
|
|
10005
10595
|
and int(summary_one.get("successCount") or 0) > 0
|
|
@@ -10039,6 +10629,9 @@ def _main_impl():
|
|
|
10039
10629
|
final_ok = False
|
|
10040
10630
|
phase = "addRefBill"
|
|
10041
10631
|
errors.append(str(exc))
|
|
10632
|
+
if isinstance(exc, _PostBuildWriteError):
|
|
10633
|
+
ref_bill_result = {"modifyMeta": exc.result, "status": False}
|
|
10634
|
+
pending_write = exc.pending_write
|
|
10042
10635
|
if final_ok and deferred_ref_bill_phase["propertySettings"]:
|
|
10043
10636
|
try:
|
|
10044
10637
|
ref_bill_property_result = _apply_ref_bill_property_settings(
|
|
@@ -10148,11 +10741,20 @@ def _main_impl():
|
|
|
10148
10741
|
f"failed={verify_summary.get('failed', 0)} "
|
|
10149
10742
|
f"errors={verify_summary.get('errors', 0)}"
|
|
10150
10743
|
)
|
|
10744
|
+
if not final_ok and phase != "buildMeta" and not summary_one.get("errorCode"):
|
|
10745
|
+
_attach_post_build_phase_failure(
|
|
10746
|
+
summary_one, transformed, phase=phase, app_root=app_root,
|
|
10747
|
+
artifact_path=afile, pending_write=pending_write,
|
|
10748
|
+
)
|
|
10151
10749
|
_apply_build_failure_contract(transformed, summary_one)
|
|
10152
10750
|
return {
|
|
10153
10751
|
"artifact": os.path.basename(afile),
|
|
10752
|
+
"artifactPath": os.path.abspath(afile),
|
|
10154
10753
|
"ok": final_ok,
|
|
10155
10754
|
"phase": phase,
|
|
10755
|
+
"writeAttempted": raw_result.get("writeAttempted", True),
|
|
10756
|
+
"errorCode": (transformed.get("errorCode") or "0") if phase == "buildMeta"
|
|
10757
|
+
else _exception_error_code(ValueError(errors[0]))[0] if errors else "0",
|
|
10156
10758
|
"result": transformed,
|
|
10157
10759
|
"summary": summary_one,
|
|
10158
10760
|
"refBillResult": ref_bill_result,
|
|
@@ -10173,19 +10775,40 @@ def _main_impl():
|
|
|
10173
10775
|
return {
|
|
10174
10776
|
"artifact": os.path.basename(afile),
|
|
10175
10777
|
"ok": False,
|
|
10176
|
-
"phase": "
|
|
10778
|
+
"phase": "writeUnknown",
|
|
10779
|
+
"writeAttempted": None,
|
|
10780
|
+
"errorCode": "BATCH_WRITE_RESULT_UNKNOWN",
|
|
10177
10781
|
"errors": [str(e)],
|
|
10178
10782
|
"elapsed_sec": round(_time.time() - t0, 2),
|
|
10179
10783
|
}
|
|
10180
10784
|
|
|
10785
|
+
def _cancelled(path):
|
|
10786
|
+
row = {"artifact": os.path.basename(path), "artifactPath": path, "ok": False,
|
|
10787
|
+
"phase": "cancelled", "writeAttempted": False,
|
|
10788
|
+
"errors": ["Not started because --fail-fast was requested"], "elapsed_sec": 0.0}
|
|
10789
|
+
return progress.finish(path, row) if progress else row
|
|
10790
|
+
|
|
10181
10791
|
results = []
|
|
10182
10792
|
fail_fast = bool(getattr(args, "fail_fast", False))
|
|
10183
10793
|
t_start = _time.time()
|
|
10184
10794
|
aborted = False
|
|
10185
10795
|
dependency_blocked = False
|
|
10186
|
-
|
|
10796
|
+
succeeded_paths = set()
|
|
10797
|
+
if progress:
|
|
10798
|
+
for source in artifact_files:
|
|
10799
|
+
reused = progress.reused(source)
|
|
10800
|
+
if reused:
|
|
10801
|
+
_cache_build_receipts(reused)
|
|
10802
|
+
succeeded_paths.add(os.path.normpath(os.path.abspath(source)))
|
|
10803
|
+
for wave_idx, wave_files in enumerate([artifact_files] if args.reconcile_only else build_waves, 1):
|
|
10187
10804
|
if aborted:
|
|
10188
|
-
|
|
10805
|
+
results.extend(_cancelled(path) for path in wave_files)
|
|
10806
|
+
continue
|
|
10807
|
+
wave_files, blocked = (wave_files, []) if args.reconcile_only else partition_build_wave(wave_files, dependencies, succeeded_paths)
|
|
10808
|
+
results.extend(progress.finish(row["artifactPath"], row) if progress else row for row in blocked)
|
|
10809
|
+
dependency_blocked = dependency_blocked or bool(blocked)
|
|
10810
|
+
if not wave_files:
|
|
10811
|
+
continue
|
|
10189
10812
|
if len(build_waves) > 1:
|
|
10190
10813
|
print(
|
|
10191
10814
|
f"[runBatch] —— wave {wave_idx}/{len(build_waves)} "
|
|
@@ -10194,14 +10817,19 @@ def _main_impl():
|
|
|
10194
10817
|
flush=True,
|
|
10195
10818
|
)
|
|
10196
10819
|
with ThreadPoolExecutor(max_workers=concurrency) as pool:
|
|
10197
|
-
wave_result_start = len(results)
|
|
10198
10820
|
futures = {pool.submit(_one, f): f for f in wave_files}
|
|
10199
10821
|
try:
|
|
10200
10822
|
for fut in as_completed(futures):
|
|
10823
|
+
if fut.cancelled():
|
|
10824
|
+
results.append(_cancelled(futures[fut]))
|
|
10825
|
+
continue
|
|
10201
10826
|
r = fut.result()
|
|
10827
|
+
if progress:
|
|
10828
|
+
r = progress.finish(futures[fut], r)
|
|
10202
10829
|
results.append(r)
|
|
10203
10830
|
if r.get("ok"):
|
|
10204
10831
|
_cache_build_receipts(r)
|
|
10832
|
+
succeeded_paths.add(os.path.normpath(os.path.abspath(futures[fut])))
|
|
10205
10833
|
print(
|
|
10206
10834
|
f"[runBatch] {'✅' if r['ok'] else '❌'} "
|
|
10207
10835
|
f"{r['artifact']} ({r['elapsed_sec']}s, phase={r['phase']})",
|
|
@@ -10213,27 +10841,9 @@ def _main_impl():
|
|
|
10213
10841
|
if not other.done():
|
|
10214
10842
|
other.cancel()
|
|
10215
10843
|
aborted = True
|
|
10216
|
-
break
|
|
10217
10844
|
finally:
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
wave_failed = any(
|
|
10221
|
-
not result["ok"] for result in results[wave_result_start:]
|
|
10222
|
-
)
|
|
10223
|
-
if wave_failed and wave_idx < len(build_waves):
|
|
10224
|
-
blocked = dependency_blocked_results(
|
|
10225
|
-
build_waves,
|
|
10226
|
-
failed_wave_index=wave_idx,
|
|
10227
|
-
)
|
|
10228
|
-
results.extend(blocked)
|
|
10229
|
-
dependency_blocked = bool(blocked)
|
|
10230
|
-
aborted = True
|
|
10231
|
-
print(
|
|
10232
|
-
f"[runBatch] ⛔ wave {wave_idx} 失败;"
|
|
10233
|
-
f"{len(blocked)} 份后续 artifact 因依赖屏障未写入",
|
|
10234
|
-
file=sys.stderr,
|
|
10235
|
-
flush=True,
|
|
10236
|
-
)
|
|
10845
|
+
# Running writes cannot be cancelled; collect their real receipts.
|
|
10846
|
+
pool.shutdown(wait=True)
|
|
10237
10847
|
|
|
10238
10848
|
total = round(_time.time() - t_start, 2)
|
|
10239
10849
|
succ = sum(1 for r in results if r["ok"])
|
|
@@ -10258,14 +10868,14 @@ def _main_impl():
|
|
|
10258
10868
|
retained = [
|
|
10259
10869
|
row for item in results if not item.get("ok")
|
|
10260
10870
|
for row in ((item.get("result") or {}).get("data") or {}).get("entityResults", [])
|
|
10261
|
-
if row.get("formId")
|
|
10871
|
+
if row.get("formId")
|
|
10262
10872
|
]
|
|
10263
10873
|
if retained:
|
|
10264
|
-
summary
|
|
10874
|
+
summary.setdefault("retainedTargets", retained)
|
|
10265
10875
|
summary["metadataRetained"] = True
|
|
10266
10876
|
summary["doNotRetryBuildMeta"] = True
|
|
10267
10877
|
summary.setdefault("retryAdvice", "reconcile_before_retry")
|
|
10268
|
-
summary
|
|
10878
|
+
summary.setdefault("reconciliation", [{"command": "getFormSchema", "formId": row["formId"]} for row in retained])
|
|
10269
10879
|
_append_meta_event(
|
|
10270
10880
|
"buildMeta.finished",
|
|
10271
10881
|
command="runBatch",
|
|
@@ -10276,11 +10886,12 @@ def _main_impl():
|
|
|
10276
10886
|
**_summary_batch,
|
|
10277
10887
|
)
|
|
10278
10888
|
# auto归档:批量场景下把所有成功 formNumber 一次性回写
|
|
10279
|
-
if app_root and _summary_batch.get("ok") and _summary_batch.get("formNumbers"):
|
|
10889
|
+
if not args.reconcile_only and app_root and _summary_batch.get("ok") and _summary_batch.get("formNumbers"):
|
|
10280
10890
|
_archive_finalize_metadata(app_root, _summary_batch["formNumbers"])
|
|
10281
10891
|
# auto-sync dym to repo (runBatch)
|
|
10282
10892
|
if (
|
|
10283
10893
|
_summary_batch.get("ok")
|
|
10894
|
+
and not args.reconcile_only
|
|
10284
10895
|
and not args.no_sync_dym
|
|
10285
10896
|
and app_root
|
|
10286
10897
|
and _summary_batch.get("formNumbers")
|
|
@@ -10295,7 +10906,18 @@ def _main_impl():
|
|
|
10295
10906
|
file=sys.stderr,
|
|
10296
10907
|
flush=True,
|
|
10297
10908
|
)
|
|
10298
|
-
|
|
10909
|
+
summary = _attach_batch_delivery(_attach_available_form_numbers(cfg, token, summary), app_root)
|
|
10910
|
+
if progress:
|
|
10911
|
+
summary["executionFinished"] = True
|
|
10912
|
+
summary["processId"] = os.getpid()
|
|
10913
|
+
summary.update({key: progress.receipt[key] for key in
|
|
10914
|
+
("executionBinding", "batchIdentity", "appRoot", "artifactPaths", "resultPath", "resume", "reconcile", "notStartedArtifacts")})
|
|
10915
|
+
summary.setdefault("reconciliation", []).extend(progress.receipt["reconciliation"])
|
|
10916
|
+
from _core.build_receipts import _atomic_json
|
|
10917
|
+
_atomic_json(progress.result_path, summary)
|
|
10918
|
+
elif app_root:
|
|
10919
|
+
summary = save_run_batch_result(summary, app_root, artifact_files)
|
|
10920
|
+
pjson(summary)
|
|
10299
10921
|
if args.verify:
|
|
10300
10922
|
verified = sum(
|
|
10301
10923
|
1 for item in results
|
|
@@ -10419,8 +11041,13 @@ def _main_impl():
|
|
|
10419
11041
|
|
|
10420
11042
|
elif args.cmd == "getDomainModel":
|
|
10421
11043
|
token = do_auth(cfg)
|
|
10422
|
-
result = do_get_domain_model(
|
|
10423
|
-
|
|
11044
|
+
result = do_get_domain_model(
|
|
11045
|
+
cfg, token, args.modelType,
|
|
11046
|
+
only_entity=args.onlyEntity,
|
|
11047
|
+
ext=args.ext,
|
|
11048
|
+
bizext=args.bizext,
|
|
11049
|
+
app_id=args.appId,
|
|
11050
|
+
)
|
|
10424
11051
|
pjson(_persist_domain_model(result, args.domain_output_file, args.modelType)
|
|
10425
11052
|
if args.domain_output_file else result)
|
|
10426
11053
|
|
|
@@ -10889,7 +11516,7 @@ def _main_impl():
|
|
|
10889
11516
|
plugin_config, form_number=args.formNumber,
|
|
10890
11517
|
biz_app_id=target.get("appId"))
|
|
10891
11518
|
result = do_register_plugin(cfg, token, form_number=args.formNumber,
|
|
10892
|
-
plugin_config=plugin_config)
|
|
11519
|
+
plugin_config=plugin_config, app_root=args.app_root)
|
|
10893
11520
|
if _is_api_success(result):
|
|
10894
11521
|
result = _attach_plugin_readback(
|
|
10895
11522
|
cfg, token, args.formNumber, plugin_config, result,
|
|
@@ -11002,6 +11629,18 @@ def _main_impl():
|
|
|
11002
11629
|
args.left, args.operator, args.right)
|
|
11003
11630
|
pjson(_attach_bound_form_identity(result, args))
|
|
11004
11631
|
|
|
11632
|
+
elif args.cmd == "ensureTimeDifferenceCalculation":
|
|
11633
|
+
token = do_auth(cfg)
|
|
11634
|
+
blocked = _bind_rule_domain_target(cfg, token, args, args.cmd)
|
|
11635
|
+
if blocked is not None:
|
|
11636
|
+
pjson(blocked)
|
|
11637
|
+
else:
|
|
11638
|
+
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11639
|
+
result = do_ensure_time_difference_calculation(
|
|
11640
|
+
cfg, token, args.formNumber, args.entity, args.target,
|
|
11641
|
+
args.start, args.end)
|
|
11642
|
+
pjson(_attach_bound_form_identity(result, args))
|
|
11643
|
+
|
|
11005
11644
|
elif args.cmd == "ensureRuleFunctionFormula":
|
|
11006
11645
|
token = do_auth(cfg)
|
|
11007
11646
|
blocked = _bind_rule_domain_target(cfg, token, args, args.cmd)
|
|
@@ -11015,11 +11654,14 @@ def _main_impl():
|
|
|
11015
11654
|
"RULE_FUNCTION_INPUT_INVALID: expected exactly {call:{functionId,args}}"
|
|
11016
11655
|
)
|
|
11017
11656
|
result = do_ensure_rule_function_formula(
|
|
11018
|
-
cfg, token, args.formNumber, args.entity, args.target, payload["call"]
|
|
11657
|
+
cfg, token, args.formNumber, args.entity, args.target, payload["call"],
|
|
11658
|
+
args.add_events, args.events,
|
|
11019
11659
|
)
|
|
11020
11660
|
pjson(_attach_bound_form_identity(result, args))
|
|
11021
11661
|
|
|
11022
11662
|
elif args.cmd == "ensureConditionalFieldState":
|
|
11663
|
+
if args.events and args.add_events:
|
|
11664
|
+
raise _CliUsageError("use --event for exact new timings or --add-event to append")
|
|
11023
11665
|
if args.operator in {"empty", "not-empty"} and args.value is not None:
|
|
11024
11666
|
raise _CliUsageError(f"--operator {args.operator} takes no --value")
|
|
11025
11667
|
if args.operator not in {"empty", "not-empty"} and args.value is None:
|
|
@@ -11032,7 +11674,8 @@ def _main_impl():
|
|
|
11032
11674
|
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11033
11675
|
result = do_ensure_conditional_field_state(
|
|
11034
11676
|
cfg, token, args.formNumber, args.entity, args.condition_field,
|
|
11035
|
-
args.operator, args.value, args.target, args.behavior
|
|
11677
|
+
args.operator, args.value, args.target, args.behavior,
|
|
11678
|
+
args.add_events, args.events)
|
|
11036
11679
|
pjson(_attach_bound_form_identity(result, args))
|
|
11037
11680
|
|
|
11038
11681
|
elif args.cmd == "ensureConditionalEntryStyle":
|
|
@@ -11068,7 +11711,8 @@ def _main_impl():
|
|
|
11068
11711
|
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11069
11712
|
result = do_ensure_conditional_control_state(
|
|
11070
11713
|
cfg, token, args.formNumber, args.entity, args.condition_field,
|
|
11071
|
-
args.operator, args.value, args.target_control, args.behavior
|
|
11714
|
+
args.operator, args.value, args.target_control, args.behavior,
|
|
11715
|
+
args.add_events, args.events)
|
|
11072
11716
|
pjson(_attach_bound_form_identity(result, args))
|
|
11073
11717
|
|
|
11074
11718
|
elif args.cmd == "ensureConditionalClearFields":
|
|
@@ -11080,20 +11724,46 @@ def _main_impl():
|
|
|
11080
11724
|
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11081
11725
|
result = do_ensure_conditional_clear_fields(
|
|
11082
11726
|
cfg, token, args.formNumber, args.entity, args.condition_field,
|
|
11083
|
-
args.operator, args.value, args.targets)
|
|
11727
|
+
args.operator, args.value, args.targets, args.add_events, args.events)
|
|
11728
|
+
pjson(_attach_bound_form_identity(result, args))
|
|
11729
|
+
|
|
11730
|
+
elif args.cmd == "ensureConditionalFieldChange":
|
|
11731
|
+
if args.events and args.add_events:
|
|
11732
|
+
raise _CliUsageError("use --event for exact new timings or --add-event to append")
|
|
11733
|
+
if args.operator in {"empty", "not-empty"} and args.value is not None:
|
|
11734
|
+
raise _CliUsageError(f"--operator {args.operator} takes no --value")
|
|
11735
|
+
if args.operator not in {"empty", "not-empty"} and args.value is None:
|
|
11736
|
+
raise _CliUsageError("--value is required with this --operator")
|
|
11737
|
+
token = do_auth(cfg)
|
|
11738
|
+
blocked = _bind_rule_domain_target(cfg, token, args, args.cmd)
|
|
11739
|
+
if blocked is not None:
|
|
11740
|
+
pjson(blocked)
|
|
11741
|
+
else:
|
|
11742
|
+
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11743
|
+
result = do_ensure_conditional_field_change(
|
|
11744
|
+
cfg, token, args.formNumber, args.entity, args.condition_field,
|
|
11745
|
+
args.operator, args.value, args.target, args.add_events, args.events)
|
|
11084
11746
|
pjson(_attach_bound_form_identity(result, args))
|
|
11085
11747
|
|
|
11086
11748
|
elif args.cmd == "ensureConditionalCopyFieldValue":
|
|
11749
|
+
if bool(args.input_file) == bool(args.source or args.target):
|
|
11750
|
+
raise _CliUsageError("use --input for several mappings, or --source/--target for one mapping")
|
|
11751
|
+
if args.input_file and args.only_empty_target:
|
|
11752
|
+
raise _CliUsageError("with --input put per-row onlyEmptyTarget inside mappings")
|
|
11753
|
+
if not args.input_file and not all((args.condition_field, args.operator, args.value, args.source, args.target)):
|
|
11754
|
+
raise _CliUsageError("single mapping requires condition-field, operator, value, source and target")
|
|
11087
11755
|
token = do_auth(cfg)
|
|
11088
11756
|
blocked = _bind_rule_domain_target(cfg, token, args, args.cmd)
|
|
11089
11757
|
if blocked is not None:
|
|
11090
11758
|
pjson(blocked)
|
|
11091
11759
|
else:
|
|
11092
11760
|
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11093
|
-
result =
|
|
11761
|
+
result = (do_ensure_copy_field_mappings(
|
|
11762
|
+
cfg, token, args.formNumber, _load_mapping_request(args),
|
|
11763
|
+
) if args.input_file else do_ensure_conditional_copy_field_value(
|
|
11094
11764
|
cfg, token, args.formNumber, args.entity, args.condition_field,
|
|
11095
11765
|
args.operator, args.value, args.source, args.target,
|
|
11096
|
-
args.only_empty_target)
|
|
11766
|
+
args.only_empty_target, args.add_events, args.events))
|
|
11097
11767
|
pjson(_attach_bound_form_identity(result, args))
|
|
11098
11768
|
|
|
11099
11769
|
elif args.cmd == "ensureConditionalCalculation":
|
|
@@ -11106,20 +11776,28 @@ def _main_impl():
|
|
|
11106
11776
|
result = do_ensure_conditional_calculation(
|
|
11107
11777
|
cfg, token, args.formNumber, args.entity, args.condition_field,
|
|
11108
11778
|
args.operator, args.value, args.target, args.left,
|
|
11109
|
-
args.calculation, args.right)
|
|
11779
|
+
args.calculation, args.right, args.add_events, args.events)
|
|
11110
11780
|
pjson(_attach_bound_form_identity(result, args))
|
|
11111
11781
|
|
|
11112
11782
|
elif args.cmd == "ensureConditionalCarryReferenceProperty":
|
|
11783
|
+
if bool(args.input_file) == bool(args.source_reference or args.source_property or args.target):
|
|
11784
|
+
raise _CliUsageError("use --input for several mappings, or --source-reference/--source-property/--target for one mapping")
|
|
11785
|
+
if args.input_file and args.only_empty_target:
|
|
11786
|
+
raise _CliUsageError("with --input put per-row onlyEmptyTarget inside mappings")
|
|
11787
|
+
if not args.input_file and not all((args.source_reference, args.source_property, args.target)):
|
|
11788
|
+
raise _CliUsageError("single mapping requires --source-reference, --source-property and --target")
|
|
11113
11789
|
token = do_auth(cfg)
|
|
11114
11790
|
blocked = _bind_rule_domain_target(cfg, token, args, args.cmd)
|
|
11115
11791
|
if blocked is not None:
|
|
11116
11792
|
pjson(blocked)
|
|
11117
11793
|
else:
|
|
11118
11794
|
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11119
|
-
result =
|
|
11795
|
+
result = (do_ensure_carry_reference_mappings(
|
|
11796
|
+
cfg, token, args.formNumber, _load_mapping_request(args),
|
|
11797
|
+
) if args.input_file else do_ensure_conditional_carry_reference_property(
|
|
11120
11798
|
cfg, token, args.formNumber, args.entity, args.condition_field,
|
|
11121
11799
|
args.operator, args.value, args.source_reference, args.source_property,
|
|
11122
|
-
args.target, args.only_empty_target)
|
|
11800
|
+
args.target, args.only_empty_target, args.add_events, args.events))
|
|
11123
11801
|
pjson(_attach_bound_form_identity(result, args))
|
|
11124
11802
|
|
|
11125
11803
|
elif args.cmd == "ensureQuantityConversion":
|
|
@@ -11131,7 +11809,7 @@ def _main_impl():
|
|
|
11131
11809
|
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11132
11810
|
result = do_ensure_quantity_conversion(
|
|
11133
11811
|
cfg, token, args.formNumber, args.entity,
|
|
11134
|
-
args.source_qty, args.target_qty)
|
|
11812
|
+
args.source_qty, args.target_qty, args.add_events, args.events)
|
|
11135
11813
|
pjson(_attach_bound_form_identity(result, args))
|
|
11136
11814
|
|
|
11137
11815
|
elif args.cmd == "updateRule":
|
|
@@ -11251,6 +11929,7 @@ def _main_impl():
|
|
|
11251
11929
|
)
|
|
11252
11930
|
else:
|
|
11253
11931
|
properties = _load_ops(args.input_file)
|
|
11932
|
+
properties = _normalize_key_property(args, properties)
|
|
11254
11933
|
element_key = args.elementKey or resolve_element_for_properties(
|
|
11255
11934
|
cfg, token, args.formNumber, args.element, properties)
|
|
11256
11935
|
result = do_set_properties_verified(
|
|
@@ -11258,6 +11937,15 @@ def _main_impl():
|
|
|
11258
11937
|
element_key=element_key, properties=properties)
|
|
11259
11938
|
pjson(_attach_bound_form_identity(result, args))
|
|
11260
11939
|
|
|
11940
|
+
elif args.cmd == "renameElementKey":
|
|
11941
|
+
token = do_auth(cfg)
|
|
11942
|
+
blocked = _bind_existing_form_target(cfg, token, args, args.cmd)
|
|
11943
|
+
if blocked is not None:
|
|
11944
|
+
pjson(blocked)
|
|
11945
|
+
else:
|
|
11946
|
+
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
11947
|
+
pjson(_attach_bound_form_identity(_rename_element_key(cfg, token, args), args))
|
|
11948
|
+
|
|
11261
11949
|
elif args.cmd == "setFeatures":
|
|
11262
11950
|
token = do_auth(cfg)
|
|
11263
11951
|
blocked = _bind_existing_form_target(cfg, token, args, args.cmd)
|
|
@@ -11395,6 +12083,20 @@ def _main_impl():
|
|
|
11395
12083
|
cfg, token, args.formNumber, element_key, args.css, args.clear)
|
|
11396
12084
|
pjson(_attach_bound_form_identity(result, args))
|
|
11397
12085
|
|
|
12086
|
+
elif args.cmd == "configureDropdownMenu":
|
|
12087
|
+
token = do_auth(cfg)
|
|
12088
|
+
blocked = _bind_existing_form_target(cfg, token, args, args.cmd)
|
|
12089
|
+
if blocked is not None:
|
|
12090
|
+
pjson(blocked)
|
|
12091
|
+
else:
|
|
12092
|
+
_assert_form_number_target_owned(cfg, token, args, args.cmd)
|
|
12093
|
+
element_key = args.elementKey or (resolve_element_selector(
|
|
12094
|
+
cfg, token, args.formNumber, args.element, category="control") if args.element else None)
|
|
12095
|
+
result = do_configure_dropdown_menu(
|
|
12096
|
+
cfg, token, args.formNumber, element_key, _load_ops(args.input_file),
|
|
12097
|
+
locale=args.locale)
|
|
12098
|
+
pjson(_attach_bound_form_identity(result, args))
|
|
12099
|
+
|
|
11398
12100
|
elif args.cmd == "setCollapseSummary":
|
|
11399
12101
|
token = do_auth(cfg)
|
|
11400
12102
|
blocked = _bind_existing_form_target(cfg, token, args, args.cmd)
|
|
@@ -12399,10 +13101,11 @@ def _main_impl():
|
|
|
12399
13101
|
pjson(result)
|
|
12400
13102
|
|
|
12401
13103
|
elif args.cmd == "addUnit":
|
|
13104
|
+
unit_app_id, unit_app_number = _unit_app_target(_resolve_app_root_arg(args), args.bizAppId, args.bizAppNumber)
|
|
12402
13105
|
token = do_auth(cfg)
|
|
12403
13106
|
result = do_add_unit(cfg, token,
|
|
12404
|
-
biz_app_id=
|
|
12405
|
-
biz_app_number=
|
|
13107
|
+
biz_app_id=unit_app_id,
|
|
13108
|
+
biz_app_number=unit_app_number,
|
|
12406
13109
|
name=args.name,
|
|
12407
13110
|
number=args.number,
|
|
12408
13111
|
description=args.description,
|
|
@@ -12823,7 +13526,7 @@ def _main_impl():
|
|
|
12823
13526
|
app_root=_resolve_app_root_arg(args))
|
|
12824
13527
|
pjson(result)
|
|
12825
13528
|
|
|
12826
|
-
elif args.cmd == "ksqlListDbRoutes":
|
|
13529
|
+
elif args.cmd == "ksqlListDbRoutes": # 本地补丁 #14
|
|
12827
13530
|
token = do_auth(cfg)
|
|
12828
13531
|
pjson(_api_call(cfg, token, "ksqlListDbRoutes", {}, method="POST"))
|
|
12829
13532
|
|
|
@@ -13339,6 +14042,9 @@ def main():
|
|
|
13339
14042
|
sys.argv[1:] = _normalize_argv(sys.argv[1:])
|
|
13340
14043
|
_CLI_MACHINE_MODE = "--stdout" in sys.argv[1:]
|
|
13341
14044
|
_CLI_RESULT_EMITTED = False
|
|
14045
|
+
if _CLI_MACHINE_MODE:
|
|
14046
|
+
_route_unstructured_prints_to_stderr()
|
|
14047
|
+
set_stdout_mode(True)
|
|
13342
14048
|
for index, argument in enumerate(sys.argv[1:]):
|
|
13343
14049
|
if argument == "--output" and index + 2 <= len(sys.argv[1:]):
|
|
13344
14050
|
set_output_file(sys.argv[1:][index + 1])
|