hap-cli 0.8.4__tar.gz → 0.8.5__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.4 → hap_cli-0.8.5}/PKG-INFO +8 -1
- {hap_cli-0.8.4/hap_cli → hap_cli-0.8.5}/README.md +7 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5/hap_cli}/README.md +7 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/README_CN.md +7 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/__init__.py +1 -1
- hap_cli-0.8.5/hap_cli/__main__.py +11 -0
- hap_cli-0.8.5/hap_cli/commands/update_cmd.py +101 -0
- hap_cli-0.8.5/hap_cli/core/update.py +294 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/hap_cli.py +18 -0
- hap_cli-0.8.5/hap_cli/tests/test_update.py +423 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli.egg-info/PKG-INFO +8 -1
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli.egg-info/SOURCES.txt +4 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/MANIFEST.in +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/app_live_tests/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/app_live_tests/config.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/app_live_tests/harness.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/app_live_tests/smoke.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/app_live_tests/state.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/app_editor_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/auth_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/chart_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/config_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/group_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/instance_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/post_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/role_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/commands/worksheet_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/context.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/action_spec_adapter.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/app.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/auth.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/calendar_mod.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/chart.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/chat.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/chatbot.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/contact.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/department.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/apply.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/componentlower.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/editspec.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/errors.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/fieldlower.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/jsonschema_mini.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/models.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/ops.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/planner.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/editor/reader.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/flow_node.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/group.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/instance.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/optionset.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/page.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/post.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/record.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/role.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/session.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/upload.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/workflow.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/workflow_node_dsl.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/worksheet.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/i18n.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/locale/messages.json +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/conftest.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_app_editor.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_app_export_usage.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_bug_036430.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_core.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_full_e2e.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_global_meta.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_approval.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_approval_actions.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_destructive.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_multi_profile.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_semantic_types_registry.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/__main__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/_wftest.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/charts.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/cleanup.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/compiler.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/config.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/errors.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/executor.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/fields.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/hap.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/recording/console.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/recording/report.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/schema.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/seed/template.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/selftest.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/smoke.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/step.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/steps.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/store.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/setup.cfg +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/setup.py +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/skills/hap-cli-app-editor/scripts/editspec/component.schema.json +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/skills/hap-cli-app-editor/scripts/editspec/custom-action.schema.json +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/skills/hap-cli-app-editor/scripts/editspec/envelope.schema.json +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/skills/hap-cli-app-editor/scripts/editspec/field.schema.json +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.8.4 → hap_cli-0.8.5}/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.5
|
|
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,13 @@ body, and a redacted `Authorization` header (first 16 chars). `password`
|
|
|
313
313
|
and other sensitive keys are masked. `HAP_LOG_LEVEL=DEBUG` temporarily
|
|
314
314
|
overrides the persisted level.
|
|
315
315
|
|
|
316
|
+
### update — keep the CLI current
|
|
317
|
+
|
|
318
|
+
| Command | Description |
|
|
319
|
+
|---|---|
|
|
320
|
+
| `update` | Check PyPI for a newer release and upgrade in place, using the same tool the CLI was installed with (pip / pipx / uv) |
|
|
321
|
+
| `update --check` | Only report whether a newer version exists, without installing |
|
|
322
|
+
|
|
316
323
|
---
|
|
317
324
|
|
|
318
325
|
## Default app & organization
|
|
@@ -284,6 +284,13 @@ body, and a redacted `Authorization` header (first 16 chars). `password`
|
|
|
284
284
|
and other sensitive keys are masked. `HAP_LOG_LEVEL=DEBUG` temporarily
|
|
285
285
|
overrides the persisted level.
|
|
286
286
|
|
|
287
|
+
### update — keep the CLI current
|
|
288
|
+
|
|
289
|
+
| Command | Description |
|
|
290
|
+
|---|---|
|
|
291
|
+
| `update` | Check PyPI for a newer release and upgrade in place, using the same tool the CLI was installed with (pip / pipx / uv) |
|
|
292
|
+
| `update --check` | Only report whether a newer version exists, without installing |
|
|
293
|
+
|
|
287
294
|
---
|
|
288
295
|
|
|
289
296
|
## Default app & organization
|
|
@@ -284,6 +284,13 @@ body, and a redacted `Authorization` header (first 16 chars). `password`
|
|
|
284
284
|
and other sensitive keys are masked. `HAP_LOG_LEVEL=DEBUG` temporarily
|
|
285
285
|
overrides the persisted level.
|
|
286
286
|
|
|
287
|
+
### update — keep the CLI current
|
|
288
|
+
|
|
289
|
+
| Command | Description |
|
|
290
|
+
|---|---|
|
|
291
|
+
| `update` | Check PyPI for a newer release and upgrade in place, using the same tool the CLI was installed with (pip / pipx / uv) |
|
|
292
|
+
| `update --check` | Only report whether a newer version exists, without installing |
|
|
293
|
+
|
|
287
294
|
---
|
|
288
295
|
|
|
289
296
|
## Default app & organization
|
|
@@ -260,6 +260,13 @@ hap --json worksheet record list WORKSHEET_ID
|
|
|
260
260
|
|
|
261
261
|
日志默认写入 `~/.hap-cli/hap-cli.log`,10 MB × 3 滚动。开启后每次 API 调用以 `INFO` 级别记录 URL、请求体、响应体,以及脱敏后的 `Authorization` 头(仅前 16 字符);`password` 等敏感字段会被自动打码。临时调试可设 `HAP_LOG_LEVEL=DEBUG`。
|
|
262
262
|
|
|
263
|
+
### update — 升级 CLI
|
|
264
|
+
|
|
265
|
+
| 命令 | 说明 |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `update` | 检查 PyPI 上的最新版本,有新版则用安装时的工具(pip / pipx / uv)原地升级 |
|
|
268
|
+
| `update --check` | 只检查是否有新版本,不安装 |
|
|
269
|
+
|
|
263
270
|
---
|
|
264
271
|
|
|
265
272
|
## 默认应用与组织
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Allow ``python -m hap_cli`` — used by the detached background
|
|
2
|
+
update child (``hap update --background``), which is spawned with
|
|
3
|
+
``sys.executable -m hap_cli`` so it always runs in the same
|
|
4
|
+
environment as the parent process regardless of how the ``hap``
|
|
5
|
+
console script was installed.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from hap_cli.hap_cli import main
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
main()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""CLI command for self-updating hap-cli from PyPI.
|
|
2
|
+
|
|
3
|
+
The hidden ``--background`` flag is the entry point for the detached
|
|
4
|
+
child process spawned by the exit-time hook in ``hap_cli.py`` — it is
|
|
5
|
+
internal plumbing and intentionally absent from ``--help`` and docs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from hap_cli.context import pass_context
|
|
13
|
+
from hap_cli.core import update as upd
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command("update")
|
|
17
|
+
@click.option(
|
|
18
|
+
"--check",
|
|
19
|
+
"check_only",
|
|
20
|
+
is_flag=True,
|
|
21
|
+
help="Only check whether a newer version exists; do not install.",
|
|
22
|
+
)
|
|
23
|
+
@click.option("--background", is_flag=True, hidden=True)
|
|
24
|
+
@pass_context
|
|
25
|
+
def update(ctx, check_only, background):
|
|
26
|
+
"""Upgrade hap-cli to the latest version from PyPI.
|
|
27
|
+
|
|
28
|
+
Checks for a newer release and installs it using the same tool the
|
|
29
|
+
CLI was installed with (pip, pipx, or uv). Installs that are not
|
|
30
|
+
managed from PyPI are left untouched.
|
|
31
|
+
"""
|
|
32
|
+
if background:
|
|
33
|
+
# Detached child: silent check-and-upgrade; results go to the
|
|
34
|
+
# state file and are surfaced on the next foreground run.
|
|
35
|
+
upd.run_background_update()
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
from hap_cli import __version__
|
|
39
|
+
|
|
40
|
+
kind = upd.get_install_kind()
|
|
41
|
+
if kind in ("editable", "unknown"):
|
|
42
|
+
ctx.output(
|
|
43
|
+
{"current": __version__, "install": kind, "updated": False},
|
|
44
|
+
lambda d: click.echo(
|
|
45
|
+
"This copy of hap-cli is not managed from PyPI "
|
|
46
|
+
f"(current version {__version__}) — nothing to update."
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
latest = upd.fetch_latest_version()
|
|
52
|
+
if latest is None:
|
|
53
|
+
ctx.handle_error(Exception("Could not reach PyPI to check for updates."))
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
if not upd.is_newer(latest, __version__):
|
|
57
|
+
upd.save_state(
|
|
58
|
+
{
|
|
59
|
+
"last_check_at": time.time(),
|
|
60
|
+
"latest_version": latest,
|
|
61
|
+
"last_result": "up_to_date",
|
|
62
|
+
"notified": True,
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
ctx.output(
|
|
66
|
+
{"current": __version__, "latest": latest, "updated": False},
|
|
67
|
+
lambda d: click.echo(
|
|
68
|
+
f"hap-cli {__version__} is already the latest version."
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
if check_only:
|
|
74
|
+
ctx.output(
|
|
75
|
+
{"current": __version__, "latest": latest, "updated": False},
|
|
76
|
+
lambda d: click.echo(
|
|
77
|
+
f"A newer version is available: {latest} "
|
|
78
|
+
f"(current {__version__}). Run `hap update` to upgrade."
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
click.echo(f"Upgrading hap-cli {__version__} -> {latest} ...")
|
|
84
|
+
ok, output = upd.perform_upgrade(kind)
|
|
85
|
+
upd.save_state(
|
|
86
|
+
{
|
|
87
|
+
"last_check_at": time.time(),
|
|
88
|
+
"latest_version": latest,
|
|
89
|
+
"last_result": "upgraded" if ok else "upgrade_failed",
|
|
90
|
+
"upgraded_to": latest if ok else None,
|
|
91
|
+
"notified": True,
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
if ok:
|
|
95
|
+
ctx.output(
|
|
96
|
+
{"current": __version__, "latest": latest, "updated": True},
|
|
97
|
+
lambda d: click.echo(f"Upgraded to hap-cli {latest}."),
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
click.echo(output, err=True)
|
|
101
|
+
ctx.handle_error(Exception(f"Upgrade to {latest} failed."))
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""Self-update support for hap-cli.
|
|
2
|
+
|
|
3
|
+
Two entry paths share this module:
|
|
4
|
+
|
|
5
|
+
- ``hap update`` — synchronous, user-invoked check/upgrade.
|
|
6
|
+
- ``hap update --background`` — hidden; spawned as a detached child
|
|
7
|
+
process when a command exits and the last check is older than
|
|
8
|
+
``UPDATE_CHECK_INTERVAL_SECONDS``. It checks PyPI, auto-upgrades if
|
|
9
|
+
newer, and records the outcome in the state file so the *next* run
|
|
10
|
+
can surface a one-line notice.
|
|
11
|
+
|
|
12
|
+
Editable installs (PEP 660 ``direct_url.json`` with
|
|
13
|
+
``dir_info.editable: true``) and unrecognized install origins disable
|
|
14
|
+
the feature entirely — they are by definition not managed via PyPI.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
import time
|
|
24
|
+
from typing import Any, Optional
|
|
25
|
+
|
|
26
|
+
import requests
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("hap_cli.update")
|
|
29
|
+
|
|
30
|
+
PYPI_PACKAGE = "hap-cli"
|
|
31
|
+
PYPI_JSON_URL = f"https://pypi.org/pypi/{PYPI_PACKAGE}/json"
|
|
32
|
+
|
|
33
|
+
# Deliberately short while the tool is iterating quickly; once releases
|
|
34
|
+
# stabilize, bump this in a regular release (it ships with the code, so
|
|
35
|
+
# every upgrade can retune the cadence).
|
|
36
|
+
UPDATE_CHECK_INTERVAL_SECONDS = 3600
|
|
37
|
+
|
|
38
|
+
# Only final releases (digits and dots) are auto-upgrade candidates;
|
|
39
|
+
# pre/dev/post markers ("0.9.0rc1", "1.0.dev2") are skipped.
|
|
40
|
+
_FINAL_RELEASE_RE = re.compile(r"^\d+(\.\d+)*$")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _state_file():
|
|
44
|
+
# Resolve lazily through the session module so tests (and
|
|
45
|
+
# HAP_CLI_HOME) that repoint CONFIG_DIR are honored.
|
|
46
|
+
from hap_cli.core import session
|
|
47
|
+
|
|
48
|
+
return session.CONFIG_DIR / "update_check.json"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ── Install-kind detection ───────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_install_kind() -> str:
|
|
55
|
+
"""Classify how this copy of hap-cli was installed.
|
|
56
|
+
|
|
57
|
+
Returns one of ``editable`` / ``pip`` / ``pipx`` / ``uv`` /
|
|
58
|
+
``unknown``. Only ``pip`` / ``pipx`` / ``uv`` are upgradable from
|
|
59
|
+
PyPI; the other two disable the update feature.
|
|
60
|
+
"""
|
|
61
|
+
import importlib.metadata
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
dist = importlib.metadata.distribution(PYPI_PACKAGE)
|
|
65
|
+
direct_url_text = dist.read_text("direct_url.json")
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.warning("install-kind detection failed: %s", e)
|
|
68
|
+
return "unknown"
|
|
69
|
+
|
|
70
|
+
if direct_url_text:
|
|
71
|
+
try:
|
|
72
|
+
direct_url = json.loads(direct_url_text)
|
|
73
|
+
except ValueError:
|
|
74
|
+
return "unknown"
|
|
75
|
+
if direct_url.get("dir_info", {}).get("editable"):
|
|
76
|
+
return "editable"
|
|
77
|
+
# Installed from a direct URL/path but not editable — we don't
|
|
78
|
+
# know how to keep it current, so stay hands-off.
|
|
79
|
+
return "unknown"
|
|
80
|
+
|
|
81
|
+
prefix = sys.prefix.replace(os.sep, "/")
|
|
82
|
+
if "pipx/venvs" in prefix:
|
|
83
|
+
return "pipx"
|
|
84
|
+
if "uv/tools" in prefix:
|
|
85
|
+
return "uv"
|
|
86
|
+
return "pip"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def build_upgrade_command(kind: str) -> Optional[list]:
|
|
90
|
+
"""Upgrade argv for an install kind, or None if not upgradable."""
|
|
91
|
+
if kind == "pip":
|
|
92
|
+
return [sys.executable, "-m", "pip", "install", "--upgrade", PYPI_PACKAGE]
|
|
93
|
+
if kind == "pipx":
|
|
94
|
+
return ["pipx", "upgrade", PYPI_PACKAGE]
|
|
95
|
+
if kind == "uv":
|
|
96
|
+
return ["uv", "tool", "upgrade", PYPI_PACKAGE]
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# ── Version handling ─────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _version_tuple(version: str) -> tuple:
|
|
104
|
+
return tuple(int(part) for part in version.split("."))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def is_newer(latest: str, current: str) -> bool:
|
|
108
|
+
"""True if ``latest`` is strictly newer than ``current``."""
|
|
109
|
+
try:
|
|
110
|
+
return _version_tuple(latest) > _version_tuple(current)
|
|
111
|
+
except ValueError:
|
|
112
|
+
logger.warning(
|
|
113
|
+
"cannot compare versions latest=%r current=%r", latest, current
|
|
114
|
+
)
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def fetch_latest_version(timeout: int = 5) -> Optional[str]:
|
|
119
|
+
"""Latest final (non-prerelease, non-yanked) version on PyPI.
|
|
120
|
+
|
|
121
|
+
Returns None on any network/parse error — callers treat that as
|
|
122
|
+
"couldn't check", never as an error surfaced to the user.
|
|
123
|
+
"""
|
|
124
|
+
from hap_cli.core.logger import format_response_for_log
|
|
125
|
+
|
|
126
|
+
logger.info("HAP GET %s params={} Authorization=<none>", PYPI_JSON_URL)
|
|
127
|
+
try:
|
|
128
|
+
response = requests.get(PYPI_JSON_URL, timeout=timeout)
|
|
129
|
+
logger.info(
|
|
130
|
+
"HAP GET %s -> %d %s",
|
|
131
|
+
PYPI_JSON_URL,
|
|
132
|
+
response.status_code,
|
|
133
|
+
format_response_for_log(getattr(response, "text", "") or ""),
|
|
134
|
+
)
|
|
135
|
+
response.raise_for_status()
|
|
136
|
+
payload = response.json()
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logger.warning("PyPI version check failed: %s", e)
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
candidates = []
|
|
142
|
+
for version, files in (payload.get("releases") or {}).items():
|
|
143
|
+
if not _FINAL_RELEASE_RE.match(version):
|
|
144
|
+
continue
|
|
145
|
+
if not files or all(f.get("yanked") for f in files):
|
|
146
|
+
continue
|
|
147
|
+
candidates.append(version)
|
|
148
|
+
if not candidates:
|
|
149
|
+
return None
|
|
150
|
+
return max(candidates, key=_version_tuple)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ── State file ───────────────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def load_state() -> dict:
|
|
157
|
+
"""Read the update-check state; empty dict if missing/corrupt."""
|
|
158
|
+
try:
|
|
159
|
+
with open(_state_file(), encoding="utf-8") as f:
|
|
160
|
+
data = json.load(f)
|
|
161
|
+
return data if isinstance(data, dict) else {}
|
|
162
|
+
except FileNotFoundError:
|
|
163
|
+
return {}
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logger.warning("corrupt update state file, resetting: %s", e)
|
|
166
|
+
return {}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def save_state(state: dict) -> None:
|
|
170
|
+
"""Atomically persist the update-check state."""
|
|
171
|
+
path = _state_file()
|
|
172
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
173
|
+
tmp = path.with_suffix(".json.tmp")
|
|
174
|
+
tmp.write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8")
|
|
175
|
+
os.replace(tmp, path)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ── Background check orchestration ───────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def maybe_spawn_background_check() -> None:
|
|
182
|
+
"""Spawn a detached background update check if it is due.
|
|
183
|
+
|
|
184
|
+
Called when a CLI command finishes. The timestamp is bumped
|
|
185
|
+
*before* spawning so rapid consecutive commands cannot stack
|
|
186
|
+
duplicate children — at worst a failed child costs one interval.
|
|
187
|
+
"""
|
|
188
|
+
kind = get_install_kind()
|
|
189
|
+
if kind in ("editable", "unknown"):
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
state = load_state()
|
|
193
|
+
last = state.get("last_check_at") or 0
|
|
194
|
+
now = time.time()
|
|
195
|
+
if now - last < UPDATE_CHECK_INTERVAL_SECONDS:
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
state["last_check_at"] = now
|
|
199
|
+
save_state(state)
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
subprocess.Popen(
|
|
203
|
+
[sys.executable, "-m", "hap_cli", "update", "--background"],
|
|
204
|
+
stdin=subprocess.DEVNULL,
|
|
205
|
+
stdout=subprocess.DEVNULL,
|
|
206
|
+
stderr=subprocess.DEVNULL,
|
|
207
|
+
start_new_session=True,
|
|
208
|
+
)
|
|
209
|
+
logger.info("spawned background update check (install kind: %s)", kind)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.warning("failed to spawn background update check: %s", e)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def perform_upgrade(kind: str) -> tuple:
|
|
215
|
+
"""Run the upgrade command for ``kind``. Returns (ok, output)."""
|
|
216
|
+
cmd = build_upgrade_command(kind)
|
|
217
|
+
if cmd is None:
|
|
218
|
+
return False, f"no upgrade command for install kind {kind!r}"
|
|
219
|
+
logger.info("running upgrade command: %s", " ".join(cmd))
|
|
220
|
+
try:
|
|
221
|
+
proc = subprocess.run(
|
|
222
|
+
cmd, capture_output=True, text=True, timeout=600
|
|
223
|
+
)
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.error("upgrade command failed to run: %s", e)
|
|
226
|
+
return False, str(e)
|
|
227
|
+
output = (proc.stdout or "") + (proc.stderr or "")
|
|
228
|
+
logger.info("upgrade command exited %d", proc.returncode)
|
|
229
|
+
return proc.returncode == 0, output
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def run_background_update() -> None:
|
|
233
|
+
"""Body of the hidden ``hap update --background`` child process.
|
|
234
|
+
|
|
235
|
+
Silent by design: outcomes go to the state file (surfaced by the
|
|
236
|
+
next foreground run) and the log, never to the terminal.
|
|
237
|
+
"""
|
|
238
|
+
from hap_cli import __version__
|
|
239
|
+
|
|
240
|
+
state = load_state()
|
|
241
|
+
state["last_check_at"] = time.time()
|
|
242
|
+
|
|
243
|
+
latest = fetch_latest_version()
|
|
244
|
+
if latest is None:
|
|
245
|
+
state["last_result"] = "check_failed"
|
|
246
|
+
save_state(state)
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
state["latest_version"] = latest
|
|
250
|
+
if not is_newer(latest, __version__):
|
|
251
|
+
state["last_result"] = "up_to_date"
|
|
252
|
+
save_state(state)
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
ok, output = perform_upgrade(get_install_kind())
|
|
256
|
+
if ok:
|
|
257
|
+
state["last_result"] = "upgraded"
|
|
258
|
+
state["upgraded_to"] = latest
|
|
259
|
+
else:
|
|
260
|
+
state["last_result"] = "upgrade_failed"
|
|
261
|
+
state["error"] = output[-500:]
|
|
262
|
+
logger.error("background upgrade to %s failed: %s", latest, output)
|
|
263
|
+
state["notified"] = False
|
|
264
|
+
save_state(state)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def consume_startup_notice() -> Optional[str]:
|
|
268
|
+
"""One-shot user notice from the last background run, if any.
|
|
269
|
+
|
|
270
|
+
Returns the message and marks it notified so it is shown exactly
|
|
271
|
+
once. Silent results (check_failed / up_to_date) return None.
|
|
272
|
+
"""
|
|
273
|
+
state = load_state()
|
|
274
|
+
if state.get("notified", True):
|
|
275
|
+
return None
|
|
276
|
+
|
|
277
|
+
result = state.get("last_result")
|
|
278
|
+
if result == "upgraded":
|
|
279
|
+
msg = (
|
|
280
|
+
f"hap-cli was auto-upgraded to {state.get('upgraded_to')} "
|
|
281
|
+
"(takes effect from this run)."
|
|
282
|
+
)
|
|
283
|
+
elif result == "upgrade_failed":
|
|
284
|
+
latest = state.get("latest_version") or "a newer version"
|
|
285
|
+
msg = (
|
|
286
|
+
f"Automatic upgrade to {latest} failed — "
|
|
287
|
+
"run `hap update` to upgrade manually."
|
|
288
|
+
)
|
|
289
|
+
else:
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
state["notified"] = True
|
|
293
|
+
save_state(state)
|
|
294
|
+
return msg
|
|
@@ -41,6 +41,11 @@ from hap_cli.commands.region_cmd import region
|
|
|
41
41
|
from hap_cli.commands.icon_cmd import icon
|
|
42
42
|
from hap_cli.commands.upload_cmd import upload
|
|
43
43
|
from hap_cli.commands.app_editor_cmd import app_editor
|
|
44
|
+
from hap_cli.commands.update_cmd import update as update_cmd
|
|
45
|
+
from hap_cli.core.update import (
|
|
46
|
+
consume_startup_notice,
|
|
47
|
+
maybe_spawn_background_check,
|
|
48
|
+
)
|
|
44
49
|
|
|
45
50
|
|
|
46
51
|
def clone_group(source, *, name=None, hidden=False):
|
|
@@ -71,6 +76,18 @@ def cli(ctx, json_mode, profile):
|
|
|
71
76
|
ctx.obj.json_mode = json_mode
|
|
72
77
|
ctx.obj.profile = profile
|
|
73
78
|
|
|
79
|
+
# Self-update plumbing. Both halves are local-file-only on the hot
|
|
80
|
+
# path: the notice reads a small JSON state file, and the spawn hook
|
|
81
|
+
# only forks a detached child when the last check is older than the
|
|
82
|
+
# interval (the child does the actual network I/O). Skipped for
|
|
83
|
+
# `update` itself (it manages the state directly) and `repl`
|
|
84
|
+
# (cli() re-runs per REPL line).
|
|
85
|
+
notice = consume_startup_notice()
|
|
86
|
+
if notice:
|
|
87
|
+
click.echo(notice, err=True)
|
|
88
|
+
if ctx.invoked_subcommand not in ("update", "repl"):
|
|
89
|
+
ctx.call_on_close(maybe_spawn_background_check)
|
|
90
|
+
|
|
74
91
|
|
|
75
92
|
# Register command groups
|
|
76
93
|
app.add_command(clone_group(role, name="role"))
|
|
@@ -127,6 +144,7 @@ cli.add_command(icon)
|
|
|
127
144
|
cli.add_command(upload)
|
|
128
145
|
cli.add_command(config_group)
|
|
129
146
|
cli.add_command(app_editor)
|
|
147
|
+
cli.add_command(update_cmd)
|
|
130
148
|
|
|
131
149
|
|
|
132
150
|
# Hidden top-level alias for the old `hap completion` command. The
|