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.
Files changed (414) hide show
  1. {hap_cli-0.9.4 → hap_cli-0.9.5}/PKG-INFO +1 -1
  2. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/__init__.py +1 -1
  3. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/post_cmd.py +57 -7
  4. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/post.py +58 -10
  5. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/upload.py +21 -10
  6. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/post.md +2 -0
  7. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/post.md +2 -0
  8. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/locale/messages.json +14 -6
  9. hap_cli-0.9.5/hap_cli/tests/test_post_attachments.py +182 -0
  10. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/PKG-INFO +1 -1
  11. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/SOURCES.txt +1 -0
  12. {hap_cli-0.9.4 → hap_cli-0.9.5}/MANIFEST.in +0 -0
  13. {hap_cli-0.9.4 → hap_cli-0.9.5}/README.md +0 -0
  14. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/__init__.py +0 -0
  15. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/__main__.py +0 -0
  16. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/config.py +0 -0
  17. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/fixtures/__init__.py +0 -0
  18. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/fixtures/feedback_scenario.py +0 -0
  19. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/harness.py +0 -0
  20. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/smoke.py +0 -0
  21. {hap_cli-0.9.4 → hap_cli-0.9.5}/app_live_tests/state.py +0 -0
  22. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/README.md +0 -0
  23. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/README_CN.md +0 -0
  24. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/__main__.py +0 -0
  25. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/_entry.py +0 -0
  26. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/__init__.py +0 -0
  27. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/app_cmd.py +0 -0
  28. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/app_creator_cmd.py +0 -0
  29. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/app_editor_cmd.py +0 -0
  30. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/attachment_cmd.py +0 -0
  31. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/auth_cmd.py +0 -0
  32. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/calendar_cmd.py +0 -0
  33. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/chart_cmd.py +0 -0
  34. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/chat_cmd.py +0 -0
  35. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/chatbot_cmd.py +0 -0
  36. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/config_cmd.py +0 -0
  37. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/contact_cmd.py +0 -0
  38. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/department_cmd.py +0 -0
  39. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/group_cmd.py +0 -0
  40. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/guide_cmd.py +0 -0
  41. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/icon_cmd.py +0 -0
  42. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/instance_cmd.py +0 -0
  43. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/node_cmd.py +0 -0
  44. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/optionset_cmd.py +0 -0
  45. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/page_cmd.py +0 -0
  46. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/record_cmd.py +0 -0
  47. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/region_cmd.py +0 -0
  48. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/role_cmd.py +0 -0
  49. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/update_cmd.py +0 -0
  50. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/upload_cmd.py +0 -0
  51. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/v3_registry.py +0 -0
  52. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/workflow_cmd.py +0 -0
  53. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/commands/worksheet_cmd.py +0 -0
  54. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/context.py +0 -0
  55. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/__init__.py +0 -0
  56. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/action_spec_adapter.py +0 -0
  57. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app.py +0 -0
  58. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/__init__.py +0 -0
  59. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  60. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  61. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  62. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/charts.py +0 -0
  63. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/cleanup.py +0 -0
  64. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/compiler.py +0 -0
  65. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/config.py +0 -0
  66. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  67. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/errors.py +0 -0
  68. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  69. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/executor.py +0 -0
  70. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/fields.py +0 -0
  71. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/hap.py +0 -0
  72. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/merge.py +0 -0
  73. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  74. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/console.py +0 -0
  75. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  76. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  77. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/recording/report.py +0 -0
  78. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/schema.py +0 -0
  79. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  80. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  81. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/cli.py +0 -0
  82. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/executor.py +0 -0
  83. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  84. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  85. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  86. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  87. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/seed/template.py +0 -0
  88. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/selftest.py +0 -0
  89. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/smoke.py +0 -0
  90. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/steps.py +0 -0
  91. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/store.py +0 -0
  92. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/validate.py +0 -0
  93. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  94. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/attachment.py +0 -0
  95. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/auth.py +0 -0
  96. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/calendar_mod.py +0 -0
  97. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/chart.py +0 -0
  98. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/chat.py +0 -0
  99. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/chatbot.py +0 -0
  100. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/cli_access.py +0 -0
  101. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/condition_dsl.py +0 -0
  102. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/contact.py +0 -0
  103. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/control_type_codes.py +0 -0
  104. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/department.py +0 -0
  105. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/__init__.py +0 -0
  106. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  107. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  108. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/apply.py +0 -0
  109. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/componentlower.py +0 -0
  110. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/component.schema.json +0 -0
  111. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
  112. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
  113. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec/field.schema.json +0 -0
  114. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/editspec.py +0 -0
  115. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/errors.py +0 -0
  116. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/fieldlower.py +0 -0
  117. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  118. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/models.py +0 -0
  119. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/ops.py +0 -0
  120. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/planner.py +0 -0
  121. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/editor/reader.py +0 -0
  122. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/field_normalizer.py +0 -0
  123. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/field_spec_adapter.py +0 -0
  124. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/filter_translator.py +0 -0
  125. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/filter_v3.py +0 -0
  126. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/flow_node.py +0 -0
  127. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/global_meta.py +0 -0
  128. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/group.py +0 -0
  129. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/guide.py +0 -0
  130. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/icon_index.py +0 -0
  131. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/instance.py +0 -0
  132. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/layout_check.py +0 -0
  133. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/logger.py +0 -0
  134. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/optionset.py +0 -0
  135. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/page.py +0 -0
  136. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record.py +0 -0
  137. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_defaults.py +0 -0
  138. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_form_checks.py +0 -0
  139. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_lookups.py +0 -0
  140. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/record_rules.py +0 -0
  141. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/relation_scope.py +0 -0
  142. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/response_crypto.py +0 -0
  143. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/role.py +0 -0
  144. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/role_perm_builder.py +0 -0
  145. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/rule_spec_adapter.py +0 -0
  146. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/session.py +0 -0
  147. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/token_crypto.py +0 -0
  148. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/update.py +0 -0
  149. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/__init__.py +0 -0
  150. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/dispatcher.py +0 -0
  151. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/render.py +0 -0
  152. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/v3/schema.py +0 -0
  153. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/view_spec_adapter.py +0 -0
  154. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/workflow.py +0 -0
  155. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/workflow_node_dsl.py +0 -0
  156. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet.py +0 -0
  157. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet_form.py +0 -0
  158. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet_reference.py +0 -0
  159. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/core/worksheet_templates.py +0 -0
  160. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/approval.md +0 -0
  161. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/chart.md +0 -0
  162. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/messages.md +0 -0
  163. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/overview.md +0 -0
  164. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/record.md +0 -0
  165. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/setup.md +0 -0
  166. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/skills.md +0 -0
  167. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/workflow.md +0 -0
  168. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/en/worksheet.md +0 -0
  169. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/index.json +0 -0
  170. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/approval.md +0 -0
  171. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/chart.md +0 -0
  172. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/messages.md +0 -0
  173. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/overview.md +0 -0
  174. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/record.md +0 -0
  175. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/setup.md +0 -0
  176. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/skills.md +0 -0
  177. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/workflow.md +0 -0
  178. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
  179. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/i18n.py +0 -0
  180. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/locale/__init__.py +0 -0
  181. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/locale/messages.schema.json +0 -0
  182. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/__init__.py +0 -0
  183. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/conftest.py +0 -0
  184. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
  185. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
  186. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_creator.py +0 -0
  187. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_editor.py +0 -0
  188. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_export_usage.py +0 -0
  189. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_light_color.py +0 -0
  190. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_app_section_name.py +0 -0
  191. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
  192. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_approval_wire.py +0 -0
  193. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_atomic_write.py +0 -0
  194. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_attachment_share.py +0 -0
  195. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_auth_prerelease.py +0 -0
  196. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_batch_add_atomic.py +0 -0
  197. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_bug_036430.py +0 -0
  198. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_bug_036750.py +0 -0
  199. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_calendar_date_range.py +0 -0
  200. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
  201. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chart.py +0 -0
  202. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chart_filter_merge.py +0 -0
  203. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_attachment_send.py +0 -0
  204. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_messages_cli.py +0 -0
  205. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_recall_cli.py +0 -0
  206. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_chat_send_socket.py +0 -0
  207. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_cli_access.py +0 -0
  208. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_coded_responses.py +0 -0
  209. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_condition_dsl.py +0 -0
  210. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
  211. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
  212. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_config_cmd.py +0 -0
  213. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_content_language.py +0 -0
  214. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  215. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_core.py +0 -0
  216. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_create_worksheet_partial.py +0 -0
  217. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  218. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
  219. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
  220. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
  221. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_engine_core_parity.py +0 -0
  222. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
  223. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_bidirectional.py +0 -0
  224. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
  225. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_entry_module.py +0 -0
  226. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
  227. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_http_error_body.py +0 -0
  228. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
  229. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
  230. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_layout_check.py +0 -0
  231. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
  232. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_node_summary.py +0 -0
  233. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_post_cursor.py +0 -0
  234. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
  235. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
  236. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
  237. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_subgroups.py +0 -0
  238. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_subtable_read.py +0 -0
  239. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
  240. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
  241. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
  242. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
  243. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_full_e2e.py +0 -0
  244. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_chart_parity.py +0 -0
  245. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_record_parity.py +0 -0
  246. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_upload_parity.py +0 -0
  247. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
  248. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
  249. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
  250. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g4_approval_parity.py +0 -0
  251. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
  252. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
  253. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
  254. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
  255. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
  256. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g8_filter_operator.py +0 -0
  257. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
  258. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
  259. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
  260. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_global_meta.py +0 -0
  261. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_guide.py +0 -0
  262. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_guide_sections.py +0 -0
  263. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_i18n.py +0 -0
  264. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
  265. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_instance_envelope.py +0 -0
  266. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_instance_history_cli.py +0 -0
  267. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration.py +0 -0
  268. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_approval.py +0 -0
  269. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  270. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_calendar.py +0 -0
  271. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_destructive.py +0 -0
  272. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_misc.py +0 -0
  273. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_post.py +0 -0
  274. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_social.py +0 -0
  275. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_v3.py +0 -0
  276. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_workflow.py +0 -0
  277. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  278. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
  279. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  280. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_multi_profile.py +0 -0
  281. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  282. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
  283. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  284. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_org_id_cli.py +0 -0
  285. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_org_id_docs.py +0 -0
  286. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_page_filters_group_read.py +0 -0
  287. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_page_filters_group_update.py +0 -0
  288. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_page_save_page_id.py +0 -0
  289. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_parameter_conventions.py +0 -0
  290. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  291. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_post_scope_cli.py +0 -0
  292. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  293. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_business_rules.py +0 -0
  294. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_defaults.py +0 -0
  295. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
  296. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_field_alias.py +0 -0
  297. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_form_checks.py +0 -0
  298. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_run_action.py +0 -0
  299. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  300. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_record_write_fixes.py +0 -0
  301. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_relation_scope.py +0 -0
  302. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
  303. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_role_app_scope.py +0 -0
  304. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
  305. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
  306. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_save_rule_spec.py +0 -0
  307. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_select_add_option.py +0 -0
  308. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  309. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  310. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
  311. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_update.py +0 -0
  312. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_update_mode.py +0 -0
  313. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_upload_allow_down.py +0 -0
  314. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  315. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
  316. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  317. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_bool_flags.py +0 -0
  318. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  319. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  320. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_response_format_json.py +0 -0
  321. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_session.py +0 -0
  322. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  323. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
  324. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_config_keys.py +0 -0
  325. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_inner_scope.py +0 -0
  326. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
  327. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
  328. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
  329. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_structure_listing.py +0 -0
  330. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
  331. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
  332. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_wo4_result_handling.py +0 -0
  333. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_code_node.py +0 -0
  334. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
  335. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
  336. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
  337. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  338. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
  339. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  340. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_node_wire.py +0 -0
  341. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
  342. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  343. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  344. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  345. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  346. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
  347. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_switch.py +0 -0
  348. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  349. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/__init__.py +0 -0
  350. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/atomic_write.py +0 -0
  351. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/confirm.py +0 -0
  352. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/formatting.py +0 -0
  353. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/options.py +0 -0
  354. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli/utils/parameter_mapping.py +0 -0
  355. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/dependency_links.txt +0 -0
  356. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/entry_points.txt +0 -0
  357. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/requires.txt +0 -0
  358. {hap_cli-0.9.4 → hap_cli-0.9.5}/hap_cli.egg-info/top_level.txt +0 -0
  359. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/__init__.py +0 -0
  360. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/__main__.py +0 -0
  361. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/_wftest.py +0 -0
  362. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/charts.py +0 -0
  363. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/cleanup.py +0 -0
  364. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/compiler.py +0 -0
  365. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/config.py +0 -0
  366. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/errors.py +0 -0
  367. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/executor.py +0 -0
  368. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/fields.py +0 -0
  369. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/hap.py +0 -0
  370. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/__init__.py +0 -0
  371. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/console.py +0 -0
  372. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/jsonl.py +0 -0
  373. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/mirror.py +0 -0
  374. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/recording/report.py +0 -0
  375. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/schema.py +0 -0
  376. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/__init__.py +0 -0
  377. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/cli.py +0 -0
  378. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/executor.py +0 -0
  379. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/seed/template.py +0 -0
  380. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/selftest.py +0 -0
  381. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/smoke.py +0 -0
  382. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/step.py +0 -0
  383. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/steps.py +0 -0
  384. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/store.py +0 -0
  385. {hap_cli-0.9.4 → hap_cli-0.9.5}/live_tests/workflow_dsl.py +0 -0
  386. {hap_cli-0.9.4 → hap_cli-0.9.5}/setup.cfg +0 -0
  387. {hap_cli-0.9.4 → hap_cli-0.9.5}/setup.py +0 -0
  388. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/INDEX.json +0 -0
  389. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  390. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  391. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/create_optionset.yaml +0 -0
  392. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/create_role.yaml +0 -0
  393. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  394. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_record.yaml +0 -0
  395. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_role.yaml +0 -0
  396. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  397. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_app_info.yaml +0 -0
  398. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  399. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  400. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  401. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_details.yaml +0 -0
  402. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  403. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_list.yaml +0 -0
  404. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  405. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  406. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  407. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  408. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_regions.yaml +0 -0
  409. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_role_details.yaml +0 -0
  410. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/get_role_list.yaml +0 -0
  411. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  412. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  413. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  414. {hap_cli-0.9.4 → hap_cli-0.9.5}/sources/v3-api-schema/update_optionset.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -3,7 +3,7 @@
3
3
  import sys as _sys
4
4
  import types as _types
5
5
 
6
- __version__ = "0.9.4"
6
+ __version__ = "0.9.5"
7
7
 
8
8
  # The command tree used to live in hap_cli/hap_cli.py. A module named
9
9
  # after the package it sits in shadows that package: run python with the
@@ -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: 0=text, 3=link, 5=vote, 7=file",
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, share_group_ids, share_org_ids,
698
- legacy_group_ids, legacy_org_ids, card_group_ids, dry_run):
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, "published": False},
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
- return session.api_call(
265
- "Post", "AddPost",
266
- {
267
- "projectId": project_id,
268
- "postType": post_type,
269
- "postMsg": message,
270
- "scope": scope,
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 record_attachment_cell(descriptors: list[dict[str, Any]]) -> dict[str, Any]:
431
- """Wrap uploaded-file descriptors into a worksheet Attachment (type 14)
432
- cell value: ``{attachmentData, attachments, knowledgeAtts}``."""
433
- attachments = []
434
- for i, d in enumerate(descriptors):
435
- attachments.append({
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
- "index": i,
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
- attachments[-1][key] = d[key]
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: 0=text, 3=link, 5=vote, 7=file",
4496
- "zh-Hans": "动态类型:0=文本,3=链接,5=投票,7=文件"
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"})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -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