hap-cli 0.9.6__tar.gz → 0.9.8__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.6 → hap_cli-0.9.8}/PKG-INFO +2 -2
- {hap_cli-0.9.6/hap_cli → hap_cli-0.9.8}/README.md +1 -1
- {hap_cli-0.9.6 → hap_cli-0.9.8/hap_cli}/README.md +1 -1
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/README_CN.md +1 -1
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/__init__.py +1 -1
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/group_cmd.py +54 -20
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/post_cmd.py +69 -16
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/group.py +43 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/post.py +85 -6
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/upload.py +33 -3
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/post.md +20 -5
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/index.json +2 -2
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/post.md +16 -4
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/locale/messages.json +36 -4
- hap_cli-0.9.8/hap_cli/tests/test_post_group_friction.py +205 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli.egg-info/PKG-INFO +2 -2
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli.egg-info/SOURCES.txt +1 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/MANIFEST.in +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/config.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/fixtures/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/fixtures/feedback_scenario.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/harness.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/smoke.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/app_live_tests/state.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/__main__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/_entry.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/app_creator_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/app_editor_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/attachment_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/auth_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/chart_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/config_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/guide_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/instance_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/role_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/update_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/commands/worksheet_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/context.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/action_spec_adapter.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/charts.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/cleanup.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/compiler.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/config.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/design/design.schema.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/errors.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/executor.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/fields.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/hap.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/merge.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/recording/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/recording/console.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/recording/mirror.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/recording/report.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/schema.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/executor.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/seed/template.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/selftest.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/smoke.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/steps.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/store.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/validate.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/attachment.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/auth.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/calendar_mod.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/chart.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/chat.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/chatbot.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/cli_access.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/condition_dsl.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/contact.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/department.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/apply.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/componentlower.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/editspec/component.schema.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/editspec/field.schema.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/editspec.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/errors.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/fieldlower.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/jsonschema_mini.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/models.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/ops.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/planner.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/editor/reader.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/filter_v3.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/flow_node.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/guide.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/instance.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/layout_check.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/optionset.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/page.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/record.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/record_defaults.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/record_form_checks.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/record_lookups.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/record_rules.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/relation_scope.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/relogin.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/role.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/rule_spec_adapter.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/session.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/update.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/workflow.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/workflow_node_dsl.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/worksheet.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/worksheet_form.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/worksheet_reference.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/approval.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/chart.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/messages.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/overview.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/record.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/setup.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/skills.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/workflow.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/en/worksheet.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/approval.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/chart.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/messages.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/overview.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/record.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/setup.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/skills.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/workflow.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/i18n.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/conftest.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_app_creator.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_app_editor.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_app_export_usage.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_app_light_color.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_app_section_name.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_approval_wire.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_atomic_write.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_attachment_share.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_batch_add_atomic.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_bug_036430.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_bug_036750.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_calendar_date_range.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_chart_filter_merge.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_chat_attachment_send.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_chat_messages_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_chat_recall_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_chat_send_socket.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_cli_access.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_coded_responses.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_condition_dsl.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_content_language.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_control_lookup_guard.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_core.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_create_worksheet_partial.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_engine_core_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_bidirectional.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_entry_module.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_http_error_body.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_layout_check.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_node_summary.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_post_cursor.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_subgroups.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_subtable_read.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_full_e2e.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g1_chart_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g1_record_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g1_upload_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g4_approval_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g8_filter_operator.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_global_meta.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_guide.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_guide_sections.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_instance_envelope.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_instance_history_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_approval.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_approval_actions.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_destructive.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_multi_profile.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_page_filters_group_read.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_page_filters_group_update.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_page_save_page_id.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_post_attachments.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_post_scope_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_business_rules.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_defaults.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_field_alias.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_form_checks.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_run_action.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_record_write_fixes.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_relation_scope.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_relogin.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_role_app_scope.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_save_rule_spec.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_select_add_option.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_semantic_types_registry.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_update.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_update_mode.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_upload_allow_down.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_upload_host_resolution.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_v3_bool_flags.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_v3_response_format_json.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_config_keys.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_inner_scope.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_structure_listing.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_wo4_result_handling.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_code_node.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_list_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_node_wire.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_worksheet_create_section.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_worksheet_switch.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/utils/atomic_write.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/utils/confirm.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/__main__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/_wftest.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/charts.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/cleanup.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/compiler.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/config.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/errors.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/executor.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/fields.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/hap.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/recording/console.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/recording/report.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/schema.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/seed/template.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/selftest.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/smoke.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/step.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/steps.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/store.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/setup.cfg +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/setup.py +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.9.6 → hap_cli-0.9.8}/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.8
|
|
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`) |
|
|
@@ -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`) |
|
|
@@ -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`) |
|
|
@@ -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
|
|
|
@@ -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 user:<accountId> followed by a space mentions and
|
|
782
|
+
notifies a person — get the id 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,14 @@ 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
|
-
|
|
755
|
-
+ (f"; files: {', '.join(d['files'])}"
|
|
756
|
-
if d["files"] else "")),
|
|
757
|
-
)
|
|
797
|
+
preview = {"audience": audience,
|
|
798
|
+
"cardToGroups": list(card_group_ids),
|
|
799
|
+
"message": post_mod.mention_syntax(message),
|
|
800
|
+
"files": list(files),
|
|
801
|
+
"categories": post_mod.extract_topics(message),
|
|
802
|
+
"mentions": _preview_mentions(ctx, org_id, message),
|
|
803
|
+
"published": False}
|
|
804
|
+
ctx.output(preview, _render_dry_run)
|
|
758
805
|
return
|
|
759
806
|
session = ctx.get_session()
|
|
760
807
|
# Only a group with the feed turned on can be a post audience. The
|
|
@@ -778,8 +825,9 @@ def post_create(ctx, org_id, message, post_type, files, share_group_ids,
|
|
|
778
825
|
+ ". Pick a group whose feed is on, or send a card to the "
|
|
779
826
|
"chat instead with --card-to-group.")
|
|
780
827
|
pid = require_api_project_id(session, org_id)
|
|
781
|
-
attachments = (post_mod.upload_post_files(
|
|
782
|
-
|
|
828
|
+
attachments = (post_mod.upload_post_files(
|
|
829
|
+
session, list(files), progress=_upload_progress)
|
|
830
|
+
if files else None)
|
|
783
831
|
raw = post_mod.add_post(
|
|
784
832
|
session,
|
|
785
833
|
project_id=pid,
|
|
@@ -868,11 +916,16 @@ def post_delete(ctx, post_id, yes):
|
|
|
868
916
|
)
|
|
869
917
|
@pass_context
|
|
870
918
|
def post_comment(ctx, post_id, message, reply_id, reply_account_id, files):
|
|
871
|
-
"""Add a comment to a post, optionally with files attached.
|
|
919
|
+
"""Add a comment to a post, optionally with files attached.
|
|
920
|
+
|
|
921
|
+
Mention someone with user:<accountId> followed by a space; they are
|
|
922
|
+
notified. Get the id with `hap contact resolve <name>`.
|
|
923
|
+
"""
|
|
872
924
|
try:
|
|
873
925
|
session = ctx.get_session()
|
|
874
|
-
attachments = (post_mod.upload_post_files(
|
|
875
|
-
|
|
926
|
+
attachments = (post_mod.upload_post_files(
|
|
927
|
+
session, list(files), progress=_upload_progress)
|
|
928
|
+
if files else None)
|
|
876
929
|
raw = post_mod.add_comment(
|
|
877
930
|
session, post_id, message,
|
|
878
931
|
reply_id=reply_id, reply_account_id=reply_account_id,
|
|
@@ -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,100 @@ 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). A topic is `#name#`, half- or
|
|
266
|
+
# full-width `#` (categoryLetterArr, :19; :308). The server files the post
|
|
267
|
+
# under the category of that name, creating it when new, and reads it back
|
|
268
|
+
# as `categories` — there is no category field on AddPost itself.
|
|
269
|
+
#
|
|
270
|
+
# A person is `user:<accountId>`: posts and post comments open the
|
|
271
|
+
# composer with sourceType POST, whose mentionItemSyntax is
|
|
272
|
+
# `<type>:<id>` (:75-78), and a captured AddPost / AddPostComment from the
|
|
273
|
+
# web feed (2026-10-12) carries exactly `user:<id> `. The server turns that
|
|
274
|
+
# into the stored `[aid]<id>[/aid]` marker *and* notifies the person.
|
|
275
|
+
# Sending `[aid]…[/aid]` directly renders as a mention but notifies nobody,
|
|
276
|
+
# so callers that learned the stored form are rewritten — the post editor
|
|
277
|
+
# does the same when it loads a post back (EditPostDialog.jsx:94).
|
|
278
|
+
_TOPIC_RE = re.compile(r"[##]([^##\n]+?)[##]")
|
|
279
|
+
_MENTION_RE = re.compile(r"(?<![0-9A-Za-z_])user:([0-9A-Za-z-]+)")
|
|
280
|
+
_STORED_MENTION_RE = re.compile(r"\[aid\]\s*([0-9A-Za-z-]+)\s*\[/aid\]")
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def mention_syntax(message: str) -> str:
|
|
284
|
+
"""Rewrite stored-form ``[aid]<id>[/aid]`` mentions to ``user:<id>``.
|
|
285
|
+
|
|
286
|
+
A space follows each rewritten mention unless whitespace already does,
|
|
287
|
+
matching the composer, which leaves one after the picked name."""
|
|
288
|
+
text = message or ""
|
|
289
|
+
|
|
290
|
+
def _sub(m: "re.Match[str]") -> str:
|
|
291
|
+
follow = text[m.end():m.end() + 1]
|
|
292
|
+
return f"user:{m.group(1)}" + ("" if not follow or follow.isspace() else " ")
|
|
293
|
+
|
|
294
|
+
return _STORED_MENTION_RE.sub(_sub, text)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def extract_topics(message: str) -> list[str]:
|
|
298
|
+
"""Topic names written as ``#name#`` in a post body, in order, unique."""
|
|
299
|
+
seen: list[str] = []
|
|
300
|
+
for name in _TOPIC_RE.findall(message or ""):
|
|
301
|
+
name = name.strip()
|
|
302
|
+
if name and name not in seen:
|
|
303
|
+
seen.append(name)
|
|
304
|
+
return seen
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def extract_mentions(message: str) -> list[str]:
|
|
308
|
+
"""Account ids mentioned in a post body (``user:<id>``, or the stored
|
|
309
|
+
``[aid]<id>[/aid]`` form), in order, unique."""
|
|
310
|
+
seen: list[str] = []
|
|
311
|
+
for account_id in _MENTION_RE.findall(mention_syntax(message)):
|
|
312
|
+
if account_id not in seen:
|
|
313
|
+
seen.append(account_id)
|
|
314
|
+
return seen
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def resolve_accounts(
|
|
318
|
+
session: Session, project_id: str, account_ids: list[str],
|
|
319
|
+
) -> dict[str, str]:
|
|
320
|
+
"""Map account ids to display names via ``User.GetUserListByAccountId``
|
|
321
|
+
(MDPublic src/api/user.js:12). ``projectId`` is mandatory — the server
|
|
322
|
+
answers "组织编号不能为空" without it. Ids it does not know are simply
|
|
323
|
+
missing from the result."""
|
|
324
|
+
if not account_ids:
|
|
325
|
+
return {}
|
|
326
|
+
raw = session.api_call("User", "GetUserListByAccountId", {
|
|
327
|
+
"projectId": project_id, "accountIds": list(account_ids)})
|
|
328
|
+
rows = raw.get("list") if isinstance(raw, dict) else raw
|
|
329
|
+
names: dict[str, str] = {}
|
|
330
|
+
for row in rows or []:
|
|
331
|
+
if isinstance(row, dict) and row.get("accountId"):
|
|
332
|
+
names[row["accountId"]] = str(row.get("fullname") or "")
|
|
333
|
+
return names
|
|
334
|
+
|
|
335
|
+
|
|
257
336
|
def add_post(
|
|
258
337
|
session: Session,
|
|
259
338
|
project_id: str,
|
|
@@ -306,7 +385,7 @@ def add_post(
|
|
|
306
385
|
data: dict[str, Any] = {
|
|
307
386
|
"projectId": project_id,
|
|
308
387
|
"postType": post_type,
|
|
309
|
-
"postMsg": message,
|
|
388
|
+
"postMsg": mention_syntax(message),
|
|
310
389
|
"scope": scope,
|
|
311
390
|
}
|
|
312
391
|
if attachments:
|
|
@@ -327,7 +406,7 @@ def edit_post(
|
|
|
327
406
|
doesn't have the previous text we omit it (the server accepts
|
|
328
407
|
either shape, but the frontend always sends the old text).
|
|
329
408
|
"""
|
|
330
|
-
data: dict[str, Any] = {"postId": post_id, "postMsg": message}
|
|
409
|
+
data: dict[str, Any] = {"postId": post_id, "postMsg": mention_syntax(message)}
|
|
331
410
|
if old_message:
|
|
332
411
|
data["oldPostMsg"] = old_message
|
|
333
412
|
return _require_post_success(
|
|
@@ -356,7 +435,7 @@ def add_comment(
|
|
|
356
435
|
string form as on a post (MDPublic src/pages/feed/components/post/
|
|
357
436
|
comment/postCommentInput.jsx:181-192; pd-openweb identical).
|
|
358
437
|
"""
|
|
359
|
-
data: dict[str, Any] = {"postID": post_id, "message": message}
|
|
438
|
+
data: dict[str, Any] = {"postID": post_id, "message": mention_syntax(message)}
|
|
360
439
|
if reply_id:
|
|
361
440
|
data["replyID"] = reply_id
|
|
362
441
|
if reply_account_id:
|
|
@@ -27,7 +27,7 @@ import random
|
|
|
27
27
|
import re
|
|
28
28
|
import time
|
|
29
29
|
from datetime import datetime, timezone
|
|
30
|
-
from typing import Any, Optional
|
|
30
|
+
from typing import Any, Callable, Optional
|
|
31
31
|
from urllib.parse import parse_qs, quote, urlparse
|
|
32
32
|
|
|
33
33
|
import requests
|
|
@@ -404,11 +404,30 @@ def upload_files(
|
|
|
404
404
|
project_id: str = "",
|
|
405
405
|
bucket: Optional[int] = None,
|
|
406
406
|
server_name: str = "",
|
|
407
|
+
progress: Optional[Callable[[str, dict[str, Any]], None]] = None,
|
|
407
408
|
) -> list[dict[str, Any]]:
|
|
408
409
|
"""Upload a list of ``{name?, url}`` / ``{name?, path}`` descriptors and
|
|
409
|
-
return one uploaded-file descriptor per file (order preserved).
|
|
410
|
+
return one uploaded-file descriptor per file (order preserved).
|
|
411
|
+
|
|
412
|
+
``progress(event, info)`` is called with ``"start"`` before each file
|
|
413
|
+
and ``"done"`` after it; ``info`` carries ``index`` (1-based),
|
|
414
|
+
``total``, ``name``, ``size`` (bytes, ``None`` for a URL not yet
|
|
415
|
+
fetched) and, on ``"done"``, ``seconds``. Large videos take a while
|
|
416
|
+
and the caller has nothing else to show in the meantime.
|
|
417
|
+
"""
|
|
410
418
|
out: list[dict[str, Any]] = []
|
|
411
|
-
|
|
419
|
+
total = len(files)
|
|
420
|
+
for index, f in enumerate(files, start=1):
|
|
421
|
+
label = f.get("name") or os.path.basename(f.get("path") or f.get("url") or "")
|
|
422
|
+
size = None
|
|
423
|
+
if f.get("path") and not f.get("url"):
|
|
424
|
+
try:
|
|
425
|
+
size = os.path.getsize(f["path"])
|
|
426
|
+
except OSError:
|
|
427
|
+
size = None
|
|
428
|
+
info = {"index": index, "total": total, "name": label, "size": size}
|
|
429
|
+
_report(progress, "start", info)
|
|
430
|
+
started = time.monotonic()
|
|
412
431
|
url = f.get("url")
|
|
413
432
|
path = f.get("path")
|
|
414
433
|
if url:
|
|
@@ -424,9 +443,20 @@ def upload_files(
|
|
|
424
443
|
session, content=content, name=name, worksheet_id=worksheet_id,
|
|
425
444
|
app_id=app_id, project_id=project_id, bucket=bucket,
|
|
426
445
|
server_name=server_name))
|
|
446
|
+
_report(progress, "done", {**info, "name": name, "size": len(content),
|
|
447
|
+
"seconds": time.monotonic() - started})
|
|
427
448
|
return out
|
|
428
449
|
|
|
429
450
|
|
|
451
|
+
def _report(progress, event: str, info: dict[str, Any]) -> None:
|
|
452
|
+
if progress is None:
|
|
453
|
+
return
|
|
454
|
+
try:
|
|
455
|
+
progress(event, info)
|
|
456
|
+
except Exception as exc:
|
|
457
|
+
logger.warning("Upload progress callback failed: %s", exc)
|
|
458
|
+
|
|
459
|
+
|
|
430
460
|
def attachment_items(descriptors: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
431
461
|
"""Uploaded-file descriptors as the attachment items the uploader hands
|
|
432
462
|
to whatever it is attached to — the ``formatResponseData`` shape
|
|
@@ -41,7 +41,8 @@ audience. Naming a `--to-group` does **not** additionally publish company-wide.
|
|
|
41
41
|
A plain chat group cannot receive a post. The server refuses with
|
|
42
42
|
"选择了没有加入的群组" (you picked a group you have not joined), which is
|
|
43
43
|
**misleading** — you are in the group; it simply has no feed. The CLI checks
|
|
44
|
-
first and names the group instead. To
|
|
44
|
+
first and names the group instead. To list every group that can receive a
|
|
45
|
+
post: `hap group list --feed-only`. To check one: `hap group info <groupId>`
|
|
45
46
|
and look at `isPost`. To merely drop a link into such a group, use
|
|
46
47
|
`--card-to-group` below.
|
|
47
48
|
|
|
@@ -90,12 +91,26 @@ remove it too, recall that message with
|
|
|
90
91
|
`hap chat recall <messageId> --group-id <groupId>` (your own messages within 5
|
|
91
92
|
minutes of sending; a group admin can recall a member's message with no limit).
|
|
92
93
|
|
|
93
|
-
## 3.
|
|
94
|
-
<!-- hook:
|
|
94
|
+
## 3. Categories and mentions in the body
|
|
95
|
+
<!-- hook: #name# in the body sets the post's category; user:<accountId> mentions and notifies a person; --dry-run previews both. -->
|
|
95
96
|
<!-- alias: tags -->
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
There is no category option: **the category comes from the body.** Writing
|
|
99
|
+
`#name#` (for example `#今日工作#`) files the post under the category of that
|
|
100
|
+
name, creating it if it is new. List existing ones with `hap post topics`.
|
|
101
|
+
|
|
102
|
+
To mention someone, write `user:<accountId>` followed by a space where the
|
|
103
|
+
`@name` should go — the person is notified. The same works in
|
|
104
|
+
`hap post comment`. Get the id from the person's name first:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
hap contact resolve 刘禹 # candidates[].id is the accountId
|
|
108
|
+
hap post create -m "#今日工作# 今天和 user:<accountId> 对齐了方案" --dry-run
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
A published post's audience cannot be changed, so check with `--dry-run`
|
|
112
|
+
first: it lists the categories and each mention with the person's name, and
|
|
113
|
+
warns about an id that matches nobody in the organization.
|
|
99
114
|
|
|
100
115
|
## 4. Other common commands
|
|
101
116
|
<!-- hook: Listing, searching, reading, commenting on and deleting posts, plus what post search --scope accepts. -->
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"zh-Hans": "发动态与发布范围"
|
|
52
52
|
},
|
|
53
53
|
"summary": {
|
|
54
|
-
"en": "Audience decides who can see a post, and you cannot read it back off an existing one.",
|
|
55
|
-
"zh-Hans": "
|
|
54
|
+
"en": "Audience decides who can see a post, and you cannot read it back off an existing one; #name# in the body sets the category, user:<accountId> notifies a person.",
|
|
55
|
+
"zh-Hans": "发布范围决定谁能看到,且无法从旧动态读出来;正文 #名称# 决定分类,user:<accountId> 提及并通知某人。"
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
{
|
|
@@ -37,6 +37,7 @@ hap post create -m "内容" --to-org fe288386-xxxx
|
|
|
37
37
|
|
|
38
38
|
纯聊天群不能接收动态。服务端对此的报错是「选择了没有加入的群组」,**具有误导性**
|
|
39
39
|
——你明明在群里,真实原因是这个群没开启动态功能。CLI 会先行检查并直接报出群名。
|
|
40
|
+
列出所有能发动态的群:`hap group list --feed-only`。
|
|
40
41
|
想查某个群是否可用:`hap group info <群组ID>`,看 `isPost` 是否为 true。
|
|
41
42
|
只是想在这种群里贴个链接,用下面的 `--card-to-group`。
|
|
42
43
|
|
|
@@ -78,12 +79,23 @@ hap post create -m "内容" --to-group 9ccde712-xxxx --card-to-group 7f694ec7-xx
|
|
|
78
79
|
指向一条已不存在的动态。要一并清掉,用 `hap chat recall <消息ID> --group-id <群组ID>`
|
|
79
80
|
撤回那条卡片消息(自己的消息限发送后 5 分钟内,群管理员撤回他人消息不限时)。
|
|
80
81
|
|
|
81
|
-
## 3.
|
|
82
|
-
<!-- hook:
|
|
82
|
+
## 3. 正文里的分类与 @提及
|
|
83
|
+
<!-- hook: 正文 #名称# 定分类,user:id 提及并通知某人,--dry-run 可预览 -->
|
|
83
84
|
<!-- alias: tags -->
|
|
84
85
|
|
|
85
|
-
正文里写
|
|
86
|
-
|
|
86
|
+
没有设置分类的选项:**分类由正文决定。** 正文里写 `#名称#`(例如 `#今日工作#`),
|
|
87
|
+
动态就会归到同名分类下,分类不存在时会自动新建。已有分类用 `hap post topics` 查。
|
|
88
|
+
|
|
89
|
+
要 @ 某人,在想出现「@姓名」的位置写 `user:<accountId>`,后面跟一个空格,对方会收到提醒。
|
|
90
|
+
`hap post comment` 里写法相同。先用姓名查出 id:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
hap contact resolve 刘禹 # candidates[].id 就是 accountId
|
|
94
|
+
hap post create -m "#今日工作# 今天和 user:<accountId> 对齐了方案" --dry-run
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
动态发出后范围无法再改,所以先用 `--dry-run` 检查:它会列出分类,以及每个
|
|
98
|
+
提及对应的姓名;某个 id 在组织里找不到人时会给出警告。
|
|
87
99
|
|
|
88
100
|
## 4. 其它常用
|
|
89
101
|
<!-- hook: 动态的列表、搜索、读取、评论、删除,以及 search --scope 接受哪些值 -->
|