hap-cli 0.9.4__tar.gz → 0.9.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hap_cli-0.9.4 → hap_cli-0.9.5}/PKG-INFO +1 -1
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/__init__.py +1 -1
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/post_cmd.py +57 -7
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/post.py +58 -10
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/upload.py +21 -10
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/post.md +2 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/post.md +2 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/locale/messages.json +14 -6
- hap_cli-0.9.5/hap_cli/tests/test_post_attachments.py +182 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/PKG-INFO +1 -1
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/SOURCES.txt +1 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/MANIFEST.in +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/README.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/config.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/fixtures/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/fixtures/feedback_scenario.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/harness.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/smoke.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/state.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/README.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/README_CN.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/__main__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/_entry.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/app_creator_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/app_editor_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/attachment_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/auth_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/chart_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/config_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/group_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/guide_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/instance_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/role_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/update_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/worksheet_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/context.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/action_spec_adapter.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/charts.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/cleanup.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/compiler.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/config.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/design/design.schema.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/errors.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/executor.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/fields.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/hap.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/merge.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/console.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/mirror.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/report.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/schema.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/executor.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/template.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/selftest.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/smoke.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/steps.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/store.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/validate.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/attachment.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/auth.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/calendar_mod.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/chart.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/chat.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/chatbot.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/cli_access.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/condition_dsl.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/contact.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/department.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/apply.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/componentlower.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/component.schema.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/field.schema.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/errors.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/fieldlower.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/jsonschema_mini.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/models.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/ops.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/planner.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/reader.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/filter_v3.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/flow_node.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/group.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/guide.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/instance.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/layout_check.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/optionset.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/page.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_defaults.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_form_checks.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_lookups.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_rules.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/relation_scope.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/role.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/rule_spec_adapter.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/session.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/update.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/workflow.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/workflow_node_dsl.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet_form.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet_reference.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/approval.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/chart.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/messages.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/overview.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/record.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/setup.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/skills.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/workflow.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/worksheet.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/index.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/approval.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/chart.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/messages.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/overview.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/record.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/setup.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/skills.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/workflow.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/i18n.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/conftest.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_creator.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_editor.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_export_usage.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_light_color.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_section_name.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_approval_wire.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_atomic_write.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_attachment_share.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_batch_add_atomic.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_bug_036430.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_bug_036750.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_calendar_date_range.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chart_filter_merge.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_attachment_send.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_messages_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_recall_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_send_socket.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_cli_access.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_coded_responses.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_condition_dsl.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_content_language.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_control_lookup_guard.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_core.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_create_worksheet_partial.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_engine_core_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_bidirectional.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_entry_module.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_http_error_body.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_layout_check.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_node_summary.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_post_cursor.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_subgroups.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_subtable_read.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_full_e2e.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_chart_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_record_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_upload_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g4_approval_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g8_filter_operator.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_global_meta.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_guide.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_guide_sections.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_instance_envelope.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_instance_history_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_approval.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_approval_actions.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_destructive.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_multi_profile.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_page_filters_group_read.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_page_filters_group_update.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_page_save_page_id.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_post_scope_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_business_rules.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_defaults.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_field_alias.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_form_checks.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_run_action.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_write_fixes.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_relation_scope.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_role_app_scope.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_save_rule_spec.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_select_add_option.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_semantic_types_registry.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_update.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_update_mode.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_upload_allow_down.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_upload_host_resolution.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_bool_flags.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_response_format_json.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_config_keys.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_inner_scope.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_structure_listing.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wo4_result_handling.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_code_node.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_list_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_node_wire.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_create_section.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_switch.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/atomic_write.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/confirm.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/__main__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/_wftest.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/charts.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/cleanup.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/compiler.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/config.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/errors.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/executor.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/fields.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/hap.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/console.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/report.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/schema.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/template.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/selftest.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/smoke.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/step.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/steps.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/store.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/setup.cfg +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/setup.py +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/update_optionset.yaml +0 -0
|
@@ -131,6 +131,19 @@ def _resolve_markers(
|
|
|
131
131
|
return out
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
def _simplify_attachments(items: Any) -> list[dict[str, Any]]:
|
|
135
|
+
out = []
|
|
136
|
+
for a in items or []:
|
|
137
|
+
if not isinstance(a, dict):
|
|
138
|
+
continue
|
|
139
|
+
out.append({
|
|
140
|
+
"id": a.get("fileID"),
|
|
141
|
+
"name": f"{a.get('originalFilename') or ''}{a.get('ext') or ''}",
|
|
142
|
+
"size": a.get("filesize"),
|
|
143
|
+
})
|
|
144
|
+
return out
|
|
145
|
+
|
|
146
|
+
|
|
134
147
|
def _simplify_comment(c: dict[str, Any]) -> dict[str, Any]:
|
|
135
148
|
text = _resolve_markers(
|
|
136
149
|
c.get("message") or "",
|
|
@@ -152,6 +165,9 @@ def _simplify_comment(c: dict[str, Any]) -> dict[str, Any]:
|
|
|
152
165
|
}
|
|
153
166
|
if c.get("withAttachment") and str(c["withAttachment"]) != "0":
|
|
154
167
|
out["has_attachment"] = True
|
|
168
|
+
files = _simplify_attachments(c.get("attachments"))
|
|
169
|
+
if files:
|
|
170
|
+
out["attachments"] = files
|
|
155
171
|
return out
|
|
156
172
|
|
|
157
173
|
|
|
@@ -203,6 +219,9 @@ def _simplify_post(p: dict[str, Any]) -> dict[str, Any]:
|
|
|
203
219
|
m_groups = [_simplify_group_ref(g) for g in (p.get("rGroupList") or [])]
|
|
204
220
|
if m_groups:
|
|
205
221
|
out["mentioned_groups"] = m_groups
|
|
222
|
+
files = _simplify_attachments(p.get("attachments"))
|
|
223
|
+
if files:
|
|
224
|
+
out["attachments"] = files
|
|
206
225
|
return out
|
|
207
226
|
|
|
208
227
|
|
|
@@ -377,6 +396,8 @@ def _render_post_detail(detail: dict[str, Any]) -> None:
|
|
|
377
396
|
click.echo("")
|
|
378
397
|
for line in (detail.get("text") or "").splitlines() or [""]:
|
|
379
398
|
click.echo(f" {line}")
|
|
399
|
+
for f in detail.get("attachments") or []:
|
|
400
|
+
click.echo(f" 📎 {f.get('name')}")
|
|
380
401
|
click.echo("")
|
|
381
402
|
|
|
382
403
|
stats = (
|
|
@@ -414,6 +435,8 @@ def _render_post_detail(detail: dict[str, Any]) -> None:
|
|
|
414
435
|
click.echo(header)
|
|
415
436
|
for line in (c.get("text") or "").splitlines() or [""]:
|
|
416
437
|
click.echo(f" {line}")
|
|
438
|
+
for f in c.get("attachments") or []:
|
|
439
|
+
click.echo(f" 📎 {f.get('name')}")
|
|
417
440
|
click.echo("")
|
|
418
441
|
|
|
419
442
|
|
|
@@ -669,7 +692,13 @@ def post_get(ctx, post_id):
|
|
|
669
692
|
default=0,
|
|
670
693
|
type=int,
|
|
671
694
|
show_default=True,
|
|
672
|
-
help="Post type
|
|
695
|
+
help="Post type. Leave at 0 (text); adding --file makes it an "
|
|
696
|
+
"attachment post on its own.",
|
|
697
|
+
)
|
|
698
|
+
@click.option(
|
|
699
|
+
"--file", "-f", "files", multiple=True,
|
|
700
|
+
help="Attach a file: a local path or an http(s) address. Repeat for "
|
|
701
|
+
"several. Videos, pictures and documents all work.",
|
|
673
702
|
)
|
|
674
703
|
@click.option(
|
|
675
704
|
"--to-group",
|
|
@@ -694,15 +723,20 @@ def post_get(ctx, post_id):
|
|
|
694
723
|
@click.option("--dry-run", is_flag=True,
|
|
695
724
|
help="Show the audience and what would be sent, without publishing.")
|
|
696
725
|
@pass_context
|
|
697
|
-
def post_create(ctx, org_id, message, post_type,
|
|
698
|
-
legacy_group_ids, legacy_org_ids,
|
|
726
|
+
def post_create(ctx, org_id, message, post_type, files, share_group_ids,
|
|
727
|
+
share_org_ids, legacy_group_ids, legacy_org_ids,
|
|
728
|
+
card_group_ids, dry_run):
|
|
699
729
|
"""Create a new post in the given organization.
|
|
700
730
|
|
|
701
731
|
Audience is set with --to-group / --to-org and defaults to everyone in
|
|
702
732
|
--org-id when neither is given. Who can see a post is covered in
|
|
703
733
|
`hap guide post audience`; the group-chat card is `hap guide post card`.
|
|
734
|
+
Files given with --file are uploaded and attached to the post.
|
|
704
735
|
"""
|
|
705
736
|
try:
|
|
737
|
+
if post_type == post_mod.POST_TYPE_ATTACHMENT and not files:
|
|
738
|
+
raise click.UsageError(
|
|
739
|
+
"An attachment post needs at least one --file.")
|
|
706
740
|
share_group_ids = tuple(share_group_ids) + tuple(legacy_group_ids)
|
|
707
741
|
share_org_ids = tuple(share_org_ids) + tuple(legacy_org_ids)
|
|
708
742
|
if dry_run:
|
|
@@ -712,11 +746,14 @@ def post_create(ctx, org_id, message, post_type, share_group_ids, share_org_ids,
|
|
|
712
746
|
) or [f"org:{org_id or '<current>'} (everyone, default)"]
|
|
713
747
|
ctx.output(
|
|
714
748
|
{"audience": audience, "cardToGroups": list(card_group_ids),
|
|
715
|
-
"message": message, "
|
|
749
|
+
"message": message, "files": list(files),
|
|
750
|
+
"published": False},
|
|
716
751
|
lambda d: click.echo(
|
|
717
752
|
"Would publish to: " + ", ".join(d["audience"])
|
|
718
753
|
+ (f"; card to groups: {', '.join(d['cardToGroups'])}"
|
|
719
|
-
if d["cardToGroups"] else "")
|
|
754
|
+
if d["cardToGroups"] else "")
|
|
755
|
+
+ (f"; files: {', '.join(d['files'])}"
|
|
756
|
+
if d["files"] else "")),
|
|
720
757
|
)
|
|
721
758
|
return
|
|
722
759
|
session = ctx.get_session()
|
|
@@ -741,6 +778,8 @@ def post_create(ctx, org_id, message, post_type, share_group_ids, share_org_ids,
|
|
|
741
778
|
+ ". Pick a group whose feed is on, or send a card to the "
|
|
742
779
|
"chat instead with --card-to-group.")
|
|
743
780
|
pid = require_api_project_id(session, org_id)
|
|
781
|
+
attachments = (post_mod.upload_post_files(session, list(files))
|
|
782
|
+
if files else None)
|
|
744
783
|
raw = post_mod.add_post(
|
|
745
784
|
session,
|
|
746
785
|
project_id=pid,
|
|
@@ -748,6 +787,7 @@ def post_create(ctx, org_id, message, post_type, share_group_ids, share_org_ids,
|
|
|
748
787
|
post_type=post_type,
|
|
749
788
|
share_group_ids=list(share_group_ids) or None,
|
|
750
789
|
share_project_ids=list(share_org_ids) or None,
|
|
790
|
+
attachments=attachments,
|
|
751
791
|
)
|
|
752
792
|
post_obj = raw.get("post") if isinstance(raw, dict) else None
|
|
753
793
|
summary = _simplify_post(post_obj) if post_obj else {"raw": raw}
|
|
@@ -821,14 +861,22 @@ def post_delete(ctx, post_id, yes):
|
|
|
821
861
|
default="",
|
|
822
862
|
help="Member id being replied to (optional; pairs with --reply-id)",
|
|
823
863
|
)
|
|
864
|
+
@click.option(
|
|
865
|
+
"--file", "-f", "files", multiple=True,
|
|
866
|
+
help="Attach a file: a local path or an http(s) address. Repeat for "
|
|
867
|
+
"several. Videos, pictures and documents all work.",
|
|
868
|
+
)
|
|
824
869
|
@pass_context
|
|
825
|
-
def post_comment(ctx, post_id, message, reply_id, reply_account_id):
|
|
826
|
-
"""Add a comment to a post."""
|
|
870
|
+
def post_comment(ctx, post_id, message, reply_id, reply_account_id, files):
|
|
871
|
+
"""Add a comment to a post, optionally with files attached."""
|
|
827
872
|
try:
|
|
828
873
|
session = ctx.get_session()
|
|
874
|
+
attachments = (post_mod.upload_post_files(session, list(files))
|
|
875
|
+
if files else None)
|
|
829
876
|
raw = post_mod.add_comment(
|
|
830
877
|
session, post_id, message,
|
|
831
878
|
reply_id=reply_id, reply_account_id=reply_account_id,
|
|
879
|
+
attachments=attachments,
|
|
832
880
|
)
|
|
833
881
|
comment_obj = raw.get("comment") if isinstance(raw, dict) else None
|
|
834
882
|
summary = _simplify_comment(comment_obj) if comment_obj else {"raw": raw}
|
|
@@ -864,6 +912,8 @@ def post_comments(ctx, post_id):
|
|
|
864
912
|
click.echo(header)
|
|
865
913
|
for line in (c.get("text") or "").splitlines() or [""]:
|
|
866
914
|
click.echo(f" {line}")
|
|
915
|
+
for f in c.get("attachments") or []:
|
|
916
|
+
click.echo(f" 📎 {f.get('name')}")
|
|
867
917
|
click.echo(f" id: {c.get('id')}")
|
|
868
918
|
click.echo("")
|
|
869
919
|
ctx.output(comments, _render)
|
|
@@ -24,6 +24,7 @@ Response envelopes (from a live probe on SaaS):
|
|
|
24
24
|
* ``GetPostDetail`` → bare post object (with ``success: "1"``)
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
+
import json
|
|
27
28
|
import logging
|
|
28
29
|
from typing import Any, Optional
|
|
29
30
|
|
|
@@ -219,6 +220,40 @@ def get_post_detail(session: Session, post_id: str) -> dict[str, Any]:
|
|
|
219
220
|
return session.api_call("Post", "GetPostDetail", {"postId": post_id})
|
|
220
221
|
|
|
221
222
|
|
|
223
|
+
# postType of a post carrying uploaded files ("pic and doc"). The feed's
|
|
224
|
+
# own types: 0 text, 1 link, 2 picture, 3 document, 7 vote, 9 attachments
|
|
225
|
+
# (MDPublic src/pages/feed/components/post/postComponent/factory.jsx:18-41,
|
|
226
|
+
# createFeed/index.js:18-22).
|
|
227
|
+
POST_TYPE_ATTACHMENT = 9
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _attachment_fields(attachments: list[dict[str, Any]]) -> dict[str, str]:
|
|
231
|
+
"""The two attachment keys, as JSON strings — the server takes strings
|
|
232
|
+
here, not arrays. Knowledge-center references are not supported, so
|
|
233
|
+
that list is always empty, as the composer sends it when none are
|
|
234
|
+
picked."""
|
|
235
|
+
return {
|
|
236
|
+
"attachments": json.dumps(attachments, ensure_ascii=False),
|
|
237
|
+
"knowledgeAttach": "[]",
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def upload_post_files(session: Session, sources: list[str]) -> list[dict[str, Any]]:
|
|
242
|
+
"""Upload local paths / http(s) addresses and return attachment items
|
|
243
|
+
for :func:`add_post` / :func:`add_comment`.
|
|
244
|
+
|
|
245
|
+
No organization is named for the upload credential: the feed mounts
|
|
246
|
+
its uploader without a projectId (MDPublic createFeed/
|
|
247
|
+
CreateFeedContent.jsx:124-136), same as chat does here.
|
|
248
|
+
"""
|
|
249
|
+
from hap_cli.core import upload as upload_mod
|
|
250
|
+
|
|
251
|
+
descriptors = upload_mod.upload_files(
|
|
252
|
+
session, upload_mod.source_descriptors(list(sources)))
|
|
253
|
+
logger.info("post: uploaded %d attachment(s)", len(descriptors))
|
|
254
|
+
return upload_mod.attachment_items(descriptors)
|
|
255
|
+
|
|
256
|
+
|
|
222
257
|
def add_post(
|
|
223
258
|
session: Session,
|
|
224
259
|
project_id: str,
|
|
@@ -228,9 +263,16 @@ def add_post(
|
|
|
228
263
|
share_group_ids: Optional[list[str]] = None,
|
|
229
264
|
share_project_ids: Optional[list[str]] = None,
|
|
230
265
|
radio_project_ids: str = "",
|
|
266
|
+
attachments: Optional[list[dict[str, Any]]] = None,
|
|
231
267
|
) -> dict[str, Any]:
|
|
232
268
|
"""Create a post via ``Post.AddPost``.
|
|
233
269
|
|
|
270
|
+
``attachments`` are uploaded-file items (see :func:`upload_post_files`).
|
|
271
|
+
Files make it an attachment post — ``postType`` 9 — and travel as JSON
|
|
272
|
+
strings next to an empty knowledge-center list, exactly as the feed's
|
|
273
|
+
composer sends them (MDPublic src/pages/feed/components/createFeed/
|
|
274
|
+
index.js:18-22 and :163-180; pd-openweb identical).
|
|
275
|
+
|
|
234
276
|
``scope`` is a ``PostShareScopeReq`` object — not an int — and it is
|
|
235
277
|
what decides **who can see the post**. The frontend
|
|
236
278
|
(``pd-openweb/src/pages/feed/components/createFeed/index.js``) posts
|
|
@@ -261,15 +303,16 @@ def add_post(
|
|
|
261
303
|
"shareGroupIds": groups,
|
|
262
304
|
"shareProjectIds": orgs,
|
|
263
305
|
}
|
|
264
|
-
|
|
265
|
-
"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
306
|
+
data: dict[str, Any] = {
|
|
307
|
+
"projectId": project_id,
|
|
308
|
+
"postType": post_type,
|
|
309
|
+
"postMsg": message,
|
|
310
|
+
"scope": scope,
|
|
311
|
+
}
|
|
312
|
+
if attachments:
|
|
313
|
+
data["postType"] = POST_TYPE_ATTACHMENT
|
|
314
|
+
data.update(_attachment_fields(attachments))
|
|
315
|
+
return session.api_call("Post", "AddPost", data)
|
|
273
316
|
|
|
274
317
|
|
|
275
318
|
def edit_post(
|
|
@@ -304,17 +347,22 @@ def add_comment(
|
|
|
304
347
|
message: str,
|
|
305
348
|
reply_id: str = "",
|
|
306
349
|
reply_account_id: str = "",
|
|
350
|
+
attachments: Optional[list[dict[str, Any]]] = None,
|
|
307
351
|
) -> dict[str, Any]:
|
|
308
352
|
"""Add a comment via ``Post.AddPostComment``.
|
|
309
353
|
|
|
310
354
|
Uses ``postID`` / ``replyID`` (capital ID) to match the documented
|
|
311
|
-
spelling in ``src/api/post.js``.
|
|
355
|
+
spelling in ``src/api/post.js``. ``attachments`` go in the same JSON
|
|
356
|
+
string form as on a post (MDPublic src/pages/feed/components/post/
|
|
357
|
+
comment/postCommentInput.jsx:181-192; pd-openweb identical).
|
|
312
358
|
"""
|
|
313
359
|
data: dict[str, Any] = {"postID": post_id, "message": message}
|
|
314
360
|
if reply_id:
|
|
315
361
|
data["replyID"] = reply_id
|
|
316
362
|
if reply_account_id:
|
|
317
363
|
data["replyAccountId"] = reply_account_id
|
|
364
|
+
if attachments:
|
|
365
|
+
data.update(_attachment_fields(attachments))
|
|
318
366
|
return _require_post_success(
|
|
319
367
|
session.api_call("Post", "AddPostComment", data), action="comment")
|
|
320
368
|
|
|
@@ -427,12 +427,15 @@ def upload_files(
|
|
|
427
427
|
return out
|
|
428
428
|
|
|
429
429
|
|
|
430
|
-
def
|
|
431
|
-
"""
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
430
|
+
def attachment_items(descriptors: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
431
|
+
"""Uploaded-file descriptors as the attachment items the uploader hands
|
|
432
|
+
to whatever it is attached to — the ``formatResponseData`` shape
|
|
433
|
+
(MDPublic src/utils/platform/file/attachment.js:93-111; pd-openweb
|
|
434
|
+
src/components/UploadFiles/utils.js). Posts and post comments send a
|
|
435
|
+
JSON string of these; record cells wrap them further."""
|
|
436
|
+
items = []
|
|
437
|
+
for d in descriptors:
|
|
438
|
+
item = {
|
|
436
439
|
"fileID": d.get("fileID"),
|
|
437
440
|
"fileSize": d.get("fileSize"),
|
|
438
441
|
"serverName": d.get("serverName"),
|
|
@@ -443,12 +446,20 @@ def record_attachment_cell(descriptors: list[dict[str, Any]]) -> dict[str, Any]:
|
|
|
443
446
|
"key": d.get("key"),
|
|
444
447
|
"url": d.get("url"),
|
|
445
448
|
"oldOriginalFileName": d.get("originalFileName"),
|
|
446
|
-
|
|
447
|
-
"isEdit": False,
|
|
448
|
-
})
|
|
449
|
+
}
|
|
449
450
|
# Carried, not recomputed: the descriptor already decided this at
|
|
450
451
|
# upload time — see _download_permission().
|
|
451
452
|
for key in ("allowDown", "docVersionID"):
|
|
452
453
|
if key in d:
|
|
453
|
-
|
|
454
|
+
item[key] = d[key]
|
|
455
|
+
items.append(item)
|
|
456
|
+
return items
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def record_attachment_cell(descriptors: list[dict[str, Any]]) -> dict[str, Any]:
|
|
460
|
+
"""Wrap uploaded-file descriptors into a worksheet Attachment (type 14)
|
|
461
|
+
cell value: ``{attachmentData, attachments, knowledgeAtts}``."""
|
|
462
|
+
attachments = []
|
|
463
|
+
for i, item in enumerate(attachment_items(descriptors)):
|
|
464
|
+
attachments.append({**item, "index": i, "isEdit": False})
|
|
454
465
|
return {"attachmentData": [], "attachments": attachments, "knowledgeAtts": []}
|
|
@@ -106,6 +106,8 @@ hap post list # list posts
|
|
|
106
106
|
hap post search -k "kw" --scope myself # only your own posts
|
|
107
107
|
hap post get <postId> # one post with its comments
|
|
108
108
|
hap post comment <postId> -m "..." # comment
|
|
109
|
+
hap post create -m "demo" -f ./demo.mp4 # attach a file (repeat -f for more)
|
|
110
|
+
hap post comment <postId> -m "..." -f ./notes.pdf # comment with a file
|
|
109
111
|
hap post delete <postId> # delete
|
|
110
112
|
```
|
|
111
113
|
|
|
@@ -94,6 +94,8 @@ hap post list # 动态列表
|
|
|
94
94
|
hap post search -k "关键字" --scope myself # 只搜自己发的
|
|
95
95
|
hap post get <动态ID> # 查看某条动态及评论
|
|
96
96
|
hap post comment <动态ID> -m "..." # 评论
|
|
97
|
+
hap post create -m "演示" -f ./demo.mp4 # 附带文件(多个就重复 -f)
|
|
98
|
+
hap post comment <动态ID> -m "..." -f ./notes.pdf # 评论附带文件
|
|
97
99
|
hap post delete <动态ID> # 删除
|
|
98
100
|
```
|
|
99
101
|
|
|
@@ -4420,8 +4420,12 @@
|
|
|
4420
4420
|
"zh-Hans": "以 JSON 格式输出"
|
|
4421
4421
|
},
|
|
4422
4422
|
"post comment": {
|
|
4423
|
-
"en": "Add a comment to a post.",
|
|
4424
|
-
"zh-Hans": "
|
|
4423
|
+
"en": "Add a comment to a post, optionally with files attached.",
|
|
4424
|
+
"zh-Hans": "为动态添加评论,可同时附带文件。"
|
|
4425
|
+
},
|
|
4426
|
+
"post comment --file": {
|
|
4427
|
+
"en": "Attach a file: a local path or an http(s) address. Repeat for several. Videos, pictures and documents all work.",
|
|
4428
|
+
"zh-Hans": "附带文件:本地路径或 http(s) 地址,可重复指定多个。视频、图片、文档都可以。"
|
|
4425
4429
|
},
|
|
4426
4430
|
"post comment --json": {
|
|
4427
4431
|
"en": "Output in JSON format",
|
|
@@ -4460,8 +4464,8 @@
|
|
|
4460
4464
|
"zh-Hans": "以 JSON 格式输出"
|
|
4461
4465
|
},
|
|
4462
4466
|
"post create": {
|
|
4463
|
-
"en": "Create a new post in the given organization.\n\nAudience is set with --to-group / --to-org and defaults to everyone in\n--org-id when neither is given. Who can see a post is covered in\n`hap guide post audience`; the group-chat card is `hap guide post card
|
|
4464
|
-
"zh-Hans": "在指定组织中发布新动态。用 --to-group / --to-org 指定发布范围(即谁能看到这条动态),两者都不给时默认发给 --org-id 组织的全体同事。谁能看到这条动态见 `hap guide post audience`;群聊卡片是另一回事,见 `hap guide post card
|
|
4467
|
+
"en": "Create a new post in the given organization.\n\nAudience is set with --to-group / --to-org and defaults to everyone in\n--org-id when neither is given. Who can see a post is covered in\n`hap guide post audience`; the group-chat card is `hap guide post card`.\nFiles given with --file are uploaded and attached to the post.",
|
|
4468
|
+
"zh-Hans": "在指定组织中发布新动态。用 --to-group / --to-org 指定发布范围(即谁能看到这条动态),两者都不给时默认发给 --org-id 组织的全体同事。谁能看到这条动态见 `hap guide post audience`;群聊卡片是另一回事,见 `hap guide post card`。用 --file 指定的文件会上传并作为附件随动态发布。"
|
|
4465
4469
|
},
|
|
4466
4470
|
"post create --card-to-group": {
|
|
4467
4471
|
"en": "After publishing, also send a card linking to the post into this group chat (repeatable). This is a chat message and does not change who can see the post.",
|
|
@@ -4471,6 +4475,10 @@
|
|
|
4471
4475
|
"en": "Show the audience and what would be sent, without publishing.",
|
|
4472
4476
|
"zh-Hans": "仅显示发布范围和将要发送的内容,不实际发布。"
|
|
4473
4477
|
},
|
|
4478
|
+
"post create --file": {
|
|
4479
|
+
"en": "Attach a file: a local path or an http(s) address. Repeat for several. Videos, pictures and documents all work.",
|
|
4480
|
+
"zh-Hans": "附带文件:本地路径或 http(s) 地址,可重复指定多个。视频、图片、文档都可以。"
|
|
4481
|
+
},
|
|
4474
4482
|
"post create --json": {
|
|
4475
4483
|
"en": "Output in JSON format",
|
|
4476
4484
|
"zh-Hans": "以 JSON 格式输出"
|
|
@@ -4492,8 +4500,8 @@
|
|
|
4492
4500
|
"zh-Hans": "发布范围:该组织的全体同事能看到这条动态(可重复指定),即公司范围。"
|
|
4493
4501
|
},
|
|
4494
4502
|
"post create --type": {
|
|
4495
|
-
"en": "Post type
|
|
4496
|
-
"zh-Hans": "
|
|
4503
|
+
"en": "Post type. Leave at 0 (text); adding --file makes it an attachment post on its own.",
|
|
4504
|
+
"zh-Hans": "动态类型。保持 0(文本)即可;加了 --file 会自动成为附件动态。"
|
|
4497
4505
|
},
|
|
4498
4506
|
"post delete": {
|
|
4499
4507
|
"en": "Delete a post.",
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Files attached to a post (动态) or a post comment.
|
|
2
|
+
|
|
3
|
+
The feed sends uploaded files as ``attachments`` — a JSON *string* of the
|
|
4
|
+
items ``formatResponseData`` builds — plus ``knowledgeAttach`` for files
|
|
5
|
+
referenced from the knowledge center, and a post carrying files is
|
|
6
|
+
``postType: 9`` (MDPublic src/pages/feed/components/createFeed/index.js:
|
|
7
|
+
18-22, 163-180; comments: src/pages/feed/components/post/comment/
|
|
8
|
+
postCommentInput.jsx:181-192; item shape: src/utils/platform/file/
|
|
9
|
+
attachment.js:93-111). pd-openweb is identical on every payload key.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from unittest.mock import MagicMock, patch
|
|
14
|
+
|
|
15
|
+
from click.testing import CliRunner
|
|
16
|
+
|
|
17
|
+
from hap_cli._entry import cli
|
|
18
|
+
from hap_cli.core import post as post_mod
|
|
19
|
+
from hap_cli.core import upload as upload_mod
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _descriptor(name="clip.mp4", ext=".mp4"):
|
|
23
|
+
d = {
|
|
24
|
+
"fileID": "o1", "fileSize": 2048, "etag": "e", "bucket": 3,
|
|
25
|
+
"serverName": "https://d.example/", "filePath": "pid/2026/",
|
|
26
|
+
"fileName": "abc", "fileExt": ext, "originalFileName": name[: -len(ext)],
|
|
27
|
+
"key": "pid/2026/abc" + ext, "url": "https://d.example/signed",
|
|
28
|
+
"urlExpiresAt": None,
|
|
29
|
+
}
|
|
30
|
+
d.update(upload_mod._download_permission(ext))
|
|
31
|
+
return d
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TestAttachmentItems:
|
|
35
|
+
def test_matches_format_response_data(self):
|
|
36
|
+
items = upload_mod.attachment_items([_descriptor()])
|
|
37
|
+
assert items == [{
|
|
38
|
+
"fileID": "o1", "fileSize": 2048,
|
|
39
|
+
"serverName": "https://d.example/", "filePath": "pid/2026/",
|
|
40
|
+
"fileName": "abc", "fileExt": ".mp4", "originalFileName": "clip",
|
|
41
|
+
"key": "pid/2026/abc.mp4", "url": "https://d.example/signed",
|
|
42
|
+
"oldOriginalFileName": "clip",
|
|
43
|
+
"allowDown": True, "docVersionID": "",
|
|
44
|
+
}]
|
|
45
|
+
|
|
46
|
+
def test_picture_carries_no_download_flag(self):
|
|
47
|
+
item = upload_mod.attachment_items([_descriptor("a.png", ".png")])[0]
|
|
48
|
+
assert "allowDown" not in item and "docVersionID" not in item
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TestAddPostAttachments:
|
|
52
|
+
def _body(self, session):
|
|
53
|
+
return session.api_call.call_args[0][2]
|
|
54
|
+
|
|
55
|
+
def test_attachments_make_an_attachment_post(self):
|
|
56
|
+
session = MagicMock()
|
|
57
|
+
session.api_call.return_value = {"success": True}
|
|
58
|
+
items = upload_mod.attachment_items([_descriptor()])
|
|
59
|
+
post_mod.add_post(session, "org1", "看视频", attachments=items)
|
|
60
|
+
body = self._body(session)
|
|
61
|
+
assert body["postType"] == 9
|
|
62
|
+
assert json.loads(body["attachments"]) == items
|
|
63
|
+
assert body["knowledgeAttach"] == "[]"
|
|
64
|
+
|
|
65
|
+
def test_text_post_sends_no_attachment_keys(self):
|
|
66
|
+
session = MagicMock()
|
|
67
|
+
session.api_call.return_value = {"success": True}
|
|
68
|
+
post_mod.add_post(session, "org1", "hi")
|
|
69
|
+
body = self._body(session)
|
|
70
|
+
assert body["postType"] == 0
|
|
71
|
+
assert "attachments" not in body and "knowledgeAttach" not in body
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TestAddCommentAttachments:
|
|
75
|
+
def test_comment_carries_attachments(self):
|
|
76
|
+
session = MagicMock()
|
|
77
|
+
session.api_call.return_value = {"success": True}
|
|
78
|
+
items = upload_mod.attachment_items([_descriptor()])
|
|
79
|
+
post_mod.add_comment(session, "p1", "附件", attachments=items)
|
|
80
|
+
body = session.api_call.call_args[0][2]
|
|
81
|
+
assert json.loads(body["attachments"]) == items
|
|
82
|
+
assert body["knowledgeAttach"] == "[]"
|
|
83
|
+
|
|
84
|
+
def test_plain_comment_unchanged(self):
|
|
85
|
+
session = MagicMock()
|
|
86
|
+
session.api_call.return_value = {"success": True}
|
|
87
|
+
post_mod.add_comment(session, "p1", "hi")
|
|
88
|
+
body = session.api_call.call_args[0][2]
|
|
89
|
+
assert "attachments" not in body
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class TestUploadPostFiles:
|
|
93
|
+
def test_paths_and_urls_upload_to_attachment_items(self):
|
|
94
|
+
"""The feed uploader asks for credentials with no organization
|
|
95
|
+
(UploadFiles is mounted without projectId), so none is passed."""
|
|
96
|
+
session = MagicMock()
|
|
97
|
+
with patch("hap_cli.core.upload.upload_files",
|
|
98
|
+
return_value=[_descriptor()]) as up:
|
|
99
|
+
items = post_mod.upload_post_files(
|
|
100
|
+
session, ["./clip.mp4", "https://x.example/a.pdf"])
|
|
101
|
+
files, = up.call_args[0][1:]
|
|
102
|
+
assert files == [{"path": "./clip.mp4"},
|
|
103
|
+
{"url": "https://x.example/a.pdf"}]
|
|
104
|
+
assert items[0]["oldOriginalFileName"] == "clip"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class TestPostCreateFileCli:
|
|
108
|
+
def _invoke(self, args):
|
|
109
|
+
runner = CliRunner()
|
|
110
|
+
with patch("hap_cli.context.Context.get_session"), \
|
|
111
|
+
patch("hap_cli.commands.post_cmd.require_api_project_id",
|
|
112
|
+
return_value="org1"), \
|
|
113
|
+
patch("hap_cli.core.post.upload_post_files",
|
|
114
|
+
return_value=[{"fileID": "o1"}]) as up, \
|
|
115
|
+
patch("hap_cli.core.post.add_post") as add:
|
|
116
|
+
add.return_value = {"post": {"postID": "p1"}}
|
|
117
|
+
result = runner.invoke(cli, args)
|
|
118
|
+
return result, up, add
|
|
119
|
+
|
|
120
|
+
def test_file_is_uploaded_and_attached(self):
|
|
121
|
+
result, up, add = self._invoke(
|
|
122
|
+
["post", "create", "-m", "看视频", "--file", "clip.mp4"])
|
|
123
|
+
assert result.exit_code == 0, result.output
|
|
124
|
+
assert up.call_args[0][1] == ["clip.mp4"]
|
|
125
|
+
assert add.call_args[1]["attachments"] == [{"fileID": "o1"}]
|
|
126
|
+
|
|
127
|
+
def test_without_file_nothing_is_uploaded(self):
|
|
128
|
+
result, up, add = self._invoke(["post", "create", "-m", "hi"])
|
|
129
|
+
assert result.exit_code == 0, result.output
|
|
130
|
+
assert not up.called
|
|
131
|
+
assert add.call_args[1].get("attachments") is None
|
|
132
|
+
|
|
133
|
+
def test_dry_run_lists_files_without_uploading(self):
|
|
134
|
+
runner = CliRunner()
|
|
135
|
+
with patch("hap_cli.context.Context.get_session") as sess, \
|
|
136
|
+
patch("hap_cli.core.post.upload_post_files") as up:
|
|
137
|
+
result = runner.invoke(cli, [
|
|
138
|
+
"--json", "post", "create", "-m", "hi", "-f", "clip.mp4",
|
|
139
|
+
"--dry-run"])
|
|
140
|
+
assert result.exit_code == 0, result.output
|
|
141
|
+
assert json.loads(result.output)["files"] == ["clip.mp4"]
|
|
142
|
+
assert not up.called and not sess.called
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class TestPostCommentFileCli:
|
|
146
|
+
def test_comment_file_is_uploaded_and_attached(self):
|
|
147
|
+
runner = CliRunner()
|
|
148
|
+
with patch("hap_cli.context.Context.get_session"), \
|
|
149
|
+
patch("hap_cli.core.post.upload_post_files",
|
|
150
|
+
return_value=[{"fileID": "o1"}]) as up, \
|
|
151
|
+
patch("hap_cli.core.post.add_comment") as add:
|
|
152
|
+
add.return_value = {"comment": {"commentID": "c1"}}
|
|
153
|
+
result = runner.invoke(cli, [
|
|
154
|
+
"post", "comment", "p1", "-m", "附件", "-f", "clip.mp4"])
|
|
155
|
+
assert result.exit_code == 0, result.output
|
|
156
|
+
assert up.call_args[0][1] == ["clip.mp4"]
|
|
157
|
+
assert add.call_args[1]["attachments"] == [{"fileID": "o1"}]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class TestAttachmentsInOutput:
|
|
161
|
+
"""Shape read back live from GetPostDetail / GetMorePostComments on
|
|
162
|
+
2026-10-10: each attachment has fileID, originalFilename (no
|
|
163
|
+
extension), ext and filesize."""
|
|
164
|
+
|
|
165
|
+
_ATT = {"fileID": "f1", "originalFilename": "clip", "ext": ".mp4",
|
|
166
|
+
"filesize": 15063, "viewType": 6}
|
|
167
|
+
|
|
168
|
+
def test_post_lists_its_files(self):
|
|
169
|
+
from hap_cli.commands.post_cmd import _simplify_post
|
|
170
|
+
out = _simplify_post({"postID": "p1", "attachments": [self._ATT]})
|
|
171
|
+
assert out["attachments"] == [
|
|
172
|
+
{"id": "f1", "name": "clip.mp4", "size": 15063}]
|
|
173
|
+
|
|
174
|
+
def test_comment_lists_its_files(self):
|
|
175
|
+
from hap_cli.commands.post_cmd import _simplify_comment
|
|
176
|
+
out = _simplify_comment({"commentID": "c1", "withAttachment": 1,
|
|
177
|
+
"attachments": [self._ATT]})
|
|
178
|
+
assert out["attachments"][0]["name"] == "clip.mp4"
|
|
179
|
+
|
|
180
|
+
def test_no_files_no_key(self):
|
|
181
|
+
from hap_cli.commands.post_cmd import _simplify_post
|
|
182
|
+
assert "attachments" not in _simplify_post({"postID": "p1"})
|
|
@@ -291,6 +291,7 @@ hap_cli/tests/test_page_filters_group_update.py
|
|
|
291
291
|
hap_cli/tests/test_page_save_page_id.py
|
|
292
292
|
hap_cli/tests/test_parameter_conventions.py
|
|
293
293
|
hap_cli/tests/test_parameter_mapping_registry.py
|
|
294
|
+
hap_cli/tests/test_post_attachments.py
|
|
294
295
|
hap_cli/tests/test_post_scope_cli.py
|
|
295
296
|
hap_cli/tests/test_record_batch_v3_semantic_cli.py
|
|
296
297
|
hap_cli/tests/test_record_business_rules.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|