hap-cli 0.8.10__tar.gz → 0.8.12__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.10 → hap_cli-0.8.12}/PKG-INFO +6 -6
- {hap_cli-0.8.10/hap_cli → hap_cli-0.8.12}/README.md +4 -4
- {hap_cli-0.8.10 → hap_cli-0.8.12/hap_cli}/README.md +4 -4
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/README_CN.md +1 -1
- hap_cli-0.8.12/hap_cli/__init__.py +3 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/auth_cmd.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/chart_cmd.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/role_cmd.py +46 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/design/design.schema.json +22 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/selftest.py +95 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/steps.py +41 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/validate.py +48 -2
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/auth.py +2 -2
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/calendar_mod.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/chart.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/chat.py +129 -9
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/chatbot.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/contact.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/department.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/flow_node.py +22 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/group.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/instance.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/optionset.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/page.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/post.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/record.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/role.py +49 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/session.py +6 -6
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/workflow.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/worksheet.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/hap_cli.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_core.py +264 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_full_e2e.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_global_meta.py +1 -1
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/PKG-INFO +6 -6
- {hap_cli-0.8.10 → hap_cli-0.8.12}/setup.py +1 -1
- hap_cli-0.8.10/hap_cli/__init__.py +0 -3
- {hap_cli-0.8.10 → hap_cli-0.8.12}/MANIFEST.in +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/config.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/harness.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/smoke.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/state.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/__main__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/app_creator_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/app_editor_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/config_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/group_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/instance_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/post_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/update_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/worksheet_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/context.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/action_spec_adapter.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/charts.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/cleanup.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/compiler.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/config.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/errors.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/executor.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/fields.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/hap.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/merge.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/console.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/mirror.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/report.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/schema.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/executor.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/template.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/smoke.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/store.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/cli_access.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/apply.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/componentlower.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/editspec.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/errors.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/fieldlower.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/jsonschema_mini.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/models.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/ops.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/planner.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/reader.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/update.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/upload.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/workflow_node_dsl.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/i18n.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/locale/messages.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/conftest.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_app_creator.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_app_editor.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_app_export_usage.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_bug_036430.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_cli_access.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_instance_history_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_approval.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_approval_actions.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_destructive.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_multi_profile.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_record_field_alias.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_semantic_types_registry.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_update.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_workflow_list_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/SOURCES.txt +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/__main__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/_wftest.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/charts.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/cleanup.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/compiler.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/config.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/errors.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/executor.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/fields.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/hap.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/console.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/report.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/schema.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/template.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/selftest.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/smoke.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/step.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/steps.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/store.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/setup.cfg +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/component.schema.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/custom-action.schema.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/envelope.schema.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/field.schema.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/update_optionset.yaml +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hap-cli
|
|
3
|
-
Version: 0.8.
|
|
4
|
-
Summary: CLI harness for
|
|
3
|
+
Version: 0.8.12
|
|
4
|
+
Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
|
|
5
5
|
Author: hap-cli
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -29,7 +29,7 @@ Dynamic: summary
|
|
|
29
29
|
|
|
30
30
|
# hap-cli
|
|
31
31
|
|
|
32
|
-
CLI harness for **
|
|
32
|
+
CLI harness for **Mingdao HAP** (明道云) — an enterprise no-code platform.
|
|
33
33
|
|
|
34
34
|
## Installation
|
|
35
35
|
|
|
@@ -54,7 +54,7 @@ each organization's status (`CLI:on/off`).
|
|
|
54
54
|
### 1. Login
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
#
|
|
57
|
+
# Mingdao SaaS (default)
|
|
58
58
|
hap auth login
|
|
59
59
|
|
|
60
60
|
# Specific environment
|
|
@@ -66,7 +66,7 @@ hap auth login https://hap.example.com # self-hosted
|
|
|
66
66
|
hap auth login https://your-server.com --token YOUR_TOKEN
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
The browser flow opens the
|
|
69
|
+
The browser flow opens the Mingdao login page and captures the token. If the
|
|
70
70
|
callback can't reach the CLI (no GUI, restricted network), interrupt the
|
|
71
71
|
command — it falls back to a paste prompt for the token shown on the success
|
|
72
72
|
page.
|
|
@@ -78,7 +78,7 @@ hap auth logout # log out the current account
|
|
|
78
78
|
|
|
79
79
|
#### Multiple environments / accounts
|
|
80
80
|
|
|
81
|
-
You can stay logged in to several environments (
|
|
81
|
+
You can stay logged in to several environments (Mingdao SaaS, Nocoly, self-hosted) and
|
|
82
82
|
several accounts at once. Each is saved under a name and you switch between them:
|
|
83
83
|
|
|
84
84
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# hap-cli
|
|
2
2
|
|
|
3
|
-
CLI harness for **
|
|
3
|
+
CLI harness for **Mingdao HAP** (明道云) — an enterprise no-code platform.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -25,7 +25,7 @@ each organization's status (`CLI:on/off`).
|
|
|
25
25
|
### 1. Login
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
#
|
|
28
|
+
# Mingdao SaaS (default)
|
|
29
29
|
hap auth login
|
|
30
30
|
|
|
31
31
|
# Specific environment
|
|
@@ -37,7 +37,7 @@ hap auth login https://hap.example.com # self-hosted
|
|
|
37
37
|
hap auth login https://your-server.com --token YOUR_TOKEN
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
The browser flow opens the
|
|
40
|
+
The browser flow opens the Mingdao login page and captures the token. If the
|
|
41
41
|
callback can't reach the CLI (no GUI, restricted network), interrupt the
|
|
42
42
|
command — it falls back to a paste prompt for the token shown on the success
|
|
43
43
|
page.
|
|
@@ -49,7 +49,7 @@ hap auth logout # log out the current account
|
|
|
49
49
|
|
|
50
50
|
#### Multiple environments / accounts
|
|
51
51
|
|
|
52
|
-
You can stay logged in to several environments (
|
|
52
|
+
You can stay logged in to several environments (Mingdao SaaS, Nocoly, self-hosted) and
|
|
53
53
|
several accounts at once. Each is saved under a name and you switch between them:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# hap-cli
|
|
2
2
|
|
|
3
|
-
CLI harness for **
|
|
3
|
+
CLI harness for **Mingdao HAP** (明道云) — an enterprise no-code platform.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -25,7 +25,7 @@ each organization's status (`CLI:on/off`).
|
|
|
25
25
|
### 1. Login
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
#
|
|
28
|
+
# Mingdao SaaS (default)
|
|
29
29
|
hap auth login
|
|
30
30
|
|
|
31
31
|
# Specific environment
|
|
@@ -37,7 +37,7 @@ hap auth login https://hap.example.com # self-hosted
|
|
|
37
37
|
hap auth login https://your-server.com --token YOUR_TOKEN
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
The browser flow opens the
|
|
40
|
+
The browser flow opens the Mingdao login page and captures the token. If the
|
|
41
41
|
callback can't reach the CLI (no GUI, restricted network), interrupt the
|
|
42
42
|
command — it falls back to a paste prompt for the token shown on the success
|
|
43
43
|
page.
|
|
@@ -49,7 +49,7 @@ hap auth logout # log out the current account
|
|
|
49
49
|
|
|
50
50
|
#### Multiple environments / accounts
|
|
51
51
|
|
|
52
|
-
You can stay logged in to several environments (
|
|
52
|
+
You can stay logged in to several environments (Mingdao SaaS, Nocoly, self-hosted) and
|
|
53
53
|
several accounts at once. Each is saved under a name and you switch between them:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
@@ -378,7 +378,7 @@ def auth_whoami(ctx):
|
|
|
378
378
|
# Read login_url from the active (or --profile) credential profile —
|
|
379
379
|
# the in-memory Session may have been mutated, and we want whoami to
|
|
380
380
|
# probe the server recorded for the profile in use. When the profile
|
|
381
|
-
# has no URL, fall back to the
|
|
381
|
+
# has no URL, fall back to the Mingdao SaaS host. Token failures
|
|
382
382
|
# (missing / expired) surface uniformly via AuthenticationError
|
|
383
383
|
# → "Not logged in" hint from handle_error.
|
|
384
384
|
profile_name = resolve_profile_name(ctx.profile)
|
|
@@ -21,7 +21,7 @@ def chart():
|
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
# Report-type enum values used by the
|
|
24
|
+
# Report-type enum values used by the Mingdao statistics module. The CLI
|
|
25
25
|
# accepts the integer directly so users can pass uncommon types without us
|
|
26
26
|
# having to enumerate every one. Note: there is NO type 4 — passing 4 is
|
|
27
27
|
# coerced to 10 (number) by the server, so it is omitted here (bug #036584).
|
|
@@ -208,6 +208,52 @@ def role_set_permissions(ctx, app_id, role_id, permissions, permission_way):
|
|
|
208
208
|
ctx.handle_error(e)
|
|
209
209
|
|
|
210
210
|
|
|
211
|
+
@role.command("set-chatbots")
|
|
212
|
+
@click.argument("app_id")
|
|
213
|
+
@click.argument("role_id")
|
|
214
|
+
@click.option("--enable-all", is_flag=True,
|
|
215
|
+
help="Grant this role access to every AI assistant in the app.")
|
|
216
|
+
@click.option("--disable-all", is_flag=True,
|
|
217
|
+
help="Remove this role's access to every AI assistant in the app.")
|
|
218
|
+
@click.option("--chatbots-json", default="",
|
|
219
|
+
help='Per-assistant access as a JSON array, e.g. '
|
|
220
|
+
'[{"id":"<assistant-id>","enable":true}].')
|
|
221
|
+
@pass_context
|
|
222
|
+
def role_set_chatbots(ctx, app_id, role_id, enable_all, disable_all, chatbots_json):
|
|
223
|
+
"""Set which AI assistants a role can access.
|
|
224
|
+
|
|
225
|
+
A newly created fine-grained role has no AI-assistant access until it
|
|
226
|
+
is granted here. Provide exactly one of --enable-all, --disable-all,
|
|
227
|
+
or --chatbots-json.
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
chosen = sum(bool(x) for x in (enable_all, disable_all, chatbots_json))
|
|
231
|
+
if chosen != 1:
|
|
232
|
+
raise click.UsageError(
|
|
233
|
+
"Provide exactly one of --enable-all, --disable-all, "
|
|
234
|
+
"or --chatbots-json."
|
|
235
|
+
)
|
|
236
|
+
session = ctx.get_session()
|
|
237
|
+
enables = None
|
|
238
|
+
default = None
|
|
239
|
+
if enable_all:
|
|
240
|
+
default = True
|
|
241
|
+
elif disable_all:
|
|
242
|
+
default = False
|
|
243
|
+
else:
|
|
244
|
+
entries = json.loads(chatbots_json)
|
|
245
|
+
enables = {
|
|
246
|
+
e["id"]: bool(e.get("enable", True))
|
|
247
|
+
for e in entries if e.get("id")
|
|
248
|
+
}
|
|
249
|
+
data = role_mod.set_role_chatbots(
|
|
250
|
+
session, app_id, role_id, enables=enables, default=default,
|
|
251
|
+
)
|
|
252
|
+
ctx.output(data, lambda d: click.echo("AI assistant access updated."))
|
|
253
|
+
except Exception as e:
|
|
254
|
+
ctx.handle_error(e)
|
|
255
|
+
|
|
256
|
+
|
|
211
257
|
@role.command("rename")
|
|
212
258
|
@click.argument("app_id")
|
|
213
259
|
@click.argument("role_id")
|
|
@@ -3485,6 +3485,28 @@
|
|
|
3485
3485
|
},
|
|
3486
3486
|
"description": "permission_scope=0 时的自定义页面权限明细(页面按逻辑名)。"
|
|
3487
3487
|
},
|
|
3488
|
+
"chatbot_permissions": {
|
|
3489
|
+
"type": "array",
|
|
3490
|
+
"items": {
|
|
3491
|
+
"type": "object",
|
|
3492
|
+
"required": [
|
|
3493
|
+
"chatbot"
|
|
3494
|
+
],
|
|
3495
|
+
"additionalProperties": false,
|
|
3496
|
+
"properties": {
|
|
3497
|
+
"chatbot": {
|
|
3498
|
+
"type": "string",
|
|
3499
|
+
"description": "AI 助手逻辑名 -> id。"
|
|
3500
|
+
},
|
|
3501
|
+
"enable": {
|
|
3502
|
+
"type": "boolean",
|
|
3503
|
+
"default": true,
|
|
3504
|
+
"description": "是否可访问该 AI 助手。"
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
},
|
|
3508
|
+
"description": "可访问的 AI 助手(chatbot)明细,按 chatbot 逻辑名引用。省略该键 = 该角色可访问应用内全部 AI 助手(默认开放)。一旦提供本数组,则只有列出且 enable=true 的助手可访问,其余助手一律不可访问。"
|
|
3509
|
+
},
|
|
3488
3510
|
"members": {
|
|
3489
3511
|
"type": "object",
|
|
3490
3512
|
"additionalProperties": false,
|
|
@@ -67,6 +67,45 @@ def test_store() -> None:
|
|
|
67
67
|
pass
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
def test_role_chatbot_enables() -> None:
|
|
71
|
+
from hap_cli.core.app_creator.store import Store
|
|
72
|
+
from hap_cli.core.app_creator.steps import _role_chatbot_enables
|
|
73
|
+
|
|
74
|
+
d = Path(tempfile.mkdtemp()) / "appCB"
|
|
75
|
+
s = Store(d)
|
|
76
|
+
s.put_app("appCB", "Demo", "org1", [{"id": "sec1", "name": "分析"}])
|
|
77
|
+
# No chatbots in the app -> nothing to grant.
|
|
78
|
+
assert _role_chatbot_enables(s, {"name": "r"}) == {}
|
|
79
|
+
|
|
80
|
+
s.put_entity("chatbot", "cb1", "订单助手", {"name": "订单助手"})
|
|
81
|
+
s.put_entity("chatbot", "cb2", "客服助手", {"name": "客服助手"})
|
|
82
|
+
# chatbot_permissions omitted -> every assistant is granted (the fix).
|
|
83
|
+
assert _role_chatbot_enables(s, {"name": "r"}) == {"cb1": True, "cb2": True}
|
|
84
|
+
# Provided -> whitelist: only listed enable:true granted, the rest off.
|
|
85
|
+
role = {"name": "r",
|
|
86
|
+
"chatbot_permissions": [{"chatbot": "订单助手", "enable": True}]}
|
|
87
|
+
assert _role_chatbot_enables(s, role) == {"cb1": True, "cb2": False}
|
|
88
|
+
# enable defaults true when omitted; explicit false revokes a listed one.
|
|
89
|
+
role2 = {"name": "r", "chatbot_permissions": [
|
|
90
|
+
{"chatbot": "订单助手", "enable": False},
|
|
91
|
+
{"chatbot": "客服助手"},
|
|
92
|
+
]}
|
|
93
|
+
assert _role_chatbot_enables(s, role2) == {"cb1": False, "cb2": True}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_role_chatbot_permissions_schema() -> None:
|
|
97
|
+
design = {
|
|
98
|
+
"app": {"name": "x"},
|
|
99
|
+
"worksheets": [{"name": "t", "fields": [
|
|
100
|
+
{"type": "Text", "name": "标题", "is_title": True}]}],
|
|
101
|
+
"chatbots": [{"name": "助手"}],
|
|
102
|
+
"roles": [{"name": "r", "permission_scope": "0",
|
|
103
|
+
"chatbot_permissions": [{"chatbot": "助手", "enable": True}]}],
|
|
104
|
+
}
|
|
105
|
+
errs = _vd(design)
|
|
106
|
+
assert not any("chatbot_permissions" in e for e in errs), errs
|
|
107
|
+
|
|
108
|
+
|
|
70
109
|
def test_schema() -> None:
|
|
71
110
|
from hap_cli.core.app_creator import schema
|
|
72
111
|
|
|
@@ -1607,6 +1646,62 @@ def test_icon_validation() -> None:
|
|
|
1607
1646
|
assert any("worksheets[0].icon" in e and "1_2_order" in e for e in errs), errs
|
|
1608
1647
|
|
|
1609
1648
|
|
|
1649
|
+
def test_icon_uniqueness() -> None:
|
|
1650
|
+
"""Nav-level entities (worksheets + custom_pages) must each carry a DISTINCT
|
|
1651
|
+
icon — the 零冗余 golden rule. Reusing one icon across two worksheets/pages
|
|
1652
|
+
is a hard validation error, caught offline before build. Buttons and
|
|
1653
|
+
page-component icons are exempt (icon reuse there is legitimate and common).
|
|
1654
|
+
Pure check — no catalogue lookup needed."""
|
|
1655
|
+
from hap_cli.core.app_creator import validate
|
|
1656
|
+
|
|
1657
|
+
# Two worksheets sharing one icon -> a single error that names both owners.
|
|
1658
|
+
dup = {
|
|
1659
|
+
"app": {"name": "D", "icon": "sys_1_2_order"},
|
|
1660
|
+
"worksheets": [
|
|
1661
|
+
{"name": "入库单", "icon": "sys_form_symbol"},
|
|
1662
|
+
{"name": "出库单", "icon": "sys_form_symbol"},
|
|
1663
|
+
{"name": "物料", "icon": "sys_18_5_warehouse"},
|
|
1664
|
+
],
|
|
1665
|
+
}
|
|
1666
|
+
errs = validate._check_icon_uniqueness(dup)
|
|
1667
|
+
assert len(errs) == 1, errs
|
|
1668
|
+
assert "sys_form_symbol" in errs[0], errs
|
|
1669
|
+
assert "入库单" in errs[0] and "出库单" in errs[0], errs
|
|
1670
|
+
|
|
1671
|
+
# A worksheet and a custom_page sharing an icon also collide — they sit in
|
|
1672
|
+
# the same left-nav strip.
|
|
1673
|
+
cross = {
|
|
1674
|
+
"worksheets": [{"name": "订单", "icon": "sys_form_symbol"}],
|
|
1675
|
+
"custom_pages": [{"name": "看板", "icon": "sys_form_symbol"}],
|
|
1676
|
+
}
|
|
1677
|
+
errs = validate._check_icon_uniqueness(cross)
|
|
1678
|
+
assert len(errs) == 1, errs
|
|
1679
|
+
assert "订单" in errs[0] and "看板" in errs[0], errs
|
|
1680
|
+
|
|
1681
|
+
# All-distinct nav icons pass clean (app icon does not participate).
|
|
1682
|
+
ok = {
|
|
1683
|
+
"app": {"name": "D", "icon": "sys_1_2_order"},
|
|
1684
|
+
"worksheets": [
|
|
1685
|
+
{"name": "A", "icon": "sys_1_2_order"}, # may coincide with app
|
|
1686
|
+
{"name": "B", "icon": "sys_18_5_warehouse"},
|
|
1687
|
+
],
|
|
1688
|
+
"custom_pages": [{"name": "P", "icon": "sys_form_symbol"}],
|
|
1689
|
+
}
|
|
1690
|
+
assert validate._check_icon_uniqueness(ok) == []
|
|
1691
|
+
|
|
1692
|
+
# Button / page-component icon reuse is EXEMPT: a worksheet icon repeated on
|
|
1693
|
+
# a page-component button is not a collision.
|
|
1694
|
+
exempt = {
|
|
1695
|
+
"worksheets": [{"name": "订单", "icon": "sys_form_symbol"}],
|
|
1696
|
+
"custom_pages": [{
|
|
1697
|
+
"name": "工作台", "icon": "sys_1_2_order",
|
|
1698
|
+
"components": [{"name": "C",
|
|
1699
|
+
"button": {"buttons": [{"icon": "sys_form_symbol"}]}}],
|
|
1700
|
+
}],
|
|
1701
|
+
}
|
|
1702
|
+
assert validate._check_icon_uniqueness(exempt) == []
|
|
1703
|
+
|
|
1704
|
+
|
|
1610
1705
|
def test_append_strips_client_control_ids() -> None:
|
|
1611
1706
|
"""Cross-sheet controls (forward relations, lookups, rollups, …) are
|
|
1612
1707
|
appended with AddWorksheetControls, which only mints a real 24-hex
|
|
@@ -962,6 +962,33 @@ def _build_ws_perm(store: Store, wp: dict[str, Any]) -> dict[str, Any]:
|
|
|
962
962
|
}
|
|
963
963
|
|
|
964
964
|
|
|
965
|
+
def _role_chatbot_enables(store: Store, role: dict[str, Any]) -> dict[str, bool]:
|
|
966
|
+
"""Map every app chatbot id -> whether this role may access it.
|
|
967
|
+
|
|
968
|
+
The fine-grained role-create endpoint can't grant AI-assistant
|
|
969
|
+
access, so the role handler patches it in afterwards (via
|
|
970
|
+
``app role set-chatbots`` -> EditAppRole). Default: every assistant in
|
|
971
|
+
the app is accessible to the role. A role narrows this with
|
|
972
|
+
``chatbot_permissions`` (a list of ``{chatbot, enable}``); when that
|
|
973
|
+
key is present only the listed assistants with ``enable: true`` are
|
|
974
|
+
granted and all other assistants are revoked.
|
|
975
|
+
|
|
976
|
+
Returns an empty mapping when the app has no chatbots (nothing to do).
|
|
977
|
+
"""
|
|
978
|
+
ids = [c["id"] for c in store.list("chatbot") if c.get("id")]
|
|
979
|
+
if not ids:
|
|
980
|
+
return {}
|
|
981
|
+
cp = role.get("chatbot_permissions")
|
|
982
|
+
if cp is None:
|
|
983
|
+
return {cid: True for cid in ids}
|
|
984
|
+
wanted: dict[str, bool] = {}
|
|
985
|
+
for item in cp:
|
|
986
|
+
wanted[store.resolve("chatbot", item["chatbot"])] = bool(
|
|
987
|
+
item.get("enable", True)
|
|
988
|
+
)
|
|
989
|
+
return {cid: bool(wanted.get(cid, False)) for cid in ids}
|
|
990
|
+
|
|
991
|
+
|
|
965
992
|
@handler("role")
|
|
966
993
|
def _h_role(ctx: ExecCtx, step: Step) -> StepOutcome:
|
|
967
994
|
store = _require_store(ctx)
|
|
@@ -1021,6 +1048,20 @@ def _h_role(ctx: ExecCtx, step: Step) -> StepOutcome:
|
|
|
1021
1048
|
hap.run(m_argv)
|
|
1022
1049
|
commands.append(m_argv)
|
|
1023
1050
|
|
|
1051
|
+
# Grant AI-assistant (chatbot) access. The fine-grained role-create
|
|
1052
|
+
# endpoint has no field for it, so every role would otherwise be unable
|
|
1053
|
+
# to reach the app's assistants. Chatbots are built before roles, so
|
|
1054
|
+
# they already exist in the store here.
|
|
1055
|
+
enables = _role_chatbot_enables(store, role)
|
|
1056
|
+
if role_id and enables:
|
|
1057
|
+
cb_argv = ["app", "role", "set-chatbots", ctx.store.app_id, role_id,
|
|
1058
|
+
"--chatbots-json",
|
|
1059
|
+
json.dumps([{"id": cid, "enable": en}
|
|
1060
|
+
for cid, en in enables.items()],
|
|
1061
|
+
ensure_ascii=False)]
|
|
1062
|
+
hap.run(cb_argv)
|
|
1063
|
+
commands.append(cb_argv)
|
|
1064
|
+
|
|
1024
1065
|
return StepOutcome(
|
|
1025
1066
|
created_id=role_id, summary=f"role {name!r} scope={scope}",
|
|
1026
1067
|
commands=commands, capture_files=[f"role_{role_id}.json", "roles.json"],
|
|
@@ -6,9 +6,9 @@ One file validates that file. Multiple files are MERGED (the split-generation
|
|
|
6
6
|
workflow: foundation + independently-authored parts) and the combined document
|
|
7
7
|
is validated as a whole — so cross-part logical-name references are checked.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Three layers run here:
|
|
10
10
|
1. structural + reference validation — pure, offline (``schema.load_designs``).
|
|
11
|
-
2. icon
|
|
11
|
+
2. icon existence — every ``icon`` field (app / worksheet / page / page
|
|
12
12
|
component / button) must be an EXACT, real catalogue icon. We confirm
|
|
13
13
|
each via ``hap icon search <icon> --limit 1`` and require the top hit's
|
|
14
14
|
``fileName`` to equal the icon byte-for-byte. ``hap icon search`` is a
|
|
@@ -17,6 +17,11 @@ Two layers run here:
|
|
|
17
17
|
A fabricated icon (``sys_15_3_user``) or a non-canonical short form
|
|
18
18
|
(``1_2_order`` instead of ``sys_1_2_order``) is rejected — the backend
|
|
19
19
|
only accepts exact catalogue fileNames.
|
|
20
|
+
3. icon uniqueness — nav-level entities (top-level worksheets + custom_pages)
|
|
21
|
+
must each carry a DISTINCT icon (the 零冗余 / "不同实体用不同图标" rule).
|
|
22
|
+
They sit side by side in the app's left navigation, so a repeated icon is
|
|
23
|
+
visually confusing. Pure/offline. Button and page-component icons are
|
|
24
|
+
EXEMPT — reuse there (many "新建"/"+" tiles) is legitimate.
|
|
20
25
|
|
|
21
26
|
Exit code 0 = valid, 2 = validation errors (printed with their JSON paths).
|
|
22
27
|
Run this after authoring a design and before ``build`` so structural and icon
|
|
@@ -92,6 +97,44 @@ def _check_icons(doc: Any, runner: Callable[..., Any] = hap.run) -> list[str]:
|
|
|
92
97
|
return errors
|
|
93
98
|
|
|
94
99
|
|
|
100
|
+
def _collect_entity_icons(doc: Any) -> list[tuple[str, str, str]]:
|
|
101
|
+
"""Return ``(kind, name, icon)`` for each NAV-LEVEL entity that carries an
|
|
102
|
+
icon — top-level worksheets and custom_pages only. These appear together in
|
|
103
|
+
the app's left navigation, so each needs a visually distinct icon (the
|
|
104
|
+
零冗余 rule). Deliberately does NOT descend into page components or button
|
|
105
|
+
``buttons[]``: reusing an icon on action buttons is legitimate and common,
|
|
106
|
+
so those are exempt from the uniqueness check."""
|
|
107
|
+
out: list[tuple[str, str, str]] = []
|
|
108
|
+
if not isinstance(doc, dict):
|
|
109
|
+
return out
|
|
110
|
+
for kind, key in (("工作表", "worksheets"), ("自定义页面", "custom_pages")):
|
|
111
|
+
items = doc.get(key)
|
|
112
|
+
if not isinstance(items, list):
|
|
113
|
+
continue
|
|
114
|
+
for it in items:
|
|
115
|
+
if isinstance(it, dict) and isinstance(it.get("icon"), str):
|
|
116
|
+
out.append((kind, str(it.get("name", "?")), it["icon"]))
|
|
117
|
+
return out
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _check_icon_uniqueness(doc: Any) -> list[str]:
|
|
121
|
+
"""Return errors for icons reused across nav-level entities (worksheets +
|
|
122
|
+
custom_pages). Each must be unique among them — 不同实体用不同图标 (零冗余).
|
|
123
|
+
One error per colliding icon, naming every owner. Pure/offline; no catalogue
|
|
124
|
+
lookup. Iteration order is document order, so messages are deterministic."""
|
|
125
|
+
by_icon: dict[str, list[str]] = {}
|
|
126
|
+
for kind, name, icon in _collect_entity_icons(doc):
|
|
127
|
+
by_icon.setdefault(icon, []).append(f"{kind} {name!r}")
|
|
128
|
+
errors: list[str] = []
|
|
129
|
+
for icon, owners in by_icon.items():
|
|
130
|
+
if len(owners) > 1:
|
|
131
|
+
errors.append(
|
|
132
|
+
f"icon {icon!r} is reused by {', '.join(owners)} — give each "
|
|
133
|
+
f"worksheet/page a DISTINCT icon (零冗余: 不同实体用不同图标). "
|
|
134
|
+
f"Find alternatives with `hap icon search <keywords>`.")
|
|
135
|
+
return errors
|
|
136
|
+
|
|
137
|
+
|
|
95
138
|
def main(argv: list[str] | None = None) -> int:
|
|
96
139
|
argv = list(sys.argv[1:] if argv is None else argv)
|
|
97
140
|
if not argv:
|
|
@@ -112,6 +155,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
112
155
|
except HapCommandError as e:
|
|
113
156
|
print(f"✗ cannot verify icons: {e}", file=sys.stderr)
|
|
114
157
|
return 2
|
|
158
|
+
# Uniqueness is a pure check (no catalogue lookup) — fold it into the same
|
|
159
|
+
# error bucket so existence + dedup problems print together.
|
|
160
|
+
icon_errors += _check_icon_uniqueness(doc)
|
|
115
161
|
if icon_errors:
|
|
116
162
|
joined = "\n - ".join(icon_errors)
|
|
117
163
|
print(f"✗ design failed icon validation:\n - {joined}", file=sys.stderr)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Browser-based authentication flow for
|
|
1
|
+
"""Browser-based authentication flow for Mingdao HAP.
|
|
2
2
|
|
|
3
3
|
Implements a local callback server pattern:
|
|
4
4
|
1. Start local HTTP server on a free port
|
|
@@ -139,7 +139,7 @@ def fetch_global_meta(host: str, token: str) -> dict:
|
|
|
139
139
|
host: Login URL the user entered at ``hap auth login`` (e.g.
|
|
140
140
|
'https://www.mingdao.com' or a self-hosted host). The API
|
|
141
141
|
base is derived from this value. When empty, defaults to
|
|
142
|
-
the
|
|
142
|
+
the Mingdao SaaS host (``https://www.mingdao.com``).
|
|
143
143
|
token: Auth token.
|
|
144
144
|
|
|
145
145
|
Raises:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Worksheet chart (统计图) backend wrappers.
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The Mingdao statistics module exposes chart CRUD under two URL families
|
|
4
4
|
on the report subdomain:
|
|
5
5
|
|
|
6
6
|
* ``/report/list`` GET — list charts of a worksheet
|