hap-cli 0.8.20__tar.gz → 0.8.21__tar.gz
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.
- {hap_cli-0.8.20 → hap_cli-0.8.21}/PKG-INFO +2 -1
- {hap_cli-0.8.20/hap_cli → hap_cli-0.8.21}/README.md +1 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21/hap_cli}/README.md +1 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/README_CN.md +1 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/__init__.py +1 -1
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/config_cmd.py +44 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/action_spec_adapter.py +9 -3
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/flow_node.py +41 -18
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/record.py +31 -2
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/session.py +9 -1
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/update.py +39 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/upload.py +31 -13
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/worksheet.py +38 -3
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/i18n.py +49 -0
- hap_cli-0.8.21/hap_cli/tests/test_content_language.py +120 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_core.py +22 -6
- hap_cli-0.8.21/hap_cli/tests/test_no_workflow_unsupported.py +92 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +20 -10
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_record_v3_semantic_cli.py +3 -1
- hap_cli-0.8.21/hap_cli/tests/test_update_mode.py +90 -0
- hap_cli-0.8.21/hap_cli/tests/test_upload_host_resolution.py +93 -0
- hap_cli-0.8.21/hap_cli/tests/test_workflow_read_write_keys.py +71 -0
- hap_cli-0.8.21/hap_cli/tests/test_worksheet_create_section.py +91 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +4 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli.egg-info/PKG-INFO +2 -1
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli.egg-info/SOURCES.txt +6 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/MANIFEST.in +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/app_live_tests/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/app_live_tests/config.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/app_live_tests/harness.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/app_live_tests/smoke.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/app_live_tests/state.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/__main__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/app_creator_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/app_editor_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/auth_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/chart_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/group_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/instance_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/post_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/role_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/update_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/commands/worksheet_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/context.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/charts.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/cleanup.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/compiler.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/config.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/design/design.schema.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/errors.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/executor.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/fields.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/hap.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/merge.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/recording/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/recording/console.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/recording/mirror.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/recording/report.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/schema.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/executor.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/seed/template.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/selftest.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/smoke.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/steps.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/store.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/validate.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/auth.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/calendar_mod.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/chart.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/chat.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/chatbot.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/cli_access.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/contact.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/department.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/apply.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/componentlower.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/editspec.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/errors.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/fieldlower.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/jsonschema_mini.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/models.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/ops.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/planner.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/editor/reader.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/group.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/instance.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/optionset.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/page.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/post.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/role.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/workflow.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/workflow_node_dsl.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/hap_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/locale/messages.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/conftest.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_app_creator.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_app_editor.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_app_export_usage.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_bug_036430.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_bug_036750.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_cli_access.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_control_lookup_guard.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_full_e2e.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_global_meta.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_instance_history_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_approval.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_approval_actions.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_destructive.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_multi_profile.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_record_field_alias.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_semantic_types_registry.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_update.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_workflow_list_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/__main__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/_wftest.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/charts.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/cleanup.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/compiler.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/config.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/errors.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/executor.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/fields.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/hap.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/recording/console.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/recording/report.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/schema.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/seed/template.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/selftest.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/smoke.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/step.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/steps.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/store.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/setup.cfg +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/setup.py +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/skills/cli/hap-cli-app-editor/scripts/editspec/component.schema.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/skills/cli/hap-cli-app-editor/scripts/editspec/custom-action.schema.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/skills/cli/hap-cli-app-editor/scripts/editspec/envelope.schema.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/skills/cli/hap-cli-app-editor/scripts/editspec/field.schema.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.8.20 → hap_cli-0.8.21}/sources/v3-api-schema/update_optionset.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hap-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.21
|
|
4
4
|
Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
|
|
5
5
|
Author: hap-cli
|
|
6
6
|
License: Apache-2.0
|
|
@@ -313,6 +313,7 @@ discover every subcommand.
|
|
|
313
313
|
| `config show` | Display the current configuration summary |
|
|
314
314
|
| `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
|
|
315
315
|
| `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
|
|
316
|
+
| `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
|
|
316
317
|
| `config log on / off / status` | Toggle and inspect API logging |
|
|
317
318
|
| `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
|
|
318
319
|
| `config log view [--lines N] [--no-follow]` | Tail the log file |
|
|
@@ -284,6 +284,7 @@ discover every subcommand.
|
|
|
284
284
|
| `config show` | Display the current configuration summary |
|
|
285
285
|
| `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
|
|
286
286
|
| `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
|
|
287
|
+
| `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
|
|
287
288
|
| `config log on / off / status` | Toggle and inspect API logging |
|
|
288
289
|
| `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
|
|
289
290
|
| `config log view [--lines N] [--no-follow]` | Tail the log file |
|
|
@@ -284,6 +284,7 @@ discover every subcommand.
|
|
|
284
284
|
| `config show` | Display the current configuration summary |
|
|
285
285
|
| `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
|
|
286
286
|
| `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
|
|
287
|
+
| `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
|
|
287
288
|
| `config log on / off / status` | Toggle and inspect API logging |
|
|
288
289
|
| `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
|
|
289
290
|
| `config log view [--lines N] [--no-follow]` | Tail the log file |
|
|
@@ -258,6 +258,7 @@ hap --json worksheet record list WORKSHEET_ID
|
|
|
258
258
|
| `config show` | 显示 CLI 配置摘要 |
|
|
259
259
|
| `config completion [bash\|zsh\|fish] [--install]` | 启用 `<Tab>` 补全 |
|
|
260
260
|
| `config language [LANG]` | 切换显示语言(`en` / `zh-Hans` / `zh-Hant` / `ja`) |
|
|
261
|
+
| `config update [auto\|notify\|off]` | 自动升级开关;`notify` 只提示不安装,`off` 不检查 |
|
|
261
262
|
| `config log on / off / status` | 日志开关与状态 |
|
|
262
263
|
| `config log level LEVEL` | 设置日志级别(`DEBUG/INFO/WARNING/ERROR/CRITICAL`) |
|
|
263
264
|
| `config log view [--lines N] [--no-follow]` | 跟踪日志文件 |
|
|
@@ -122,6 +122,8 @@ def config_completion(shell, install):
|
|
|
122
122
|
@pass_context
|
|
123
123
|
def config_show(ctx):
|
|
124
124
|
"""Display current harness configuration."""
|
|
125
|
+
from hap_cli.core.update import get_update_mode
|
|
126
|
+
|
|
125
127
|
log_cfg = load_logging_config()
|
|
126
128
|
data = {
|
|
127
129
|
"logging": {
|
|
@@ -129,6 +131,7 @@ def config_show(ctx):
|
|
|
129
131
|
"level": log_cfg["level"],
|
|
130
132
|
"file": str(resolve_log_path(log_cfg)),
|
|
131
133
|
},
|
|
134
|
+
"update": get_update_mode(),
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
def _human(d):
|
|
@@ -136,6 +139,7 @@ def config_show(ctx):
|
|
|
136
139
|
click.echo(f" enabled : {d['logging']['enabled']}")
|
|
137
140
|
click.echo(f" level : {d['logging']['level']}")
|
|
138
141
|
click.echo(f" file : {d['logging']['file']}")
|
|
142
|
+
click.echo(f"update : {d['update']}")
|
|
139
143
|
|
|
140
144
|
ctx.output(data, _human)
|
|
141
145
|
|
|
@@ -205,6 +209,46 @@ def log_status(ctx):
|
|
|
205
209
|
_echo_log_status(ctx, cfg)
|
|
206
210
|
|
|
207
211
|
|
|
212
|
+
# ── `hap config update` ───────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@config.command("update")
|
|
216
|
+
@click.argument(
|
|
217
|
+
"mode", required=False, type=click.Choice(["auto", "notify", "off"]),
|
|
218
|
+
)
|
|
219
|
+
@pass_context
|
|
220
|
+
def config_update(ctx, mode):
|
|
221
|
+
"""Control whether hap-cli upgrades itself.
|
|
222
|
+
|
|
223
|
+
\b
|
|
224
|
+
auto check hourly and install new versions (default)
|
|
225
|
+
notify check hourly and tell you, but never install
|
|
226
|
+
off never check
|
|
227
|
+
|
|
228
|
+
Use ``notify`` or ``off`` to pin the version in scripted or
|
|
229
|
+
production environments. Without an argument, shows the current
|
|
230
|
+
setting. ``hap update`` still upgrades on demand in every mode.
|
|
231
|
+
"""
|
|
232
|
+
from hap_cli.core.session import load_config, save_config
|
|
233
|
+
from hap_cli.core.update import get_update_mode
|
|
234
|
+
|
|
235
|
+
if mode is None:
|
|
236
|
+
current = get_update_mode()
|
|
237
|
+
if ctx.json_mode:
|
|
238
|
+
from hap_cli.utils.formatting import output_json
|
|
239
|
+
output_json({"update": current})
|
|
240
|
+
else:
|
|
241
|
+
click.echo(f"Self-update: {current}")
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
save_config({"update": mode})
|
|
245
|
+
if ctx.json_mode:
|
|
246
|
+
from hap_cli.utils.formatting import output_json
|
|
247
|
+
output_json({"status": "ok", "update": mode})
|
|
248
|
+
else:
|
|
249
|
+
click.echo(f"Self-update set to: {mode}")
|
|
250
|
+
|
|
251
|
+
|
|
208
252
|
# ── `hap config language` ─────────────────────────────────────────────────
|
|
209
253
|
|
|
210
254
|
|
|
@@ -110,9 +110,15 @@ def build_button_payload(spec: dict[str, Any]) -> dict[str, Any]:
|
|
|
110
110
|
# in the UI; for the dedicated 二次确认 button use clickType 2.
|
|
111
111
|
if out["clickType"] == 1 and confirm:
|
|
112
112
|
out["clickType"] = 2
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
# Dialog text is stored on the button itself, so it must be
|
|
114
|
+
# written in the application's language, not ours. Follows the
|
|
115
|
+
# active CLI language; an explicit value always wins.
|
|
116
|
+
from hap_cli.i18n import content_default
|
|
117
|
+
|
|
118
|
+
out["confirmMsg"] = confirm_msg or content_default("button.confirmMsg")
|
|
119
|
+
out["sureName"] = spec.get("sureName") or content_default("button.sure")
|
|
120
|
+
out["cancelName"] = (
|
|
121
|
+
spec.get("cancelName") or content_default("button.cancel"))
|
|
116
122
|
|
|
117
123
|
# enableWhen → filters (reuse the shared filter translator).
|
|
118
124
|
enable_when = spec.get("enableWhen")
|
|
@@ -394,27 +394,50 @@ def save_node(
|
|
|
394
394
|
if select_node_id:
|
|
395
395
|
data["selectNodeId"] = select_node_id
|
|
396
396
|
data.update(config)
|
|
397
|
-
|
|
398
|
-
# their filter: getNodeDetail returns it as ``conditions`` (see
|
|
399
|
-
# pd-openweb Branch/index.jsx, which reads ``result.conditions``), but
|
|
400
|
-
# saveNode only persists it under ``operateCondition`` (the same file
|
|
401
|
-
# sends ``saveNode({ operateCondition: data })``). The value shape is
|
|
402
|
-
# identical. Saving with the read-side ``conditions`` key is silently
|
|
403
|
-
# ignored by the server — the save still reports success but the
|
|
404
|
-
# condition is dropped. Accept the read key so copying a node's
|
|
405
|
-
# read structure straight back round-trips correctly.
|
|
406
|
-
if flow_node_type == 2 and "conditions" in data:
|
|
407
|
-
if "operateCondition" not in data:
|
|
408
|
-
data["operateCondition"] = data["conditions"]
|
|
409
|
-
logger.info(
|
|
410
|
-
"save_node: branch item %s — mapped read-side key "
|
|
411
|
-
"'conditions' to write-side 'operateCondition'", node_id)
|
|
412
|
-
# ``conditions`` is not a saveNode key; drop it so it doesn't ride
|
|
413
|
-
# along as dead weight.
|
|
414
|
-
data.pop("conditions", None)
|
|
397
|
+
_map_read_side_keys(data, node_id)
|
|
415
398
|
return session.workflow_call("flowNode/saveNode", data)
|
|
416
399
|
|
|
417
400
|
|
|
401
|
+
# Node config keys whose read form differs from their write form. Reading
|
|
402
|
+
# a node, editing one value and writing it back is the natural way to work
|
|
403
|
+
# with these APIs, but the server ignores the read-side key on save — the
|
|
404
|
+
# call still reports success while the value is dropped.
|
|
405
|
+
#
|
|
406
|
+
# The filter is asymmetric on every filtering node in pd-openweb, not just
|
|
407
|
+
# on branches: the component holds it as ``conditions`` and posts it as
|
|
408
|
+
# ``operateCondition``, with an identical value shape.
|
|
409
|
+
# Branch/index.jsx:184 saveNode({ operateCondition: conditions })
|
|
410
|
+
# GetMoreRecord/index.jsx:184 saveNode({ operateCondition: conditions })
|
|
411
|
+
# Search/index.jsx:190 saveNode({ operateCondition: conditions })
|
|
412
|
+
# FindSystem/index.jsx:110 saveNode({ operateCondition: conditions })
|
|
413
|
+
# ``conditions`` is never itself a saveNode key, so translating it is safe
|
|
414
|
+
# for any node type.
|
|
415
|
+
_READ_TO_WRITE_KEYS = {
|
|
416
|
+
"conditions": "operateCondition",
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _map_read_side_keys(data: dict[str, Any], node_id: str) -> None:
|
|
421
|
+
"""Rewrite read-side config keys to the keys saveNode persists.
|
|
422
|
+
|
|
423
|
+
An explicit write-side key always wins; the read-side key is then
|
|
424
|
+
dropped either way so it cannot ride along as dead weight.
|
|
425
|
+
"""
|
|
426
|
+
for read_key, write_key in _READ_TO_WRITE_KEYS.items():
|
|
427
|
+
if read_key not in data:
|
|
428
|
+
continue
|
|
429
|
+
if write_key in data:
|
|
430
|
+
logger.info(
|
|
431
|
+
"save_node: node %s carries both '%s' and '%s'; keeping "
|
|
432
|
+
"the write-side value", node_id, read_key, write_key)
|
|
433
|
+
else:
|
|
434
|
+
data[write_key] = data[read_key]
|
|
435
|
+
logger.info(
|
|
436
|
+
"save_node: node %s — mapped read-side key '%s' to "
|
|
437
|
+
"write-side '%s'", node_id, read_key, write_key)
|
|
438
|
+
data.pop(read_key, None)
|
|
439
|
+
|
|
440
|
+
|
|
418
441
|
# ── Testing ──────────────────────────────────────────────────────────────
|
|
419
442
|
|
|
420
443
|
|
|
@@ -831,6 +831,35 @@ def get_record(
|
|
|
831
831
|
return session.api_call("Worksheet", "GetRowDetail", data)
|
|
832
832
|
|
|
833
833
|
|
|
834
|
+
# The main-site record endpoints have no workflow-suppression parameter.
|
|
835
|
+
# pd-openweb documents UpdateWorksheetRow as worksheetId / rowId /
|
|
836
|
+
# newOldControl / viewId / instanceId / workId / btnId / btnRemark /
|
|
837
|
+
# btnWorksheetId / btnRowId / pushUniqueId / rowStatus / updateType
|
|
838
|
+
# (src/api/worksheet.js:535-548), and AddWorksheetRow likewise has none.
|
|
839
|
+
# ``triggerWorkflow`` belongs to the open API v2 (worksheet/addRow,
|
|
840
|
+
# worksheet/editRow) — a different transport. We used to send it anyway;
|
|
841
|
+
# the main site accepted the body and ignored the flag, so the workflows
|
|
842
|
+
# ran while the caller believed they had been suppressed (confirmed live:
|
|
843
|
+
# a record-update flow stamped its field on an update sent with
|
|
844
|
+
# triggerWorkflow: false).
|
|
845
|
+
#
|
|
846
|
+
# Record writes cannot simply move to the open API: it silently drops
|
|
847
|
+
# RELATE_SHEET / SUB_LIST cells. Until there is a real way to suppress,
|
|
848
|
+
# refusing is the only honest answer — anyone reaching for this is trying
|
|
849
|
+
# to edit data *safely*, and quietly doing the unsafe thing is worse than
|
|
850
|
+
# failing.
|
|
851
|
+
def _reject_workflow_suppression(trigger_workflow: bool) -> None:
|
|
852
|
+
if trigger_workflow:
|
|
853
|
+
return
|
|
854
|
+
raise ValueError(
|
|
855
|
+
"Workflows cannot be suppressed for this write — they will run "
|
|
856
|
+
"whatever this command does, so the option is refused rather than "
|
|
857
|
+
"ignored. To edit records without them, turn the workflow off "
|
|
858
|
+
"first (hap workflow publish <id> --disable), make the change, "
|
|
859
|
+
"then turn it back on."
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
|
|
834
863
|
def create_record(
|
|
835
864
|
session: Session,
|
|
836
865
|
worksheet_id: str,
|
|
@@ -848,13 +877,13 @@ def create_record(
|
|
|
848
877
|
Returns:
|
|
849
878
|
Created record info
|
|
850
879
|
"""
|
|
880
|
+
_reject_workflow_suppression(trigger_workflow)
|
|
851
881
|
return session.api_call(
|
|
852
882
|
"Worksheet",
|
|
853
883
|
"AddWorksheetRow",
|
|
854
884
|
{
|
|
855
885
|
"worksheetId": worksheet_id,
|
|
856
886
|
"receiveControls": controls,
|
|
857
|
-
"triggerWorkflow": trigger_workflow,
|
|
858
887
|
},
|
|
859
888
|
)
|
|
860
889
|
|
|
@@ -878,6 +907,7 @@ def update_record(
|
|
|
878
907
|
Returns:
|
|
879
908
|
Update result
|
|
880
909
|
"""
|
|
910
|
+
_reject_workflow_suppression(trigger_workflow)
|
|
881
911
|
return session.api_call(
|
|
882
912
|
"Worksheet",
|
|
883
913
|
"UpdateWorksheetRow",
|
|
@@ -885,7 +915,6 @@ def update_record(
|
|
|
885
915
|
"worksheetId": worksheet_id,
|
|
886
916
|
"rowId": row_id,
|
|
887
917
|
"newOldControl": controls,
|
|
888
|
-
"triggerWorkflow": trigger_workflow,
|
|
889
918
|
},
|
|
890
919
|
)
|
|
891
920
|
|
|
@@ -360,11 +360,19 @@ class Session:
|
|
|
360
360
|
from hap_cli.core import cli_access
|
|
361
361
|
|
|
362
362
|
cli_access.ensure_before_request(self, url)
|
|
363
|
+
from hap_cli.i18n import get_active_language
|
|
364
|
+
|
|
363
365
|
headers = {
|
|
364
366
|
"Content-Type": "application/json",
|
|
365
367
|
"Authorization": f"md_pss_id {self.auth_token}",
|
|
366
368
|
"X-Requested-With": "XMLHttpRequest",
|
|
367
369
|
"User-Agent": hap_user_agent(),
|
|
370
|
+
# Give the server the language context it otherwise has to
|
|
371
|
+
# guess at. Defaults the server fills in (hints, template
|
|
372
|
+
# text) were coming back in Simplified Chinese for
|
|
373
|
+
# all-English applications because API requests carried no
|
|
374
|
+
# language at all — the browser always sends one.
|
|
375
|
+
"Accept-Language": get_active_language(),
|
|
368
376
|
}
|
|
369
377
|
# pd-openweb sends the caller's own accountId on every request
|
|
370
378
|
# (``src/common/global.js`` ``disposeRequestParams``). Some
|
|
@@ -1005,7 +1013,7 @@ PROFILE_FIELDS = (
|
|
|
1005
1013
|
# are properties of the human at the terminal / the install, not of any
|
|
1006
1014
|
# one account, so logger and the language commands keep reading/writing
|
|
1007
1015
|
# them via the plain load_config()/save_config() top-level accessors.
|
|
1008
|
-
GLOBAL_FIELDS = ("logging", "language", "language_source")
|
|
1016
|
+
GLOBAL_FIELDS = ("logging", "language", "language_source", "update")
|
|
1009
1017
|
|
|
1010
1018
|
|
|
1011
1019
|
def _empty_v2_doc() -> dict:
|
|
@@ -25,6 +25,8 @@ from typing import Any, Optional
|
|
|
25
25
|
|
|
26
26
|
import requests
|
|
27
27
|
|
|
28
|
+
from hap_cli.core.session import load_config
|
|
29
|
+
|
|
28
30
|
logger = logging.getLogger("hap_cli.update")
|
|
29
31
|
|
|
30
32
|
PYPI_PACKAGE = "hap-cli"
|
|
@@ -172,6 +174,27 @@ def save_state(state: dict) -> None:
|
|
|
172
174
|
# ── Background check orchestration ───────────────────────────────────────
|
|
173
175
|
|
|
174
176
|
|
|
177
|
+
UPDATE_MODES = ("auto", "notify", "off")
|
|
178
|
+
DEFAULT_UPDATE_MODE = "auto"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_update_mode() -> str:
|
|
182
|
+
"""Resolve the self-update mode from config.
|
|
183
|
+
|
|
184
|
+
``auto`` checks and upgrades, ``notify`` checks and reports without
|
|
185
|
+
upgrading, ``off`` does not check at all. Anything unrecognised — or
|
|
186
|
+
no setting — is ``auto``, the behaviour that shipped before this was
|
|
187
|
+
configurable.
|
|
188
|
+
"""
|
|
189
|
+
try:
|
|
190
|
+
cfg = load_config() or {}
|
|
191
|
+
except Exception as e:
|
|
192
|
+
logger.warning("could not read the update mode from config: %s", e)
|
|
193
|
+
return DEFAULT_UPDATE_MODE
|
|
194
|
+
mode = cfg.get("update")
|
|
195
|
+
return mode if mode in UPDATE_MODES else DEFAULT_UPDATE_MODE
|
|
196
|
+
|
|
197
|
+
|
|
175
198
|
def maybe_spawn_background_check() -> None:
|
|
176
199
|
"""Spawn a detached background update check if it is due.
|
|
177
200
|
|
|
@@ -179,6 +202,8 @@ def maybe_spawn_background_check() -> None:
|
|
|
179
202
|
*before* spawning so rapid consecutive commands cannot stack
|
|
180
203
|
duplicate children — at worst a failed child costs one interval.
|
|
181
204
|
"""
|
|
205
|
+
if get_update_mode() == "off":
|
|
206
|
+
return
|
|
182
207
|
kind = get_install_kind()
|
|
183
208
|
if kind in ("editable", "unknown"):
|
|
184
209
|
return
|
|
@@ -246,6 +271,15 @@ def run_background_update() -> None:
|
|
|
246
271
|
save_state(state)
|
|
247
272
|
return
|
|
248
273
|
|
|
274
|
+
if get_update_mode() == "notify":
|
|
275
|
+
# Report it and stop — the point of this mode is that the
|
|
276
|
+
# installed version never changes underneath a running script.
|
|
277
|
+
state["last_result"] = "update_available"
|
|
278
|
+
state["notified"] = False
|
|
279
|
+
save_state(state)
|
|
280
|
+
logger.info("update to %s available (notify-only mode)", latest)
|
|
281
|
+
return
|
|
282
|
+
|
|
249
283
|
ok, output = perform_upgrade(get_install_kind())
|
|
250
284
|
if ok:
|
|
251
285
|
state["last_result"] = "upgraded"
|
|
@@ -280,6 +314,11 @@ def consume_startup_notice() -> Optional[str]:
|
|
|
280
314
|
f"Automatic upgrade to {latest} failed — "
|
|
281
315
|
"run `hap update` to upgrade manually."
|
|
282
316
|
)
|
|
317
|
+
elif result == "update_available":
|
|
318
|
+
latest = state.get("latest_version") or "a newer version"
|
|
319
|
+
msg = (
|
|
320
|
+
f"hap-cli {latest} is available — run `hap update` to install it."
|
|
321
|
+
)
|
|
283
322
|
else:
|
|
284
323
|
return None
|
|
285
324
|
|
|
@@ -30,12 +30,11 @@ from urllib.parse import urlparse
|
|
|
30
30
|
|
|
31
31
|
import requests
|
|
32
32
|
|
|
33
|
-
from hap_cli.core.session import Session
|
|
33
|
+
from hap_cli.core.session import Session, SessionError
|
|
34
34
|
logger = logging.getLogger("hap_cli.upload")
|
|
35
35
|
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
_PUBLIC_UPLOAD_HOST = "https://upload.qiniup.com/"
|
|
36
|
+
# The upload endpoint is always read from Global/GetGlobalMeta — there is
|
|
37
|
+
# deliberately no constant to fall back to, see upload_host().
|
|
39
38
|
|
|
40
39
|
# bucket ids (pd-openweb kc/utils): 4=picture(mdpic), 3=document(mdoc).
|
|
41
40
|
_PICTURE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg",
|
|
@@ -67,17 +66,36 @@ def _bucket_for_ext(ext: str) -> int:
|
|
|
67
66
|
|
|
68
67
|
|
|
69
68
|
def upload_host(session: Session) -> str:
|
|
70
|
-
"""Resolve
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
"""Resolve this environment's file-store upload endpoint.
|
|
70
|
+
|
|
71
|
+
Global/GetGlobalMeta answers ``{config: {...}, "md.global": {...}}``
|
|
72
|
+
and publishes the file store under ``md.global.FileStoreConfig`` —
|
|
73
|
+
the same object every frontend upload posts to
|
|
74
|
+
(``md.global.FileStoreConfig.uploadHost``, pd-openweb
|
|
75
|
+
src/library/plupload/createUploader.js:43 and 12 sibling call sites,
|
|
76
|
+
none of which has a fallback). We used to read ``FileStoreConfig``
|
|
77
|
+
at the top level, so it was never found on any environment and a
|
|
78
|
+
hardcoded public-qiniu constant was used instead. That constant
|
|
79
|
+
equals the public SaaS's own uploadHost, which hid the bug there and
|
|
80
|
+
broke every environment with its own file store: the bytes went to
|
|
81
|
+
public qiniu while the upload token came from the environment's
|
|
82
|
+
store, so the store answered 401 with nothing to point at.
|
|
83
|
+
|
|
84
|
+
A missing host is an error rather than a guess — guessing is what
|
|
85
|
+
turned a routing bug into an unexplainable 401.
|
|
86
|
+
"""
|
|
87
|
+
meta = session.get_global_meta()
|
|
88
|
+
for scope in (meta.get("md.global"), meta):
|
|
89
|
+
if not isinstance(scope, dict):
|
|
90
|
+
continue
|
|
91
|
+
host = (scope.get("FileStoreConfig") or {}).get("uploadHost")
|
|
76
92
|
if host:
|
|
77
93
|
return host
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
94
|
+
raise SessionError(
|
|
95
|
+
"This server did not publish a file upload address, so the file "
|
|
96
|
+
"cannot be uploaded. Check that you are signed in to the right "
|
|
97
|
+
"server, then try again."
|
|
98
|
+
)
|
|
81
99
|
|
|
82
100
|
|
|
83
101
|
def get_upload_token(
|
|
@@ -217,6 +217,37 @@ FIELD_TYPES = {
|
|
|
217
217
|
# ── Worksheet CRUD ───────────────────────────────────────────────────────
|
|
218
218
|
|
|
219
219
|
|
|
220
|
+
def _first_app_section_id(session: Session, app_id: str) -> str:
|
|
221
|
+
"""Return the application's first sidebar section id.
|
|
222
|
+
|
|
223
|
+
AddWorkSheet files the new worksheet under a section, and some
|
|
224
|
+
deployments reject the call with a bare "Invalid Parameters" when
|
|
225
|
+
``appSectionId`` is absent instead of defaulting to the first one.
|
|
226
|
+
Every application has at least one section and the UI always creates
|
|
227
|
+
into a specific one, so resolving it here spares the caller a lookup
|
|
228
|
+
they have no reason to perform.
|
|
229
|
+
"""
|
|
230
|
+
from hap_cli.core import app as _app
|
|
231
|
+
try:
|
|
232
|
+
detail = _app.get_app_detail(session, app_id)
|
|
233
|
+
except Exception as exc:
|
|
234
|
+
logger.warning("section lookup for app %s failed: %s", app_id, exc)
|
|
235
|
+
raise ValueError(
|
|
236
|
+
f"Could not read the sections of application {app_id}, so there "
|
|
237
|
+
"is nowhere to put the new worksheet. Check the application id, "
|
|
238
|
+
"or name a section with --section-id."
|
|
239
|
+
) from exc
|
|
240
|
+
sections = (detail or {}).get("sections") or []
|
|
241
|
+
for sec in sections:
|
|
242
|
+
sid = sec.get("appSectionId")
|
|
243
|
+
if sid:
|
|
244
|
+
return sid
|
|
245
|
+
raise ValueError(
|
|
246
|
+
f"Application {app_id} has no sections to create the worksheet in. "
|
|
247
|
+
"Add a section first, or name one with --section-id."
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
220
251
|
def create_worksheet(
|
|
221
252
|
session: Session,
|
|
222
253
|
app_id: str,
|
|
@@ -266,8 +297,9 @@ def create_worksheet(
|
|
|
266
297
|
}
|
|
267
298
|
if project_id:
|
|
268
299
|
data["projectId"] = project_id
|
|
269
|
-
if section_id:
|
|
270
|
-
|
|
300
|
+
if not section_id:
|
|
301
|
+
section_id = _first_app_section_id(session, app_id)
|
|
302
|
+
data["appSectionId"] = section_id # API requires appSectionId
|
|
271
303
|
data["iconUrl"] = icon_url or (
|
|
272
304
|
f"https://fp1.mingdaoyun.cn/customIcon/{icon}.svg"
|
|
273
305
|
)
|
|
@@ -878,7 +910,10 @@ def bind_sub_list_to_back_relate(
|
|
|
878
910
|
except Exception:
|
|
879
911
|
synth_name = None
|
|
880
912
|
if not synth_name:
|
|
881
|
-
|
|
913
|
+
# Becomes a visible column header in the customer's app, so it
|
|
914
|
+
# follows the active language rather than being hardcoded.
|
|
915
|
+
from hap_cli.i18n import content_default
|
|
916
|
+
synth_name = content_default("control.backRelation")
|
|
882
917
|
from hap_cli.core import worksheet_templates as _wt
|
|
883
918
|
new_rel = _wt.build_control(
|
|
884
919
|
"RELATE_SHEET",
|
|
@@ -51,6 +51,55 @@ LANG_LABELS = {
|
|
|
51
51
|
_CONFIG_FILE = Path.home() / ".hap-cli" / "config.json"
|
|
52
52
|
_CATALOG_FILE = Path(__file__).parent / "locale" / "messages.json"
|
|
53
53
|
|
|
54
|
+
# ── Content defaults ─────────────────────────────────────────────────────
|
|
55
|
+
#
|
|
56
|
+
# Text this CLI writes *into* a HAP application, as opposed to the CLI's
|
|
57
|
+
# own interface text above. These end up stored in the customer's
|
|
58
|
+
# application and shown to its end users — a button's confirm dialog, a
|
|
59
|
+
# synthesized field name — so leaving them hardcoded in one language put
|
|
60
|
+
# Simplified Chinese into all-English applications regardless of the
|
|
61
|
+
# account or application language.
|
|
62
|
+
#
|
|
63
|
+
# Kept in source rather than in messages.json because they are payload
|
|
64
|
+
# values, not display strings: they must resolve identically whether or
|
|
65
|
+
# not the catalog file ships, and a missing key is a bug in our code, not
|
|
66
|
+
# an untranslated string.
|
|
67
|
+
CONTENT_DEFAULTS: dict[str, dict[str, str]] = {
|
|
68
|
+
# Custom-button confirm dialog (stored in the button's own
|
|
69
|
+
# sureName / cancelName / confirmMsg fields).
|
|
70
|
+
"button.sure": {
|
|
71
|
+
"en": "OK", "zh-Hans": "确认", "zh-Hant": "確認", "ja": "OK",
|
|
72
|
+
},
|
|
73
|
+
"button.cancel": {
|
|
74
|
+
"en": "Cancel", "zh-Hans": "取消", "zh-Hant": "取消",
|
|
75
|
+
"ja": "キャンセル",
|
|
76
|
+
},
|
|
77
|
+
"button.confirmMsg": {
|
|
78
|
+
"en": "Are you sure you want to do this?",
|
|
79
|
+
"zh-Hans": "你确认执行此操作吗?",
|
|
80
|
+
"zh-Hant": "你確認執行此操作嗎?",
|
|
81
|
+
"ja": "この操作を実行してもよろしいですか?",
|
|
82
|
+
},
|
|
83
|
+
# Name given to the reverse control synthesized when a two-way
|
|
84
|
+
# relation is paired and the caller supplied no name.
|
|
85
|
+
"control.backRelation": {
|
|
86
|
+
"en": "Related records", "zh-Hans": "关联记录",
|
|
87
|
+
"zh-Hant": "關聯記錄", "ja": "関連レコード",
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def content_default(key: str) -> str:
|
|
93
|
+
"""Return the default *content* string for ``key`` in the active
|
|
94
|
+
language, falling back to English.
|
|
95
|
+
|
|
96
|
+
Raises ``KeyError`` for an unknown key — these are our own payload
|
|
97
|
+
defaults, so a miss is a programming error rather than a missing
|
|
98
|
+
translation.
|
|
99
|
+
"""
|
|
100
|
+
entry = CONTENT_DEFAULTS[key]
|
|
101
|
+
return entry.get(get_active_language()) or entry["en"]
|
|
102
|
+
|
|
54
103
|
|
|
55
104
|
def lang_from_hap_account(account_lang: str) -> str:
|
|
56
105
|
"""Map HAP's ``md.global.Account.lang`` field to a CLI language code."""
|