hap-cli 0.9.5__tar.gz → 0.9.7__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.9.5 → hap_cli-0.9.7}/PKG-INFO +3 -2
- {hap_cli-0.9.5/hap_cli → hap_cli-0.9.7}/README.md +2 -1
- {hap_cli-0.9.5 → hap_cli-0.9.7/hap_cli}/README.md +2 -1
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/README_CN.md +2 -1
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/__init__.py +1 -1
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/config_cmd.py +42 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/group_cmd.py +54 -20
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/post_cmd.py +63 -15
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/context.py +26 -1
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/chat.py +36 -19
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/group.py +43 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/post.py +60 -3
- hap_cli-0.9.7/hap_cli/core/relogin.py +255 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/session.py +50 -1
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/upload.py +33 -3
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/post.md +19 -5
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/index.json +2 -2
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/post.md +15 -4
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/locale/messages.json +50 -2
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/conftest.py +12 -0
- hap_cli-0.9.7/hap_cli/tests/test_post_group_friction.py +157 -0
- hap_cli-0.9.7/hap_cli/tests/test_relogin.py +234 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/PKG-INFO +3 -2
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/SOURCES.txt +3 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/MANIFEST.in +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/config.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/fixtures/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/fixtures/feedback_scenario.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/harness.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/smoke.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/state.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/__main__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/_entry.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/app_creator_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/app_editor_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/attachment_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/auth_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/chart_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/guide_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/instance_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/role_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/update_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/worksheet_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/action_spec_adapter.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/charts.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/cleanup.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/compiler.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/config.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/design/design.schema.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/errors.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/executor.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/fields.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/hap.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/merge.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/console.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/mirror.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/report.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/schema.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/executor.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/template.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/selftest.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/smoke.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/steps.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/store.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/validate.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/attachment.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/auth.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/calendar_mod.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/chart.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/chatbot.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/cli_access.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/condition_dsl.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/contact.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/department.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/apply.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/componentlower.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/component.schema.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/field.schema.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/errors.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/fieldlower.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/jsonschema_mini.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/models.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/ops.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/planner.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/reader.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/filter_v3.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/flow_node.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/guide.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/instance.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/layout_check.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/optionset.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/page.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_defaults.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_form_checks.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_lookups.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_rules.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/relation_scope.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/role.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/rule_spec_adapter.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/update.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/workflow.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/workflow_node_dsl.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet_form.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet_reference.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/approval.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/chart.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/messages.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/overview.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/record.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/setup.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/skills.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/workflow.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/worksheet.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/approval.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/chart.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/messages.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/overview.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/record.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/setup.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/skills.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/workflow.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/i18n.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_creator.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_editor.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_export_usage.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_light_color.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_section_name.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_approval_wire.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_atomic_write.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_attachment_share.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_batch_add_atomic.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_bug_036430.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_bug_036750.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_calendar_date_range.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chart_filter_merge.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_attachment_send.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_messages_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_recall_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_send_socket.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_cli_access.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_coded_responses.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_content_language.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_control_lookup_guard.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_core.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_create_worksheet_partial.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_engine_core_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_bidirectional.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_entry_module.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_http_error_body.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_layout_check.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_node_summary.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_post_cursor.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_subgroups.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_subtable_read.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_full_e2e.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_chart_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_record_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_upload_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g4_approval_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g8_filter_operator.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_global_meta.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_guide.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_guide_sections.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_instance_envelope.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_instance_history_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_approval.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_approval_actions.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_destructive.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_multi_profile.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_page_filters_group_read.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_page_filters_group_update.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_page_save_page_id.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_post_attachments.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_post_scope_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_business_rules.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_defaults.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_field_alias.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_form_checks.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_run_action.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_write_fixes.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_relation_scope.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_role_app_scope.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_save_rule_spec.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_select_add_option.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_semantic_types_registry.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_update.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_update_mode.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_upload_allow_down.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_upload_host_resolution.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_bool_flags.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_response_format_json.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_config_keys.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_inner_scope.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_structure_listing.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wo4_result_handling.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_code_node.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_list_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_node_wire.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_create_section.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_switch.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/atomic_write.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/confirm.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/__main__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/_wftest.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/charts.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/cleanup.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/compiler.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/config.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/errors.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/executor.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/fields.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/hap.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/console.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/report.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/schema.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/template.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/selftest.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/smoke.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/step.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/steps.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/store.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/setup.cfg +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/setup.py +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.9.5 → hap_cli-0.9.7}/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.9.
|
|
3
|
+
Version: 0.9.7
|
|
4
4
|
Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
|
|
5
5
|
Author: hap-cli
|
|
6
6
|
License: Apache-2.0
|
|
@@ -289,7 +289,7 @@ discover every subcommand.
|
|
|
289
289
|
|
|
290
290
|
| Command | Description |
|
|
291
291
|
|---|---|
|
|
292
|
-
| `group list` | List open groups you have joined (`--org-id`, `--keywords`,
|
|
292
|
+
| `group list` | List open groups you have joined (`--org-id`, `--keywords`, pagination; `has_more`/`next_page` when more exist, `--all` for every group, `--feed-only` for groups that can receive a post) |
|
|
293
293
|
| `group info GROUP_ID` | Group metadata |
|
|
294
294
|
| `group create` | Create a new group (`--name`, `--org-id`, optional `--member`) |
|
|
295
295
|
| `group add-member` | Add one or more members (`--group-id`, `--account-id`) |
|
|
@@ -339,6 +339,7 @@ discover every subcommand.
|
|
|
339
339
|
| `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
|
|
340
340
|
| `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
|
|
341
341
|
| `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
|
|
342
|
+
| `config auto-login [on\|off]` | Renew an expired login in the browser (one click on Authorize) and carry on; `off` stops with "not logged in" |
|
|
342
343
|
| `config log on / off / status` | Toggle and inspect API logging |
|
|
343
344
|
| `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
|
|
344
345
|
| `config log view [--lines N] [--no-follow]` | Tail the log file |
|
|
@@ -260,7 +260,7 @@ discover every subcommand.
|
|
|
260
260
|
|
|
261
261
|
| Command | Description |
|
|
262
262
|
|---|---|
|
|
263
|
-
| `group list` | List open groups you have joined (`--org-id`, `--keywords`,
|
|
263
|
+
| `group list` | List open groups you have joined (`--org-id`, `--keywords`, pagination; `has_more`/`next_page` when more exist, `--all` for every group, `--feed-only` for groups that can receive a post) |
|
|
264
264
|
| `group info GROUP_ID` | Group metadata |
|
|
265
265
|
| `group create` | Create a new group (`--name`, `--org-id`, optional `--member`) |
|
|
266
266
|
| `group add-member` | Add one or more members (`--group-id`, `--account-id`) |
|
|
@@ -310,6 +310,7 @@ discover every subcommand.
|
|
|
310
310
|
| `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
|
|
311
311
|
| `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
|
|
312
312
|
| `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
|
|
313
|
+
| `config auto-login [on\|off]` | Renew an expired login in the browser (one click on Authorize) and carry on; `off` stops with "not logged in" |
|
|
313
314
|
| `config log on / off / status` | Toggle and inspect API logging |
|
|
314
315
|
| `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
|
|
315
316
|
| `config log view [--lines N] [--no-follow]` | Tail the log file |
|
|
@@ -260,7 +260,7 @@ discover every subcommand.
|
|
|
260
260
|
|
|
261
261
|
| Command | Description |
|
|
262
262
|
|---|---|
|
|
263
|
-
| `group list` | List open groups you have joined (`--org-id`, `--keywords`,
|
|
263
|
+
| `group list` | List open groups you have joined (`--org-id`, `--keywords`, pagination; `has_more`/`next_page` when more exist, `--all` for every group, `--feed-only` for groups that can receive a post) |
|
|
264
264
|
| `group info GROUP_ID` | Group metadata |
|
|
265
265
|
| `group create` | Create a new group (`--name`, `--org-id`, optional `--member`) |
|
|
266
266
|
| `group add-member` | Add one or more members (`--group-id`, `--account-id`) |
|
|
@@ -310,6 +310,7 @@ discover every subcommand.
|
|
|
310
310
|
| `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
|
|
311
311
|
| `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
|
|
312
312
|
| `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
|
|
313
|
+
| `config auto-login [on\|off]` | Renew an expired login in the browser (one click on Authorize) and carry on; `off` stops with "not logged in" |
|
|
313
314
|
| `config log on / off / status` | Toggle and inspect API logging |
|
|
314
315
|
| `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
|
|
315
316
|
| `config log view [--lines N] [--no-follow]` | Tail the log file |
|
|
@@ -243,7 +243,7 @@ hap --json worksheet record list WORKSHEET_ID
|
|
|
243
243
|
|
|
244
244
|
| 命令 | 说明 |
|
|
245
245
|
|---|---|
|
|
246
|
-
| `group list` | 分页列出当前用户已加入的正常群组(可用 `--org-id`、`--keywords`
|
|
246
|
+
| `group list` | 分页列出当前用户已加入的正常群组(可用 `--org-id`、`--keywords` 筛选;还有更多时返回 `has_more`/`next_page`;`--all` 取全部;`--feed-only` 只列能发动态的群) |
|
|
247
247
|
|
|
248
248
|
### region — 行政区划
|
|
249
249
|
|
|
@@ -289,6 +289,7 @@ hap --json worksheet record list WORKSHEET_ID
|
|
|
289
289
|
| `config completion [bash\|zsh\|fish] [--install]` | 启用 `<Tab>` 补全 |
|
|
290
290
|
| `config language [LANG]` | 切换显示语言(`en` / `zh-Hans`;`auto` 跟随登录账号的语言) |
|
|
291
291
|
| `config update [auto\|notify\|off]` | 自动升级开关;`notify` 只提示不安装,`off` 不检查 |
|
|
292
|
+
| `config auto-login [on\|off]` | 登录过期时自动打开浏览器授权页,点一下「授权」命令继续执行;`off` 则直接报未登录 |
|
|
292
293
|
| `config log on / off / status` | 日志开关与状态 |
|
|
293
294
|
| `config log level LEVEL` | 设置日志级别(`DEBUG/INFO/WARNING/ERROR/CRITICAL`) |
|
|
294
295
|
| `config log view [--lines N] [--no-follow]` | 跟踪日志文件 |
|
|
@@ -126,6 +126,7 @@ def config_completion(shell, install):
|
|
|
126
126
|
@pass_context
|
|
127
127
|
def config_show(ctx):
|
|
128
128
|
"""Display current harness configuration."""
|
|
129
|
+
from hap_cli.core.relogin import get_mode as get_auto_login_mode
|
|
129
130
|
from hap_cli.core.update import get_update_mode
|
|
130
131
|
|
|
131
132
|
log_cfg = load_logging_config()
|
|
@@ -136,6 +137,7 @@ def config_show(ctx):
|
|
|
136
137
|
"file": str(resolve_log_path(log_cfg)),
|
|
137
138
|
},
|
|
138
139
|
"update": get_update_mode(),
|
|
140
|
+
"auto_login": get_auto_login_mode(),
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
def _human(d):
|
|
@@ -144,6 +146,7 @@ def config_show(ctx):
|
|
|
144
146
|
click.echo(f" level : {d['logging']['level']}")
|
|
145
147
|
click.echo(f" file : {d['logging']['file']}")
|
|
146
148
|
click.echo(f"update : {d['update']}")
|
|
149
|
+
click.echo(f"auto-login: {d['auto_login']}")
|
|
147
150
|
|
|
148
151
|
ctx.output(data, _human)
|
|
149
152
|
|
|
@@ -253,6 +256,45 @@ def config_update(ctx, mode):
|
|
|
253
256
|
click.echo(f"Self-update set to: {mode}")
|
|
254
257
|
|
|
255
258
|
|
|
259
|
+
# ── `hap config auto-login` ───────────────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@config.command("auto-login")
|
|
263
|
+
@click.argument("mode", required=False, type=click.Choice(["on", "off"]))
|
|
264
|
+
@pass_context
|
|
265
|
+
def config_auto_login(ctx, mode):
|
|
266
|
+
"""Control whether an expired login is renewed in the browser.
|
|
267
|
+
|
|
268
|
+
\b
|
|
269
|
+
on when a command finds your login expired, open the browser
|
|
270
|
+
authorization page, wait for you to click Authorize, then
|
|
271
|
+
carry on (default)
|
|
272
|
+
off stop with a "not logged in" error instead
|
|
273
|
+
|
|
274
|
+
Without an argument, shows the current setting. It never applies over
|
|
275
|
+
SSH, in CI, or where no browser can be shown. Setting the environment
|
|
276
|
+
variable HAP_AUTO_LOGIN=0 turns it off for one shell.
|
|
277
|
+
"""
|
|
278
|
+
from hap_cli.core.relogin import CONFIG_KEY, get_mode
|
|
279
|
+
from hap_cli.core.session import save_config
|
|
280
|
+
|
|
281
|
+
if mode is None:
|
|
282
|
+
current = get_mode()
|
|
283
|
+
if ctx.json_mode:
|
|
284
|
+
from hap_cli.utils.formatting import output_json
|
|
285
|
+
output_json({"auto_login": current})
|
|
286
|
+
else:
|
|
287
|
+
click.echo(f"Auto-login: {current}")
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
save_config({CONFIG_KEY: mode})
|
|
291
|
+
if ctx.json_mode:
|
|
292
|
+
from hap_cli.utils.formatting import output_json
|
|
293
|
+
output_json({"status": "ok", "auto_login": mode})
|
|
294
|
+
else:
|
|
295
|
+
click.echo(f"Auto-login set to: {mode}")
|
|
296
|
+
|
|
297
|
+
|
|
256
298
|
# ── `hap config language` ─────────────────────────────────────────────────
|
|
257
299
|
|
|
258
300
|
|
|
@@ -18,37 +18,71 @@ def group():
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _output_group_list(data):
|
|
21
|
-
rows = [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
]
|
|
21
|
+
rows = (data or {}).get("list", [])
|
|
22
|
+
with_feed = any("isPost" in row for row in rows)
|
|
23
|
+
columns = ["groupId", "name", "groupMemberCount"]
|
|
24
|
+
headers = ["Group ID", "Name", "Members"]
|
|
25
|
+
if with_feed:
|
|
26
|
+
columns.append("isPost")
|
|
27
|
+
headers.append("Feed")
|
|
29
28
|
output_table(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
[
|
|
30
|
+
{
|
|
31
|
+
"groupId": row.get("groupId", ""),
|
|
32
|
+
"name": row.get("name") or row.get("groupName", ""),
|
|
33
|
+
"groupMemberCount": row.get("groupMemberCount", ""),
|
|
34
|
+
"isPost": "yes" if row.get("isPost") else "no",
|
|
35
|
+
}
|
|
36
|
+
for row in rows
|
|
37
|
+
],
|
|
38
|
+
columns,
|
|
39
|
+
headers,
|
|
33
40
|
)
|
|
41
|
+
if data.get("has_more"):
|
|
42
|
+
click.echo(
|
|
43
|
+
f"Showing {len(rows)} of {data.get('allCount')} groups. Next page: "
|
|
44
|
+
f"-p {data.get('next_page')}, or --all for every group.",
|
|
45
|
+
err=True,
|
|
46
|
+
)
|
|
34
47
|
|
|
35
48
|
|
|
36
49
|
@group.command("list")
|
|
37
50
|
@org_id_option()
|
|
38
51
|
@click.option("--keywords", "-k", default="", help="Filter groups by name")
|
|
39
52
|
@pagination_options
|
|
53
|
+
@click.option("--all", "all_pages", is_flag=True,
|
|
54
|
+
help="Return every group instead of one page.")
|
|
55
|
+
@click.option("--feed-only", is_flag=True,
|
|
56
|
+
help="Only groups with the feed on, which are the ones a post "
|
|
57
|
+
"can be published to. Checks every group, so it is slower.")
|
|
40
58
|
@pass_context
|
|
41
|
-
def group_list(ctx, org_id, keywords, page_size, page):
|
|
42
|
-
"""List open chat groups you have joined.
|
|
59
|
+
def group_list(ctx, org_id, keywords, page_size, page, all_pages, feed_only):
|
|
60
|
+
"""List open chat groups you have joined.
|
|
61
|
+
|
|
62
|
+
One page at a time by default; when more exist the output says so
|
|
63
|
+
(has_more / next_page) — use --all to get every group at once.
|
|
64
|
+
"""
|
|
43
65
|
try:
|
|
44
66
|
session = ctx.get_session()
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
project_id = require_api_project_id(session, org_id)
|
|
68
|
+
if all_pages or feed_only:
|
|
69
|
+
data = group_mod.get_all_groups(session, project_id, keywords=keywords)
|
|
70
|
+
else:
|
|
71
|
+
data = group_mod.get_groups(
|
|
72
|
+
session,
|
|
73
|
+
project_id=project_id,
|
|
74
|
+
keywords=keywords,
|
|
75
|
+
page_index=page,
|
|
76
|
+
page_size=page_size,
|
|
77
|
+
) or {}
|
|
78
|
+
total = data.get("allCount")
|
|
79
|
+
shown = (page - 1) * page_size + len(data.get("list") or [])
|
|
80
|
+
data["has_more"] = isinstance(total, int) and shown < total
|
|
81
|
+
if data["has_more"]:
|
|
82
|
+
data["next_page"] = page + 1
|
|
83
|
+
if feed_only:
|
|
84
|
+
rows = group_mod.mark_feed_groups(session, data.get("list") or [])
|
|
85
|
+
data["list"] = [row for row in rows if row.get("isPost")]
|
|
52
86
|
ctx.output(data, _output_group_list)
|
|
53
87
|
except Exception as e:
|
|
54
88
|
ctx.handle_error(e)
|
|
@@ -683,6 +683,50 @@ def post_get(ctx, post_id):
|
|
|
683
683
|
ctx.handle_error(e)
|
|
684
684
|
|
|
685
685
|
|
|
686
|
+
def _upload_progress(event: str, info: dict[str, Any]) -> None:
|
|
687
|
+
"""One line per file on stderr, so --json output stays parseable."""
|
|
688
|
+
size = info.get("size")
|
|
689
|
+
size_text = f" ({size / 1048576:.1f} MB)" if size else ""
|
|
690
|
+
where = f"[{info['index']}/{info['total']}]"
|
|
691
|
+
if event == "start":
|
|
692
|
+
click.echo(f"Uploading {where} {info['name']}{size_text} ...", err=True)
|
|
693
|
+
elif event == "done":
|
|
694
|
+
click.echo(f"Uploaded {where} {info['name']}{size_text} in "
|
|
695
|
+
f"{info.get('seconds', 0):.1f}s", err=True)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def _preview_mentions(ctx, org_id: str, message: str) -> list[dict[str, Any]]:
|
|
699
|
+
"""Who a --dry-run message would mention, looked up by account id.
|
|
700
|
+
|
|
701
|
+
Only signs in when the message actually mentions someone, so a plain
|
|
702
|
+
dry run stays offline."""
|
|
703
|
+
ids = post_mod.extract_mentions(message)
|
|
704
|
+
if not ids:
|
|
705
|
+
return []
|
|
706
|
+
session = ctx.get_session()
|
|
707
|
+
names = post_mod.resolve_accounts(
|
|
708
|
+
session, require_api_project_id(session, org_id), ids)
|
|
709
|
+
return [{"accountId": i, "name": names.get(i), "found": i in names}
|
|
710
|
+
for i in ids]
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def _render_dry_run(d: dict[str, Any]) -> None:
|
|
714
|
+
line = "Would publish to: " + ", ".join(d["audience"])
|
|
715
|
+
if d["cardToGroups"]:
|
|
716
|
+
line += f"; card to groups: {', '.join(d['cardToGroups'])}"
|
|
717
|
+
if d["files"]:
|
|
718
|
+
line += f"; files: {', '.join(d['files'])}"
|
|
719
|
+
click.echo(line)
|
|
720
|
+
if d["categories"]:
|
|
721
|
+
click.echo("Categories: " + ", ".join(d["categories"]))
|
|
722
|
+
for m in d["mentions"]:
|
|
723
|
+
if m["found"]:
|
|
724
|
+
click.echo(f"Mentions: {m['name']} ({m['accountId']})")
|
|
725
|
+
else:
|
|
726
|
+
click.echo(f"Warning: no one in this organization has account id "
|
|
727
|
+
f"{m['accountId']}; that mention will not notify anyone.")
|
|
728
|
+
|
|
729
|
+
|
|
686
730
|
@post.command("create")
|
|
687
731
|
@org_id_option()
|
|
688
732
|
@click.option("--message", "-m", required=True, help="Post content")
|
|
@@ -732,6 +776,12 @@ def post_create(ctx, org_id, message, post_type, files, share_group_ids,
|
|
|
732
776
|
--org-id when neither is given. Who can see a post is covered in
|
|
733
777
|
`hap guide post audience`; the group-chat card is `hap guide post card`.
|
|
734
778
|
Files given with --file are uploaded and attached to the post.
|
|
779
|
+
|
|
780
|
+
In the message, #name# files the post under the category (topic) of
|
|
781
|
+
that name, and [aid]<accountId>[/aid] mentions a person — get the id
|
|
782
|
+
with `hap contact resolve <name>`. --dry-run lists both, with the
|
|
783
|
+
mentioned names, before anything is published. Details in
|
|
784
|
+
`hap guide post tags`.
|
|
735
785
|
"""
|
|
736
786
|
try:
|
|
737
787
|
if post_type == post_mod.POST_TYPE_ATTACHMENT and not files:
|
|
@@ -744,17 +794,13 @@ def post_create(ctx, org_id, message, post_type, files, share_group_ids,
|
|
|
744
794
|
[f"group:{g}" for g in share_group_ids]
|
|
745
795
|
+ [f"org:{o}" for o in share_org_ids]
|
|
746
796
|
) or [f"org:{org_id or '<current>'} (everyone, default)"]
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
if d["cardToGroups"] else "")
|
|
755
|
-
+ (f"; files: {', '.join(d['files'])}"
|
|
756
|
-
if d["files"] else "")),
|
|
757
|
-
)
|
|
797
|
+
preview = {"audience": audience,
|
|
798
|
+
"cardToGroups": list(card_group_ids),
|
|
799
|
+
"message": message, "files": list(files),
|
|
800
|
+
"categories": post_mod.extract_topics(message),
|
|
801
|
+
"mentions": _preview_mentions(ctx, org_id, message),
|
|
802
|
+
"published": False}
|
|
803
|
+
ctx.output(preview, _render_dry_run)
|
|
758
804
|
return
|
|
759
805
|
session = ctx.get_session()
|
|
760
806
|
# Only a group with the feed turned on can be a post audience. The
|
|
@@ -778,8 +824,9 @@ def post_create(ctx, org_id, message, post_type, files, share_group_ids,
|
|
|
778
824
|
+ ". Pick a group whose feed is on, or send a card to the "
|
|
779
825
|
"chat instead with --card-to-group.")
|
|
780
826
|
pid = require_api_project_id(session, org_id)
|
|
781
|
-
attachments = (post_mod.upload_post_files(
|
|
782
|
-
|
|
827
|
+
attachments = (post_mod.upload_post_files(
|
|
828
|
+
session, list(files), progress=_upload_progress)
|
|
829
|
+
if files else None)
|
|
783
830
|
raw = post_mod.add_post(
|
|
784
831
|
session,
|
|
785
832
|
project_id=pid,
|
|
@@ -871,8 +918,9 @@ def post_comment(ctx, post_id, message, reply_id, reply_account_id, files):
|
|
|
871
918
|
"""Add a comment to a post, optionally with files attached."""
|
|
872
919
|
try:
|
|
873
920
|
session = ctx.get_session()
|
|
874
|
-
attachments = (post_mod.upload_post_files(
|
|
875
|
-
|
|
921
|
+
attachments = (post_mod.upload_post_files(
|
|
922
|
+
session, list(files), progress=_upload_progress)
|
|
923
|
+
if files else None)
|
|
876
924
|
raw = post_mod.add_comment(
|
|
877
925
|
session, post_id, message,
|
|
878
926
|
reply_id=reply_id, reply_account_id=reply_account_id,
|
|
@@ -20,6 +20,27 @@ NOT_LOGGED_IN_HINT = (
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _relogin_notice(login_url: str, timeout: int) -> None:
|
|
24
|
+
"""Tell the user an authorization page is waiting for one click.
|
|
25
|
+
|
|
26
|
+
Written to stderr so ``--json`` output on stdout stays parseable; the
|
|
27
|
+
desktop notification is for callers (AI agents) whose terminal output
|
|
28
|
+
the user only sees after the command ends.
|
|
29
|
+
"""
|
|
30
|
+
from hap_cli.core.relogin import desktop_notify
|
|
31
|
+
from hap_cli.i18n import t
|
|
32
|
+
|
|
33
|
+
click.echo(
|
|
34
|
+
t("Your HAP login has expired. A browser page is open — click "
|
|
35
|
+
"Authorize (授权) to continue (waiting up to {seconds}s).").format(
|
|
36
|
+
seconds=timeout),
|
|
37
|
+
err=True,
|
|
38
|
+
)
|
|
39
|
+
click.echo(f" {login_url}", err=True)
|
|
40
|
+
desktop_notify(t("Login expired — click Authorize (授权) in the browser "
|
|
41
|
+
"to let the command continue."))
|
|
42
|
+
|
|
43
|
+
|
|
23
44
|
class Context:
|
|
24
45
|
"""Shared CLI context."""
|
|
25
46
|
|
|
@@ -43,6 +64,7 @@ class Context:
|
|
|
43
64
|
"""
|
|
44
65
|
if self.session is None:
|
|
45
66
|
self.session = Session.load(self.profile)
|
|
67
|
+
self.session.relogin_notice = _relogin_notice
|
|
46
68
|
# One-time migration for sessions logged in before account_id
|
|
47
69
|
# was persisted. Some HAP controllers (notably
|
|
48
70
|
# Invitation/InviteUser) silently degrade when the AccountId
|
|
@@ -55,7 +77,10 @@ class Context:
|
|
|
55
77
|
):
|
|
56
78
|
self.session._lazy_resolve_account_id()
|
|
57
79
|
if require_auth and not self.session.auth_token:
|
|
58
|
-
|
|
80
|
+
# A saved environment without a token: authorize it again in
|
|
81
|
+
# the browser rather than stopping the command.
|
|
82
|
+
if not (self.session.login_url and self.session.recover_auth()):
|
|
83
|
+
raise SessionError("Not logged in. Run 'hap auth login' first.")
|
|
59
84
|
return self.session
|
|
60
85
|
|
|
61
86
|
def output(self, data, table_fn=None):
|
|
@@ -48,26 +48,43 @@ def _chatmq_get(
|
|
|
48
48
|
# mirror the other transport hooks.
|
|
49
49
|
from hap_cli.core import cli_access
|
|
50
50
|
|
|
51
|
+
from hap_cli.core.session import AuthenticationError
|
|
52
|
+
|
|
51
53
|
cli_access.ensure_before_request(session, url)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
54
|
+
|
|
55
|
+
def _send():
|
|
56
|
+
headers = {
|
|
57
|
+
"Authorization": f"md_pss_id {session.auth_token}",
|
|
58
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
59
|
+
"User-Agent": hap_user_agent(),
|
|
60
|
+
}
|
|
61
|
+
auth_for_log = redact_authorization(headers["Authorization"])
|
|
62
|
+
logger.info(
|
|
63
|
+
"HAP GET %s params=%s Authorization=%s",
|
|
64
|
+
url,
|
|
65
|
+
format_body_for_log(params) or "{}",
|
|
66
|
+
auth_for_log,
|
|
67
|
+
)
|
|
68
|
+
response = requests.get(url, params=params, headers=headers, timeout=timeout)
|
|
69
|
+
logger.info(
|
|
70
|
+
"HAP GET %s -> %d %s",
|
|
71
|
+
url,
|
|
72
|
+
response.status_code,
|
|
73
|
+
format_response_for_log(getattr(response, "text", "") or ""),
|
|
74
|
+
)
|
|
75
|
+
return response
|
|
76
|
+
|
|
77
|
+
response = _send()
|
|
78
|
+
# An expired login gets the same one-shot browser re-authorization as
|
|
79
|
+
# the main transport (Session._post); the read is then resent once.
|
|
80
|
+
if response.status_code == 401:
|
|
81
|
+
if not session.recover_auth():
|
|
82
|
+
raise AuthenticationError(
|
|
83
|
+
"Authentication failed (HTTP 401). "
|
|
84
|
+
"Your session token is missing, invalid, or expired.",
|
|
85
|
+
status_code=401,
|
|
86
|
+
)
|
|
87
|
+
response = _send()
|
|
71
88
|
response.raise_for_status()
|
|
72
89
|
return response.json()
|
|
73
90
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Group management for Mingdao HAP."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import logging
|
|
4
5
|
from typing import Any, Optional
|
|
5
6
|
|
|
6
7
|
from hap_cli.core.session import Session
|
|
7
8
|
|
|
9
|
+
logger = logging.getLogger("hap_cli.group")
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
def get_groups(
|
|
10
13
|
session: Session,
|
|
@@ -36,6 +39,46 @@ def get_groups(
|
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
|
|
42
|
+
def get_all_groups(
|
|
43
|
+
session: Session,
|
|
44
|
+
project_id: str,
|
|
45
|
+
keywords: str = "",
|
|
46
|
+
page_size: int = 100,
|
|
47
|
+
) -> dict[str, Any]:
|
|
48
|
+
"""Every page of :func:`get_groups`, as one ``{list, allCount}``.
|
|
49
|
+
|
|
50
|
+
Stops on a short page or once ``allCount`` rows are in, whichever
|
|
51
|
+
comes first, so a server that miscounts cannot loop forever.
|
|
52
|
+
"""
|
|
53
|
+
rows: list[dict[str, Any]] = []
|
|
54
|
+
total = None
|
|
55
|
+
page = 1
|
|
56
|
+
while True:
|
|
57
|
+
data = get_groups(session, project_id, keywords=keywords,
|
|
58
|
+
page_index=page, page_size=page_size) or {}
|
|
59
|
+
batch = data.get("list") or []
|
|
60
|
+
rows.extend(batch)
|
|
61
|
+
if total is None:
|
|
62
|
+
total = data.get("allCount")
|
|
63
|
+
if len(batch) < page_size or (isinstance(total, int) and len(rows) >= total):
|
|
64
|
+
break
|
|
65
|
+
page += 1
|
|
66
|
+
logger.info("group: fetched %d group(s) over %d page(s)", len(rows), page)
|
|
67
|
+
return {"list": rows, "allCount": total if total is not None else len(rows)}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def mark_feed_groups(session: Session, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
71
|
+
"""Add ``isPost`` (feed on, so the group can be a post audience) to
|
|
72
|
+
each row. ``GetGroups`` rows do not carry it — only ``GetGroupInfo``
|
|
73
|
+
does — so this costs one request per group."""
|
|
74
|
+
for row in rows:
|
|
75
|
+
info = get_group_info(session, row.get("groupId", "")) or {}
|
|
76
|
+
if isinstance(info.get("data"), dict):
|
|
77
|
+
info = info["data"]
|
|
78
|
+
row["isPost"] = bool(info.get("isPost"))
|
|
79
|
+
return rows
|
|
80
|
+
|
|
81
|
+
|
|
39
82
|
def get_group_info(
|
|
40
83
|
session: Session,
|
|
41
84
|
group_id: str,
|
|
@@ -26,7 +26,8 @@ Response envelopes (from a live probe on SaaS):
|
|
|
26
26
|
|
|
27
27
|
import json
|
|
28
28
|
import logging
|
|
29
|
-
|
|
29
|
+
import re
|
|
30
|
+
from typing import Any, Callable, Optional
|
|
30
31
|
|
|
31
32
|
from hap_cli.core.session import APIError, Session
|
|
32
33
|
|
|
@@ -238,22 +239,78 @@ def _attachment_fields(attachments: list[dict[str, Any]]) -> dict[str, str]:
|
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
|
|
241
|
-
def upload_post_files(
|
|
242
|
+
def upload_post_files(
|
|
243
|
+
session: Session,
|
|
244
|
+
sources: list[str],
|
|
245
|
+
progress: Optional[Callable[[str, dict[str, Any]], None]] = None,
|
|
246
|
+
) -> list[dict[str, Any]]:
|
|
242
247
|
"""Upload local paths / http(s) addresses and return attachment items
|
|
243
248
|
for :func:`add_post` / :func:`add_comment`.
|
|
244
249
|
|
|
245
250
|
No organization is named for the upload credential: the feed mounts
|
|
246
251
|
its uploader without a projectId (MDPublic createFeed/
|
|
247
252
|
CreateFeedContent.jsx:124-136), same as chat does here.
|
|
253
|
+
``progress`` is handed to ``upload.upload_files`` unchanged.
|
|
248
254
|
"""
|
|
249
255
|
from hap_cli.core import upload as upload_mod
|
|
250
256
|
|
|
251
257
|
descriptors = upload_mod.upload_files(
|
|
252
|
-
session, upload_mod.source_descriptors(list(sources))
|
|
258
|
+
session, upload_mod.source_descriptors(list(sources)),
|
|
259
|
+
progress=progress)
|
|
253
260
|
logger.info("post: uploaded %d attachment(s)", len(descriptors))
|
|
254
261
|
return upload_mod.attachment_items(descriptors)
|
|
255
262
|
|
|
256
263
|
|
|
264
|
+
# What the feed composer writes into a post body (MDPublic
|
|
265
|
+
# src/components/MentionsInput/index.jsx). The post composer opens it
|
|
266
|
+
# without a sourceType, so a person is `[aid]<accountId>[/aid]`
|
|
267
|
+
# (mentionItemSyntax, :75-76) and a topic is `#name#`, half- or full-width
|
|
268
|
+
# `#` (categoryLetterArr, :19; :308). The server files the post under the
|
|
269
|
+
# category of that name, creating it when new, and reads it back as
|
|
270
|
+
# `categories` — there is no category field on AddPost itself.
|
|
271
|
+
_TOPIC_RE = re.compile(r"[##]([^##\n]+?)[##]")
|
|
272
|
+
_MENTION_RE = re.compile(r"\[aid\]([^\[\]]+?)\[/aid\]")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def extract_topics(message: str) -> list[str]:
|
|
276
|
+
"""Topic names written as ``#name#`` in a post body, in order, unique."""
|
|
277
|
+
seen: list[str] = []
|
|
278
|
+
for name in _TOPIC_RE.findall(message or ""):
|
|
279
|
+
name = name.strip()
|
|
280
|
+
if name and name not in seen:
|
|
281
|
+
seen.append(name)
|
|
282
|
+
return seen
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def extract_mentions(message: str) -> list[str]:
|
|
286
|
+
"""Account ids written as ``[aid]<id>[/aid]`` in a post body, unique."""
|
|
287
|
+
seen: list[str] = []
|
|
288
|
+
for account_id in _MENTION_RE.findall(message or ""):
|
|
289
|
+
account_id = account_id.strip()
|
|
290
|
+
if account_id and account_id not in seen:
|
|
291
|
+
seen.append(account_id)
|
|
292
|
+
return seen
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def resolve_accounts(
|
|
296
|
+
session: Session, project_id: str, account_ids: list[str],
|
|
297
|
+
) -> dict[str, str]:
|
|
298
|
+
"""Map account ids to display names via ``User.GetUserListByAccountId``
|
|
299
|
+
(MDPublic src/api/user.js:12). ``projectId`` is mandatory — the server
|
|
300
|
+
answers "组织编号不能为空" without it. Ids it does not know are simply
|
|
301
|
+
missing from the result."""
|
|
302
|
+
if not account_ids:
|
|
303
|
+
return {}
|
|
304
|
+
raw = session.api_call("User", "GetUserListByAccountId", {
|
|
305
|
+
"projectId": project_id, "accountIds": list(account_ids)})
|
|
306
|
+
rows = raw.get("list") if isinstance(raw, dict) else raw
|
|
307
|
+
names: dict[str, str] = {}
|
|
308
|
+
for row in rows or []:
|
|
309
|
+
if isinstance(row, dict) and row.get("accountId"):
|
|
310
|
+
names[row["accountId"]] = str(row.get("fullname") or "")
|
|
311
|
+
return names
|
|
312
|
+
|
|
313
|
+
|
|
257
314
|
def add_post(
|
|
258
315
|
session: Session,
|
|
259
316
|
project_id: str,
|