@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
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Resolve
|
|
2
|
+
"""Resolve platform form and verified standard list preview URLs."""
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
from urllib.parse import urljoin, urlparse
|
|
6
|
+
from urllib.parse import parse_qs, urlencode, urljoin, urlparse, urlunparse
|
|
7
7
|
|
|
8
8
|
import kd_api
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
PREVIEW_URL_PATH = "/kapi/v2/devportal/ai-meta/getPreviewUrl"
|
|
12
12
|
FORM_LOOKUP_PATH = "/kapi/v2/devportal/ai-meta/queryForms"
|
|
13
|
+
FORM_SCHEMA_PATH = "/kapi/v2/devportal/ai-meta/getFormSchema"
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class PreviewUrlError(RuntimeError):
|
|
@@ -51,8 +52,11 @@ def _response_preview_url(response: object, base_url: str) -> str:
|
|
|
51
52
|
return candidate
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
def get_preview_url(
|
|
55
|
-
|
|
55
|
+
def get_preview_url(
|
|
56
|
+
config: dict, base_url: str, form_number: str, view_type: str = "",
|
|
57
|
+
form_preview_url: str = "",
|
|
58
|
+
) -> str:
|
|
59
|
+
"""获取平台预览地址;仅在列表元数据已验证时恢复标准列表入口。
|
|
56
60
|
|
|
57
61
|
``view_type`` 选择预览面:留空保持平台默认的表单视图;
|
|
58
62
|
``"list"`` 请求列表预览 URL。
|
|
@@ -70,7 +74,32 @@ def get_preview_url(config: dict, base_url: str, form_number: str, view_type: st
|
|
|
70
74
|
config,
|
|
71
75
|
params,
|
|
72
76
|
)
|
|
73
|
-
|
|
77
|
+
candidate = _response_preview_url(response, base_url)
|
|
78
|
+
if view != "list":
|
|
79
|
+
return candidate
|
|
80
|
+
editor = form_preview_url or get_preview_url(config, base_url, number)
|
|
81
|
+
if candidate != editor:
|
|
82
|
+
return candidate
|
|
83
|
+
|
|
84
|
+
schema = kd_api.get(base_url, FORM_SCHEMA_PATH, config, {"formId": number})
|
|
85
|
+
data = schema.get("data") if isinstance(schema, dict) and schema.get("status") is True else None
|
|
86
|
+
if (
|
|
87
|
+
not isinstance(data, dict)
|
|
88
|
+
or data.get("formNumber") not in (None, "", number)
|
|
89
|
+
or not isinstance(data.get("listLayout"), dict)
|
|
90
|
+
or not isinstance(data.get("listConfig"), dict)
|
|
91
|
+
or data["listConfig"].get("entityId") != number
|
|
92
|
+
or not isinstance(data.get("listColumns"), list)
|
|
93
|
+
or not data["listColumns"]
|
|
94
|
+
):
|
|
95
|
+
raise PreviewUrlError("LIST_PREVIEW_METADATA_UNAVAILABLE: platform returned the editor URL for list")
|
|
96
|
+
parsed = urlparse(editor)
|
|
97
|
+
if parse_qs(parsed.query).get("formId") != [number]:
|
|
98
|
+
raise PreviewUrlError("LIST_PREVIEW_EDITOR_URL_UNEXPECTED")
|
|
99
|
+
# 标准 bos_list 路由来自平台先前返回的列表地址,且已在当前表单验证。
|
|
100
|
+
return urlunparse(parsed._replace(query=urlencode({
|
|
101
|
+
"formId": "bos_list", "type": "list", "billFormId": number,
|
|
102
|
+
}), fragment=""))
|
|
74
103
|
|
|
75
104
|
|
|
76
105
|
def get_form_name(config: dict, base_url: str, form_number: str) -> str:
|
|
@@ -92,6 +121,6 @@ def get_form_name(config: dict, base_url: str, form_number: str) -> str:
|
|
|
92
121
|
|
|
93
122
|
|
|
94
123
|
__all__ = [
|
|
95
|
-
"PREVIEW_URL_PATH", "FORM_LOOKUP_PATH", "PreviewUrlError",
|
|
124
|
+
"PREVIEW_URL_PATH", "FORM_LOOKUP_PATH", "FORM_SCHEMA_PATH", "PreviewUrlError",
|
|
96
125
|
"get_preview_url", "get_form_name",
|
|
97
126
|
]
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Read only the requested portion of a verified plugin handoff snapshot."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
MAX_OUTPUT_BYTES = 8 * 1024
|
|
14
|
+
MAX_CODING_CONTEXT_BYTES = 48 * 1024
|
|
15
|
+
MAX_CODING_START_BYTES = 16 * 1024
|
|
16
|
+
DEFAULT_PAGE_SIZE = 15
|
|
17
|
+
MAX_PAGE_SIZE = 50
|
|
18
|
+
IDENTITY_KEYS = (
|
|
19
|
+
"formNumber", "entityKey", "operationKey", "fieldKey", "class",
|
|
20
|
+
"reference", "entityName", "operationName", "fieldName",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class QueryError(ValueError):
|
|
25
|
+
def __init__(self, code: str, message: str):
|
|
26
|
+
super().__init__(message)
|
|
27
|
+
self.code = code
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def encoded(value: object) -> str:
|
|
31
|
+
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def pointer_child(parent: str, key: str | int) -> str:
|
|
35
|
+
return f"{parent}/{str(key).replace('~', '~0').replace('/', '~1')}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def resolve_pointer(document: object, pointer: str) -> object:
|
|
39
|
+
if not pointer:
|
|
40
|
+
return document
|
|
41
|
+
if not pointer.startswith("/"):
|
|
42
|
+
raise QueryError("HANDOFF_POINTER_INVALID", "path must be an exact JSON pointer beginning with /")
|
|
43
|
+
value = document
|
|
44
|
+
for token in pointer[1:].split("/"):
|
|
45
|
+
key = token.replace("~1", "/").replace("~0", "~")
|
|
46
|
+
if isinstance(value, dict) and key in value:
|
|
47
|
+
value = value[key]
|
|
48
|
+
continue
|
|
49
|
+
if isinstance(value, list) and key.isdecimal() and str(int(key)) == key and int(key) < len(value):
|
|
50
|
+
value = value[int(key)]
|
|
51
|
+
continue
|
|
52
|
+
raise QueryError("HANDOFF_POINTER_NOT_FOUND", f"path does not exist: {pointer}")
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def describe(value: object, pointer: str) -> dict:
|
|
57
|
+
kind = "object" if isinstance(value, dict) else "array" if isinstance(value, list) else type(value).__name__
|
|
58
|
+
result = {"path": pointer, "kind": kind, "bytes": len(encoded(value).encode("utf-8"))}
|
|
59
|
+
if isinstance(value, (dict, list)):
|
|
60
|
+
result["count"] = len(value)
|
|
61
|
+
if isinstance(value, dict):
|
|
62
|
+
result["identity"] = {
|
|
63
|
+
key: value[key][:120] for key in IDENTITY_KEYS
|
|
64
|
+
if isinstance(value.get(key), str)
|
|
65
|
+
}
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def load_handoff(filename: str) -> tuple[Path, dict]:
|
|
70
|
+
file = Path(filename)
|
|
71
|
+
if not file.is_absolute() or file.suffix.lower() != ".json":
|
|
72
|
+
raise QueryError("HANDOFF_PATH_INVALID", "handoff must be an absolute JSON path returned by preparation")
|
|
73
|
+
try:
|
|
74
|
+
document = json.loads(file.read_text(encoding="utf-8"))
|
|
75
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as error:
|
|
76
|
+
raise QueryError("HANDOFF_UNAVAILABLE", f"cannot read handoff: {type(error).__name__}") from error
|
|
77
|
+
if not isinstance(document, dict) or document.get("status") is not True or not isinstance(document.get("handoff"), dict):
|
|
78
|
+
raise QueryError("HANDOFF_INVALID", "file is not a successful plugin handoff")
|
|
79
|
+
if not isinstance(document.get("handoffPath"), str) or Path(document["handoffPath"]).resolve() != file.resolve():
|
|
80
|
+
raise QueryError("HANDOFF_PATH_MISMATCH", "handoffPath does not match the selected file")
|
|
81
|
+
return file, document
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def query(document: dict, args: argparse.Namespace) -> dict:
|
|
85
|
+
if args.operation in ("coding-context", "coding-start"):
|
|
86
|
+
handoff = document["handoff"]
|
|
87
|
+
graph = handoff.get("formGraph")
|
|
88
|
+
evidence = handoff.get("codingEvidence")
|
|
89
|
+
if not isinstance(graph, dict) or not isinstance(evidence, dict):
|
|
90
|
+
raise QueryError("HANDOFF_CODING_CONTEXT_INVALID", "formGraph and codingEvidence are required")
|
|
91
|
+
if not isinstance(handoff.get("relatedForms", []), list) or not isinstance(evidence.get("apiContracts", []), list):
|
|
92
|
+
raise QueryError("HANDOFF_CODING_CONTEXT_INVALID", "relatedForms and apiContracts must be arrays")
|
|
93
|
+
|
|
94
|
+
def pick(record: dict, keys: tuple[str, ...]) -> dict:
|
|
95
|
+
return {key: record[key] for key in keys if key in record and record[key] is not None}
|
|
96
|
+
|
|
97
|
+
def target_sdk() -> dict:
|
|
98
|
+
if not isinstance(handoff.get("targetSdk"), dict):
|
|
99
|
+
return {"status": "not-provided", "evidenceScope": "This snapshot does not establish target SDK readiness"}
|
|
100
|
+
return {**pick(handoff["targetSdk"], ("status", "projectRoot", "module", "evidenceScope")),
|
|
101
|
+
"path": "/handoff/targetSdk"}
|
|
102
|
+
|
|
103
|
+
if args.operation == "coding-start":
|
|
104
|
+
from compile_plugin_source import inspect_compile_environment
|
|
105
|
+
if not isinstance(graph.get("entities"), list):
|
|
106
|
+
raise QueryError("HANDOFF_CODING_CONTEXT_INVALID", "formGraph entities are required")
|
|
107
|
+
if any(not isinstance(entity, dict) or not isinstance(entity.get("fields"), list)
|
|
108
|
+
or any(not isinstance(field, dict) for field in entity["fields"]) for entity in graph["entities"]):
|
|
109
|
+
raise QueryError("HANDOFF_CODING_CONTEXT_INVALID", "entities and fields must be objects")
|
|
110
|
+
start = {
|
|
111
|
+
"status": True, "contextMode": "coding-start", "handoffPath": document["handoffPath"],
|
|
112
|
+
"handoffSha256": hashlib.sha256(Path(document["handoffPath"]).read_bytes()).hexdigest(),
|
|
113
|
+
**pick(handoff, ("appRoot", "projectRoot", "formNumber", "className", "sourcePath", "fqcn", "operationKeys", "coreLogic", "sourceGate")),
|
|
114
|
+
"entities": [{**pick(entity, ("entityKey", "role", "collectionProperty", "collectionPath")),
|
|
115
|
+
"fieldsPath": f"/handoff/formGraph/entities/{index}/fields",
|
|
116
|
+
"fields": [pick(field, ("fieldKey", "fieldName", "fieldType", "javaType", "valueAccessor", "idAccessor"))
|
|
117
|
+
for field in entity.get("fields", [])]}
|
|
118
|
+
for index, entity in enumerate(graph["entities"])],
|
|
119
|
+
"relatedForms": [{**pick(form, ("formNumber", "formName", "metadataVersion")),
|
|
120
|
+
"path": f"/handoff/relatedForms/{index}"}
|
|
121
|
+
for index, form in enumerate(handoff.get("relatedForms", []))],
|
|
122
|
+
"operationContracts": handoff.get("operationContracts", []),
|
|
123
|
+
"availableOperationKeys": [row["operationKey"] for row in handoff.get("availableOperations", [])
|
|
124
|
+
if isinstance(row, dict) and isinstance(row.get("operationKey"), str)],
|
|
125
|
+
"availableOperationsPath": "/handoff/availableOperations",
|
|
126
|
+
"selectedOperations": [pick(row, ("operationKey", "operationName", "operationType"))
|
|
127
|
+
for row in handoff.get("availableOperations", [])
|
|
128
|
+
if isinstance(row, dict) and row.get("operationKey") in handoff.get("operationKeys", [])],
|
|
129
|
+
"stateWarnings": handoff.get("stateWarnings", []),
|
|
130
|
+
"stateFields": [pick(field, ("fieldKey", "fieldName", "entityKey", "comboOptions")) for field in handoff.get("stateFields", [])],
|
|
131
|
+
"buildEnvironment": inspect_compile_environment(handoff["projectRoot"]) if handoff.get("projectRoot") else {"status": "not-provided"},
|
|
132
|
+
"targetSdk": target_sdk(),
|
|
133
|
+
"codingEvidence": {**pick(evidence, ("codingEntry", "operationDispatch", "apiLookup", "sdkLookup")),
|
|
134
|
+
"signatureTypes": [pick(item, ("class", "import")) for item in evidence.get("signatureTypes", [])],
|
|
135
|
+
"signatureTypesPath": "/handoff/codingEvidence/signatureTypes",
|
|
136
|
+
**({"guide": evidence["guide"]} if isinstance(evidence.get("guide"), str) and len(evidence["guide"]) <= 512 else {}),
|
|
137
|
+
"apiContracts": [{**pick(contract, ("class",)),
|
|
138
|
+
"path": f"/handoff/codingEvidence/apiContracts/{index}",
|
|
139
|
+
"members": list(dict.fromkeys(member["name"] for member in contract.get("members", []) if "name" in member))}
|
|
140
|
+
for index, contract in enumerate(evidence.get("apiContracts", []))]},
|
|
141
|
+
"remainingContext": {"stateFields": "/handoff/stateFields", "specialized": "/handoff/specialized", "reportContract": "/handoff/reportContract"},
|
|
142
|
+
"evidenceScope": "Startup facts and pointers only; collection membership is not proof of a standalone query root, and source compilation does not establish business coverage",
|
|
143
|
+
}
|
|
144
|
+
if len(encoded(start).encode("utf-8")) > MAX_CODING_START_BYTES:
|
|
145
|
+
for entity in start["entities"]:
|
|
146
|
+
entity["fieldCount"] = len(entity.pop("fields"))
|
|
147
|
+
entity["fieldsOmitted"] = True
|
|
148
|
+
return start
|
|
149
|
+
|
|
150
|
+
def compact_graph(value: dict) -> dict:
|
|
151
|
+
if not isinstance(value, dict) or not isinstance(value.get("entities"), list):
|
|
152
|
+
raise QueryError("HANDOFF_CODING_CONTEXT_INVALID", "formGraph entities are required")
|
|
153
|
+
if any(not isinstance(entity, dict) or not isinstance(entity.get("fields"), list) or any(not isinstance(field, dict) for field in entity["fields"]) for entity in value["entities"]):
|
|
154
|
+
raise QueryError("HANDOFF_CODING_CONTEXT_INVALID", "entities and fields must be objects")
|
|
155
|
+
return {
|
|
156
|
+
**pick(value, ("formNumber", "formName", "metadataVersion")),
|
|
157
|
+
"entities": [
|
|
158
|
+
{
|
|
159
|
+
**pick(entity, ("entityKey", "entityName", "entityType", "role", "parentEntityKey", "collectionProperty", "collectionPath")),
|
|
160
|
+
"fields": [pick(field, ("fieldKey", "fieldName", "fieldType", "fieldPath", "javaType", "valueAccessor", "timeValueUnit", "refTo", "comboOptions", "reference", "idAccessor", "filterValue")) for field in entity.get("fields", [])],
|
|
161
|
+
}
|
|
162
|
+
for entity in value["entities"]
|
|
163
|
+
],
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
"status": True,
|
|
168
|
+
"contextMode": "coding-context",
|
|
169
|
+
"handoffPath": document["handoffPath"],
|
|
170
|
+
"handoffSha256": hashlib.sha256(Path(document["handoffPath"]).read_bytes()).hexdigest(),
|
|
171
|
+
**pick(handoff, ("appRoot", "projectRoot", "formNumber", "className", "sourcePath", "fqcn", "operationKeys", "coreLogic", "specialized", "sourceGate")),
|
|
172
|
+
"formGraph": compact_graph(graph),
|
|
173
|
+
"targetSdk": target_sdk(),
|
|
174
|
+
"queryRootEvidence": {
|
|
175
|
+
"formNumber": graph.get("formNumber"),
|
|
176
|
+
"nonRootEntityKeys": [entity["entityKey"] for entity in graph["entities"]
|
|
177
|
+
if entity.get("role") in ("entries", "subentries") and isinstance(entity.get("entityKey"), str)],
|
|
178
|
+
"boundary": "EntryType and SubEntryType are collection members here, not evidence of standalone QueryServiceHelper roots; runtime query behavior is unverified by this snapshot.",
|
|
179
|
+
},
|
|
180
|
+
"relatedForms": [compact_graph(form) for form in handoff.get("relatedForms", [])],
|
|
181
|
+
"operationContracts": handoff.get("operationContracts", []),
|
|
182
|
+
"availableOperations": handoff.get("availableOperations", []),
|
|
183
|
+
"stateFields": handoff.get("stateFields", []),
|
|
184
|
+
"stateWarnings": handoff.get("stateWarnings", []),
|
|
185
|
+
"codingEvidence": {
|
|
186
|
+
**pick(evidence, ("codingEntry", "signatureTypes", "operationDispatch", "apiLookup", "sdkLookup")),
|
|
187
|
+
**({"guide": evidence["guide"]} if isinstance(evidence.get("guide"), str) and len(evidence["guide"]) <= 512 else {}),
|
|
188
|
+
"apiContracts": [
|
|
189
|
+
{
|
|
190
|
+
**pick(contract, ("class", "import", "reference")),
|
|
191
|
+
"members": [pick(member, ("name", "signature", "declaredBy", "deprecated")) for member in contract.get("members", [])],
|
|
192
|
+
}
|
|
193
|
+
for contract in evidence.get("apiContracts", [])
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
value = resolve_pointer(document, args.path)
|
|
198
|
+
if args.operation == "get":
|
|
199
|
+
return {"status": True, "path": args.path, "value": value}
|
|
200
|
+
if args.operation == "index":
|
|
201
|
+
if not isinstance(value, (dict, list)):
|
|
202
|
+
raise QueryError("HANDOFF_NOT_CONTAINER", "index requires an object or array; use get for a scalar")
|
|
203
|
+
children = list(value.items()) if isinstance(value, dict) else list(enumerate(value))
|
|
204
|
+
page = children[args.offset:args.offset + args.limit]
|
|
205
|
+
return {
|
|
206
|
+
"status": True, "path": args.path, "kind": "object" if isinstance(value, dict) else "array",
|
|
207
|
+
"total": len(children), "offset": args.offset,
|
|
208
|
+
"nextOffset": args.offset + len(page) if args.offset + len(page) < len(children) else None,
|
|
209
|
+
"items": [describe(child, pointer_child(args.path, key)) for key, child in page],
|
|
210
|
+
}
|
|
211
|
+
if not isinstance(value, list) or any(not isinstance(item, dict) for item in value):
|
|
212
|
+
raise QueryError("HANDOFF_NOT_RECORD_LIST", "find requires an array of objects")
|
|
213
|
+
matches = [describe(item, pointer_child(args.path, index)) for index, item in enumerate(value) if item.get(args.field) == args.equals]
|
|
214
|
+
return {"status": True, "path": args.path, "field": args.field, "equals": args.equals, "matches": matches}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def main() -> int:
|
|
218
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
219
|
+
parser.add_argument("operation", choices=("index", "find", "get", "coding-context", "coding-start"))
|
|
220
|
+
parser.add_argument("--handoff", required=True, help="Exact handoffPath returned by prepare_plugin_handoff.py")
|
|
221
|
+
parser.add_argument("--path", default="", help="JSON pointer, such as /handoff/formGraph/entities")
|
|
222
|
+
parser.add_argument("--offset", type=int, default=0, help="First child for index")
|
|
223
|
+
parser.add_argument("--limit", type=int, default=DEFAULT_PAGE_SIZE, help="Children per index page")
|
|
224
|
+
parser.add_argument("--field", help="Direct object field for exact find")
|
|
225
|
+
parser.add_argument("--equals", help="Exact value for find")
|
|
226
|
+
args = parser.parse_args()
|
|
227
|
+
try:
|
|
228
|
+
if args.offset < 0 or not 1 <= args.limit <= MAX_PAGE_SIZE:
|
|
229
|
+
raise QueryError("HANDOFF_PAGE_INVALID", "offset must be nonnegative and limit must be 1–50")
|
|
230
|
+
if args.operation == "find" and (not args.field or args.equals is None or not args.path):
|
|
231
|
+
raise QueryError("HANDOFF_FIND_INVALID", "find requires --path, --field and --equals")
|
|
232
|
+
if args.operation != "find" and (args.field is not None or args.equals is not None):
|
|
233
|
+
raise QueryError("HANDOFF_FIND_INVALID", "--field and --equals apply only to find")
|
|
234
|
+
if args.operation in ("coding-context", "coding-start") and (args.path or args.offset or args.limit != DEFAULT_PAGE_SIZE):
|
|
235
|
+
raise QueryError("HANDOFF_CODING_CONTEXT_ARGUMENT_INVALID", "coding-context accepts only --handoff")
|
|
236
|
+
_, document = load_handoff(args.handoff)
|
|
237
|
+
response = query(document, args)
|
|
238
|
+
output = encoded(response)
|
|
239
|
+
limit = MAX_CODING_CONTEXT_BYTES if args.operation == "coding-context" else MAX_CODING_START_BYTES if args.operation == "coding-start" else MAX_OUTPUT_BYTES
|
|
240
|
+
if len(output.encode("utf-8")) > limit:
|
|
241
|
+
raise QueryError("HANDOFF_QUERY_TOO_LARGE", f"selected result exceeds {limit} bytes; index its children or select a narrower path")
|
|
242
|
+
print(output)
|
|
243
|
+
return 0
|
|
244
|
+
except QueryError as error:
|
|
245
|
+
print(encoded({"status": False, "errorCode": error.code, "message": str(error)}))
|
|
246
|
+
return 2
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
if __name__ == "__main__":
|
|
250
|
+
sys.exit(main())
|
|
@@ -19,6 +19,7 @@ import urllib.error
|
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
21
|
import kd_api
|
|
22
|
+
from plugin_source_gate import validate_registration_entry
|
|
22
23
|
|
|
23
24
|
REGISTER_PATH = "/kapi/v2/devportal/ai-meta/plugin/register"
|
|
24
25
|
QUERY_PATH = "/kapi/v2/devportal/ai-meta/plugin/queryEditable"
|
|
@@ -200,7 +201,8 @@ def _load_classes_from_archive(app_root):
|
|
|
200
201
|
error_code='JAVA_ARCHIVE_ENTRY_INVALID',
|
|
201
202
|
details=invalid,
|
|
202
203
|
)
|
|
203
|
-
|
|
204
|
+
biz_app_id = str(archive.get('appId') or '').strip()
|
|
205
|
+
return [{**entry, 'bizAppId': biz_app_id} for entry in entries] if biz_app_id else entries
|
|
204
206
|
|
|
205
207
|
|
|
206
208
|
def _validate_candidate_sources(candidates):
|
|
@@ -262,7 +264,7 @@ def _filter_candidates(candidates, requested_names):
|
|
|
262
264
|
]
|
|
263
265
|
|
|
264
266
|
|
|
265
|
-
def _build_register_payload(form_number, class_name, description, target_type, operation_key=None):
|
|
267
|
+
def _build_register_payload(form_number, class_name, description, target_type, operation_key=None, biz_app_id=None):
|
|
266
268
|
"""构造注册请求 payload。"""
|
|
267
269
|
payload = {
|
|
268
270
|
'formNumber': form_number,
|
|
@@ -270,7 +272,10 @@ def _build_register_payload(form_number, class_name, description, target_type, o
|
|
|
270
272
|
'description': description,
|
|
271
273
|
'enabled': True,
|
|
272
274
|
'targetType': target_type,
|
|
275
|
+
'pluginType': 'JAVA',
|
|
273
276
|
}
|
|
277
|
+
if biz_app_id:
|
|
278
|
+
payload['bizAppId'] = biz_app_id
|
|
274
279
|
if operation_key:
|
|
275
280
|
payload['operationKey'] = operation_key
|
|
276
281
|
return payload
|
|
@@ -389,6 +394,16 @@ def main():
|
|
|
389
394
|
root_dir = _find_root_dir()
|
|
390
395
|
type_map = _load_type_registry(root_dir)
|
|
391
396
|
type_owners = _load_type_owners(root_dir)
|
|
397
|
+
source_checks = []
|
|
398
|
+
try:
|
|
399
|
+
for candidate in candidates:
|
|
400
|
+
if candidate.get('filePath') and type_map.get(candidate.get('type')):
|
|
401
|
+
checked = validate_registration_entry(candidate, type_map[candidate['type']], _resolve_fqcn(candidate))
|
|
402
|
+
source_checks.append({'className': _resolve_fqcn(candidate), 'compileStatus': checked['compileStatus']})
|
|
403
|
+
except ValueError as exc:
|
|
404
|
+
code, _, message = str(exc).partition(':')
|
|
405
|
+
print(json.dumps(_failure(code, message.strip(), writeAttempted=False), ensure_ascii=False))
|
|
406
|
+
return 2
|
|
392
407
|
server = _load_config(root_dir)
|
|
393
408
|
|
|
394
409
|
if not server:
|
|
@@ -475,6 +490,7 @@ def main():
|
|
|
475
490
|
description=c.get('description', ''),
|
|
476
491
|
target_type=target_type,
|
|
477
492
|
operation_key=operation_key,
|
|
493
|
+
biz_app_id=c.get('bizAppId'),
|
|
478
494
|
)
|
|
479
495
|
try:
|
|
480
496
|
response = kd_api.post(
|
|
@@ -536,8 +552,18 @@ def main():
|
|
|
536
552
|
'failed': sum(1 for r in results if not r['success']),
|
|
537
553
|
},
|
|
538
554
|
}
|
|
555
|
+
if source_checks:
|
|
556
|
+
output['sourceChecks'] = source_checks
|
|
539
557
|
if verification is not None:
|
|
540
558
|
output['verification'] = verification
|
|
559
|
+
if expected:
|
|
560
|
+
# Registration read-back does not establish deployment activation or restart needs.
|
|
561
|
+
output['activation'] = {
|
|
562
|
+
'requiresServiceRestart': None,
|
|
563
|
+
'runtimeVerified': False,
|
|
564
|
+
'state': 'registration_incomplete' if has_failure else 'runtime_unverified' if verification and verification['status'] == 'pass' else 'registration_unverified',
|
|
565
|
+
'registeredClasses': sorted({item['className'] for item in expected}),
|
|
566
|
+
}
|
|
541
567
|
print(json.dumps(output, ensure_ascii=False, indent=2))
|
|
542
568
|
return 2 if has_failure else 0
|
|
543
569
|
|
|
@@ -14,6 +14,7 @@ from __future__ import annotations
|
|
|
14
14
|
import argparse
|
|
15
15
|
import hashlib
|
|
16
16
|
import json
|
|
17
|
+
import os
|
|
17
18
|
import subprocess
|
|
18
19
|
import sys
|
|
19
20
|
from pathlib import Path
|
|
@@ -25,8 +26,10 @@ if str(SCRIPT_DIR) not in sys.path:
|
|
|
25
26
|
sys.path.insert(0, str(SCRIPT_DIR))
|
|
26
27
|
|
|
27
28
|
from preview_url import PreviewUrlError, get_form_name, get_preview_url
|
|
29
|
+
from test_environment import export_test_environment
|
|
28
30
|
|
|
29
31
|
_DYM_BACKGROUND_NOTICE = None
|
|
32
|
+
_TEST_ENVIRONMENT_HANDOFF = None
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
class ReportPluginSourceMissing(Exception):
|
|
@@ -343,6 +346,8 @@ def render(
|
|
|
343
346
|
) -> list[str]:
|
|
344
347
|
global _DYM_BACKGROUND_NOTICE
|
|
345
348
|
_DYM_BACKGROUND_NOTICE = None
|
|
349
|
+
global _TEST_ENVIRONMENT_HANDOFF
|
|
350
|
+
_TEST_ENVIRONMENT_HANDOFF = None
|
|
346
351
|
archive_path = Path(app_root) / "archive.json"
|
|
347
352
|
if not archive_path.exists():
|
|
348
353
|
print(f"Warning: {archive_path} does not exist, skipping kdform cards", file=sys.stderr)
|
|
@@ -394,6 +399,7 @@ def render(
|
|
|
394
399
|
|
|
395
400
|
blocks: list[str] = []
|
|
396
401
|
deleted_numbers: list[str] = []
|
|
402
|
+
browser_entries: dict[str, dict[str, str]] = {}
|
|
397
403
|
artifacts: list[dict[str, str]] = archive.get("artifacts", [])
|
|
398
404
|
if not isinstance(artifacts, list):
|
|
399
405
|
artifacts = []
|
|
@@ -411,6 +417,9 @@ def render(
|
|
|
411
417
|
if not base_url:
|
|
412
418
|
print("Warning: server.baseUrl is empty (check ~/.kd/config.json)", file=sys.stderr)
|
|
413
419
|
else:
|
|
420
|
+
_TEST_ENVIRONMENT_HANDOFF = export_test_environment(
|
|
421
|
+
app_root, server_config, [number for number, _title in form_entries]
|
|
422
|
+
)
|
|
414
423
|
archived_urls = _archived_preview_urls(artifacts)
|
|
415
424
|
# 子集渲染不能清掉其他表单已经归档的卡片。
|
|
416
425
|
requested_set = {fn for fn, _title in form_entries}
|
|
@@ -444,12 +453,19 @@ def render(
|
|
|
444
453
|
url = archived_urls.get(fn, "")
|
|
445
454
|
fallback = "reusing archived preview link" if url else "rendering card without preview link"
|
|
446
455
|
print(f"Warning: getPreviewUrl({fn}) failed ({e}); {fallback}", file=sys.stderr)
|
|
447
|
-
#
|
|
448
|
-
# listUrl 字段,绝不阻塞卡片本身。
|
|
456
|
+
# 平台忽略 viewType=list 时,由已验证列表元数据恢复标准 bos_list 入口。
|
|
449
457
|
try:
|
|
450
|
-
list_url = get_preview_url(
|
|
458
|
+
list_url = get_preview_url(
|
|
459
|
+
server_config, base_url, fn, view_type="list", form_preview_url=url,
|
|
460
|
+
)
|
|
451
461
|
except Exception:
|
|
452
462
|
list_url = ""
|
|
463
|
+
if list_url and list_url == url:
|
|
464
|
+
print(
|
|
465
|
+
f"Warning: getPreviewUrl({fn}, viewType=list) returned the editor URL; omitted listUrl",
|
|
466
|
+
file=sys.stderr,
|
|
467
|
+
)
|
|
468
|
+
list_url = ""
|
|
453
469
|
metadata = _join_url(base_url, f"/kapi/v2/devportal/ai-meta/getEntityFields?formNumber={fn}")
|
|
454
470
|
plugins = _join_url(base_url, f"/kapi/v2/devportal/ai-meta/getFormPlugins?formNumber={fn}")
|
|
455
471
|
designer = _join_url(base_url, f"/kapi/v2/devportal/ai-meta/getDesignerUrl?formNumber={fn}")
|
|
@@ -458,6 +474,7 @@ def render(
|
|
|
458
474
|
card = {"title": title, "url": url, "metadata": metadata, "plugins": plugins, "designer": designer}
|
|
459
475
|
if list_url:
|
|
460
476
|
card["listUrl"] = list_url
|
|
477
|
+
browser_entries[fn] = {"formName": title, "previewUrl": url, "listUrl": list_url}
|
|
461
478
|
if api:
|
|
462
479
|
card["api"] = api
|
|
463
480
|
|
|
@@ -465,6 +482,12 @@ def render(
|
|
|
465
482
|
card_json = json.dumps(card, ensure_ascii=False)
|
|
466
483
|
blocks.append(f":::render:kdform\n{card_json}\n:::")
|
|
467
484
|
|
|
485
|
+
_TEST_ENVIRONMENT_HANDOFF = export_test_environment(
|
|
486
|
+
app_root, server_config,
|
|
487
|
+
[number for number, _title in form_entries if number not in deleted_numbers],
|
|
488
|
+
form_links=browser_entries,
|
|
489
|
+
)
|
|
490
|
+
|
|
468
491
|
archive["artifacts"] = artifacts
|
|
469
492
|
_write_json(archive_path, archive)
|
|
470
493
|
|
|
@@ -487,6 +510,8 @@ def render(
|
|
|
487
510
|
|
|
488
511
|
|
|
489
512
|
def main() -> int:
|
|
513
|
+
global _TEST_ENVIRONMENT_HANDOFF
|
|
514
|
+
_TEST_ENVIRONMENT_HANDOFF = None
|
|
490
515
|
parser = argparse.ArgumentParser(description="Render output cards")
|
|
491
516
|
parser.add_argument("--app-root", required=True, help="App root directory")
|
|
492
517
|
parser.add_argument("--wait-dym", action="store_true",
|
|
@@ -511,6 +536,7 @@ def main() -> int:
|
|
|
511
536
|
help="Display name paired with --form-number",
|
|
512
537
|
)
|
|
513
538
|
args = parser.parse_args()
|
|
539
|
+
os.environ["PROJECT_ROOT"] = str(Path(args.app_root).resolve())
|
|
514
540
|
|
|
515
541
|
if args.form_name and not args.form_numbers:
|
|
516
542
|
parser.error("--form-name requires --form-number")
|
|
@@ -536,20 +562,27 @@ def main() -> int:
|
|
|
536
562
|
except DeletedArtifactNoCard as exc:
|
|
537
563
|
print(str(exc), file=sys.stderr)
|
|
538
564
|
return 0
|
|
565
|
+
except ValueError as exc:
|
|
566
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
567
|
+
return 1
|
|
539
568
|
if not blocks:
|
|
540
569
|
return 1
|
|
541
570
|
|
|
542
571
|
for block in blocks:
|
|
543
572
|
print(block, flush=True)
|
|
573
|
+
if _TEST_ENVIRONMENT_HANDOFF:
|
|
574
|
+
print("TEST_ENVIRONMENT_HANDOFF=" + json.dumps(_TEST_ENVIRONMENT_HANDOFF, ensure_ascii=False), flush=True)
|
|
544
575
|
if _DYM_BACKGROUND_NOTICE:
|
|
545
|
-
|
|
576
|
+
# 后台同步是必须交给宿主执行的动作,不是诊断日志。放在卡片块后的
|
|
577
|
+
# stdout,避免调用方隐藏 stderr 时把同步请求一起丢弃。
|
|
578
|
+
print(_DYM_BACKGROUND_NOTICE, flush=True)
|
|
546
579
|
|
|
547
580
|
print(
|
|
548
|
-
"DELIVERY:
|
|
549
|
-
"
|
|
550
|
-
"the blocks
|
|
551
|
-
"
|
|
552
|
-
"
|
|
581
|
+
"DELIVERY: Preserve the exact blocks for the final delivery response; do not rerender them. "
|
|
582
|
+
"These are domain artifacts, not evidence that the whole request or acceptance is complete. "
|
|
583
|
+
"For delegated implementation, return the blocks and actual results to the owner and continue "
|
|
584
|
+
"its remaining acceptance work. At whole-request finalization, call output_check once if "
|
|
585
|
+
"available, then include the blocks without Markdown links or code fences.",
|
|
553
586
|
file=sys.stderr,
|
|
554
587
|
flush=True,
|
|
555
588
|
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Run the production Algo examples against a supplied target SDK, without database queries."""
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
parser=argparse.ArgumentParser(description=__doc__)
|
|
13
|
+
parser.add_argument("--sdk-classpath", required=True)
|
|
14
|
+
parser.add_argument("--java-home", required=True)
|
|
15
|
+
parser.add_argument("--rows", type=int, default=10000)
|
|
16
|
+
parser.add_argument("--groups", type=int, default=100)
|
|
17
|
+
parser.add_argument("--iterations", type=int, default=5)
|
|
18
|
+
parser.add_argument("--output")
|
|
19
|
+
args=parser.parse_args()
|
|
20
|
+
if not 1 <= args.groups <= args.rows or args.iterations < 3:
|
|
21
|
+
parser.error("1 <= groups <= rows and iterations >= 3 are required")
|
|
22
|
+
root=Path(__file__).resolve().parents[1]
|
|
23
|
+
suffix=".exe" if os.name=="nt" else ""
|
|
24
|
+
java=Path(args.java_home)/"bin"/("java"+suffix)
|
|
25
|
+
javac=Path(args.java_home)/"bin"/("javac"+suffix)
|
|
26
|
+
with tempfile.TemporaryDirectory(prefix="report-algo-") as directory:
|
|
27
|
+
compiled=subprocess.run([str(javac),"-source","8","-target","8","-cp",args.sdk_classpath,"-d",directory,
|
|
28
|
+
str(root/"examples/java-rules/CosmicReportAggregation.java"),
|
|
29
|
+
str(root/"assets/report-algo-benchmark/ReportBenchmarkLogFactory.java"),
|
|
30
|
+
str(root/"assets/report-algo-benchmark/ReportAlgoBenchmark.java")],capture_output=True,text=True,timeout=60)
|
|
31
|
+
if compiled.returncode:
|
|
32
|
+
print(json.dumps({"status":"blocked","phase":"compile","diagnostics":compiled.stderr[-4000:]}));return 2
|
|
33
|
+
# Use the verified Java 8 runtime. Newer JVMs may need opens flags for SDK managed threads.
|
|
34
|
+
run=subprocess.run([str(java),"-DclusterName=report-benchmark","-Dlogfactory.className=ReportBenchmarkLogFactory",
|
|
35
|
+
"-cp",os.pathsep.join([directory,args.sdk_classpath]),"ReportAlgoBenchmark",str(args.rows),str(args.groups),str(args.iterations)],
|
|
36
|
+
cwd=directory,capture_output=True,text=True,timeout=75)
|
|
37
|
+
lines=[line for line in run.stdout.splitlines() if line.startswith('{"rows":')]
|
|
38
|
+
result={"status":"passed",**json.loads(lines[-1]),"scope":"local Algo computation only; no database, permission or browser latency benchmark"} if run.returncode==0 and lines else {
|
|
39
|
+
"status":"blocked","phase":"runtime","diagnostics":run.stderr[-4000:]}
|
|
40
|
+
if args.output: Path(args.output).write_text(json.dumps(result,ensure_ascii=False,indent=2)+"\n",encoding="utf-8")
|
|
41
|
+
print(json.dumps(result,ensure_ascii=False))
|
|
42
|
+
return 0 if result["status"]=="passed" else 2
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__=="__main__":
|
|
46
|
+
raise SystemExit(main())
|
|
@@ -1282,8 +1282,8 @@ def parse_entry(prop):
|
|
|
1282
1282
|
if not sub_name or sub_type == 'VarcharProp':
|
|
1283
1283
|
continue
|
|
1284
1284
|
|
|
1285
|
-
#
|
|
1286
|
-
#
|
|
1285
|
+
# Check for sub-entry(本地补丁 #1:MulBasedataProp / SubEntryProp 均为子单据体;
|
|
1286
|
+
# 漏 SubEntryProp 会导致子单据体字段静默丢失)
|
|
1287
1287
|
if sub_type in ('MulBasedataProp', 'SubEntryProp') and 'ItemType' in sub_prop:
|
|
1288
1288
|
sub_entry = parse_sub_entry(sub_prop)
|
|
1289
1289
|
entry['sub_entries'].append(sub_entry)
|