hap-cli 0.9.6__tar.gz → 0.9.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. {hap_cli-0.9.6 → hap_cli-0.9.7}/PKG-INFO +2 -2
  2. {hap_cli-0.9.6/hap_cli → hap_cli-0.9.7}/README.md +1 -1
  3. {hap_cli-0.9.6 → hap_cli-0.9.7/hap_cli}/README.md +1 -1
  4. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/README_CN.md +1 -1
  5. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/__init__.py +1 -1
  6. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/group_cmd.py +54 -20
  7. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/post_cmd.py +63 -15
  8. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/group.py +43 -0
  9. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/post.py +60 -3
  10. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/upload.py +33 -3
  11. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/post.md +19 -5
  12. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/index.json +2 -2
  13. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/post.md +15 -4
  14. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/locale/messages.json +34 -2
  15. hap_cli-0.9.7/hap_cli/tests/test_post_group_friction.py +157 -0
  16. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli.egg-info/PKG-INFO +2 -2
  17. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli.egg-info/SOURCES.txt +1 -0
  18. {hap_cli-0.9.6 → hap_cli-0.9.7}/MANIFEST.in +0 -0
  19. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/__init__.py +0 -0
  20. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/__main__.py +0 -0
  21. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/config.py +0 -0
  22. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/fixtures/__init__.py +0 -0
  23. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/fixtures/feedback_scenario.py +0 -0
  24. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/harness.py +0 -0
  25. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/smoke.py +0 -0
  26. {hap_cli-0.9.6 → hap_cli-0.9.7}/app_live_tests/state.py +0 -0
  27. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/__main__.py +0 -0
  28. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/_entry.py +0 -0
  29. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/__init__.py +0 -0
  30. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/app_cmd.py +0 -0
  31. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/app_creator_cmd.py +0 -0
  32. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/app_editor_cmd.py +0 -0
  33. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/attachment_cmd.py +0 -0
  34. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/auth_cmd.py +0 -0
  35. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/calendar_cmd.py +0 -0
  36. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/chart_cmd.py +0 -0
  37. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/chat_cmd.py +0 -0
  38. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/chatbot_cmd.py +0 -0
  39. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/config_cmd.py +0 -0
  40. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/contact_cmd.py +0 -0
  41. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/department_cmd.py +0 -0
  42. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/guide_cmd.py +0 -0
  43. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/icon_cmd.py +0 -0
  44. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/instance_cmd.py +0 -0
  45. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/node_cmd.py +0 -0
  46. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/optionset_cmd.py +0 -0
  47. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/page_cmd.py +0 -0
  48. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/record_cmd.py +0 -0
  49. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/region_cmd.py +0 -0
  50. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/role_cmd.py +0 -0
  51. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/update_cmd.py +0 -0
  52. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/upload_cmd.py +0 -0
  53. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/v3_registry.py +0 -0
  54. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/workflow_cmd.py +0 -0
  55. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/commands/worksheet_cmd.py +0 -0
  56. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/context.py +0 -0
  57. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/__init__.py +0 -0
  58. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/action_spec_adapter.py +0 -0
  59. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app.py +0 -0
  60. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/__init__.py +0 -0
  61. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  62. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  63. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  64. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/charts.py +0 -0
  65. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/cleanup.py +0 -0
  66. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/compiler.py +0 -0
  67. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/config.py +0 -0
  68. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  69. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/errors.py +0 -0
  70. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  71. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/executor.py +0 -0
  72. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/fields.py +0 -0
  73. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/hap.py +0 -0
  74. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/merge.py +0 -0
  75. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  76. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/console.py +0 -0
  77. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  78. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  79. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/report.py +0 -0
  80. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/schema.py +0 -0
  81. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  82. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  83. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/cli.py +0 -0
  84. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/executor.py +0 -0
  85. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  86. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  87. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  88. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  89. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/template.py +0 -0
  90. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/selftest.py +0 -0
  91. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/smoke.py +0 -0
  92. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/steps.py +0 -0
  93. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/store.py +0 -0
  94. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/validate.py +0 -0
  95. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  96. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/attachment.py +0 -0
  97. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/auth.py +0 -0
  98. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/calendar_mod.py +0 -0
  99. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/chart.py +0 -0
  100. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/chat.py +0 -0
  101. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/chatbot.py +0 -0
  102. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/cli_access.py +0 -0
  103. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/condition_dsl.py +0 -0
  104. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/contact.py +0 -0
  105. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/control_type_codes.py +0 -0
  106. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/department.py +0 -0
  107. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/__init__.py +0 -0
  108. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  109. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  110. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/apply.py +0 -0
  111. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/componentlower.py +0 -0
  112. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/component.schema.json +0 -0
  113. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
  114. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
  115. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/field.schema.json +0 -0
  116. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/editspec.py +0 -0
  117. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/errors.py +0 -0
  118. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/fieldlower.py +0 -0
  119. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  120. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/models.py +0 -0
  121. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/ops.py +0 -0
  122. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/planner.py +0 -0
  123. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/editor/reader.py +0 -0
  124. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/field_normalizer.py +0 -0
  125. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/field_spec_adapter.py +0 -0
  126. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/filter_translator.py +0 -0
  127. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/filter_v3.py +0 -0
  128. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/flow_node.py +0 -0
  129. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/global_meta.py +0 -0
  130. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/guide.py +0 -0
  131. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/icon_index.py +0 -0
  132. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/instance.py +0 -0
  133. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/layout_check.py +0 -0
  134. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/logger.py +0 -0
  135. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/optionset.py +0 -0
  136. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/page.py +0 -0
  137. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/record.py +0 -0
  138. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/record_defaults.py +0 -0
  139. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/record_form_checks.py +0 -0
  140. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/record_lookups.py +0 -0
  141. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/record_rules.py +0 -0
  142. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/relation_scope.py +0 -0
  143. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/relogin.py +0 -0
  144. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/response_crypto.py +0 -0
  145. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/role.py +0 -0
  146. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/role_perm_builder.py +0 -0
  147. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/rule_spec_adapter.py +0 -0
  148. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/session.py +0 -0
  149. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/token_crypto.py +0 -0
  150. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/update.py +0 -0
  151. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/v3/__init__.py +0 -0
  152. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/v3/dispatcher.py +0 -0
  153. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/v3/render.py +0 -0
  154. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/v3/schema.py +0 -0
  155. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/view_spec_adapter.py +0 -0
  156. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/workflow.py +0 -0
  157. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/workflow_node_dsl.py +0 -0
  158. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/worksheet.py +0 -0
  159. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/worksheet_form.py +0 -0
  160. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/worksheet_reference.py +0 -0
  161. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/core/worksheet_templates.py +0 -0
  162. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/approval.md +0 -0
  163. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/chart.md +0 -0
  164. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/messages.md +0 -0
  165. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/overview.md +0 -0
  166. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/record.md +0 -0
  167. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/setup.md +0 -0
  168. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/skills.md +0 -0
  169. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/workflow.md +0 -0
  170. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/en/worksheet.md +0 -0
  171. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/approval.md +0 -0
  172. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/chart.md +0 -0
  173. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/messages.md +0 -0
  174. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/overview.md +0 -0
  175. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/record.md +0 -0
  176. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/setup.md +0 -0
  177. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/skills.md +0 -0
  178. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/workflow.md +0 -0
  179. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
  180. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/i18n.py +0 -0
  181. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/locale/__init__.py +0 -0
  182. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/locale/messages.schema.json +0 -0
  183. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/__init__.py +0 -0
  184. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/conftest.py +0 -0
  185. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
  186. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
  187. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_app_creator.py +0 -0
  188. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_app_editor.py +0 -0
  189. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_app_export_usage.py +0 -0
  190. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_app_light_color.py +0 -0
  191. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_app_section_name.py +0 -0
  192. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
  193. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_approval_wire.py +0 -0
  194. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_atomic_write.py +0 -0
  195. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_attachment_share.py +0 -0
  196. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_auth_prerelease.py +0 -0
  197. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_batch_add_atomic.py +0 -0
  198. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_bug_036430.py +0 -0
  199. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_bug_036750.py +0 -0
  200. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_calendar_date_range.py +0 -0
  201. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
  202. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_chart.py +0 -0
  203. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_chart_filter_merge.py +0 -0
  204. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_chat_attachment_send.py +0 -0
  205. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_chat_messages_cli.py +0 -0
  206. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_chat_recall_cli.py +0 -0
  207. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_chat_send_socket.py +0 -0
  208. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_cli_access.py +0 -0
  209. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_coded_responses.py +0 -0
  210. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl.py +0 -0
  211. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
  212. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
  213. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_config_cmd.py +0 -0
  214. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_content_language.py +0 -0
  215. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  216. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_core.py +0 -0
  217. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_create_worksheet_partial.py +0 -0
  218. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  219. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
  220. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
  221. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
  222. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_engine_core_parity.py +0 -0
  223. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
  224. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_bidirectional.py +0 -0
  225. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
  226. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_entry_module.py +0 -0
  227. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
  228. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_http_error_body.py +0 -0
  229. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
  230. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
  231. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_layout_check.py +0 -0
  232. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
  233. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_node_summary.py +0 -0
  234. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_post_cursor.py +0 -0
  235. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
  236. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
  237. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
  238. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_subgroups.py +0 -0
  239. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_subtable_read.py +0 -0
  240. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
  241. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
  242. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
  243. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
  244. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_full_e2e.py +0 -0
  245. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g1_chart_parity.py +0 -0
  246. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g1_record_parity.py +0 -0
  247. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g1_upload_parity.py +0 -0
  248. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
  249. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
  250. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
  251. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g4_approval_parity.py +0 -0
  252. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
  253. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
  254. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
  255. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
  256. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
  257. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g8_filter_operator.py +0 -0
  258. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
  259. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
  260. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
  261. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_global_meta.py +0 -0
  262. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_guide.py +0 -0
  263. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_guide_sections.py +0 -0
  264. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_i18n.py +0 -0
  265. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
  266. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_instance_envelope.py +0 -0
  267. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_instance_history_cli.py +0 -0
  268. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration.py +0 -0
  269. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_approval.py +0 -0
  270. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  271. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_calendar.py +0 -0
  272. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_destructive.py +0 -0
  273. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_misc.py +0 -0
  274. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_post.py +0 -0
  275. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_social.py +0 -0
  276. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_v3.py +0 -0
  277. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_workflow.py +0 -0
  278. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  279. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
  280. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  281. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_multi_profile.py +0 -0
  282. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  283. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
  284. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  285. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_org_id_cli.py +0 -0
  286. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_org_id_docs.py +0 -0
  287. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_page_filters_group_read.py +0 -0
  288. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_page_filters_group_update.py +0 -0
  289. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_page_save_page_id.py +0 -0
  290. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_parameter_conventions.py +0 -0
  291. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  292. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_post_attachments.py +0 -0
  293. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_post_scope_cli.py +0 -0
  294. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  295. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_business_rules.py +0 -0
  296. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_defaults.py +0 -0
  297. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
  298. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_field_alias.py +0 -0
  299. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_form_checks.py +0 -0
  300. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_run_action.py +0 -0
  301. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  302. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_record_write_fixes.py +0 -0
  303. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_relation_scope.py +0 -0
  304. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_relogin.py +0 -0
  305. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
  306. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_role_app_scope.py +0 -0
  307. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
  308. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
  309. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_save_rule_spec.py +0 -0
  310. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_select_add_option.py +0 -0
  311. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  312. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  313. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
  314. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_update.py +0 -0
  315. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_update_mode.py +0 -0
  316. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_upload_allow_down.py +0 -0
  317. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  318. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
  319. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  320. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_v3_bool_flags.py +0 -0
  321. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  322. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  323. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_v3_response_format_json.py +0 -0
  324. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_v3_session.py +0 -0
  325. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  326. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
  327. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_config_keys.py +0 -0
  328. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_inner_scope.py +0 -0
  329. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
  330. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
  331. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
  332. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_structure_listing.py +0 -0
  333. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
  334. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
  335. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_wo4_result_handling.py +0 -0
  336. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_code_node.py +0 -0
  337. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
  338. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
  339. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
  340. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  341. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
  342. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  343. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_node_wire.py +0 -0
  344. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
  345. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  346. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  347. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  348. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  349. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
  350. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_switch.py +0 -0
  351. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  352. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/utils/__init__.py +0 -0
  353. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/utils/atomic_write.py +0 -0
  354. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/utils/confirm.py +0 -0
  355. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/utils/formatting.py +0 -0
  356. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/utils/options.py +0 -0
  357. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli/utils/parameter_mapping.py +0 -0
  358. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli.egg-info/dependency_links.txt +0 -0
  359. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli.egg-info/entry_points.txt +0 -0
  360. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli.egg-info/requires.txt +0 -0
  361. {hap_cli-0.9.6 → hap_cli-0.9.7}/hap_cli.egg-info/top_level.txt +0 -0
  362. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/__init__.py +0 -0
  363. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/__main__.py +0 -0
  364. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/_wftest.py +0 -0
  365. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/charts.py +0 -0
  366. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/cleanup.py +0 -0
  367. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/compiler.py +0 -0
  368. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/config.py +0 -0
  369. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/errors.py +0 -0
  370. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/executor.py +0 -0
  371. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/fields.py +0 -0
  372. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/hap.py +0 -0
  373. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/recording/__init__.py +0 -0
  374. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/recording/console.py +0 -0
  375. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/recording/jsonl.py +0 -0
  376. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/recording/mirror.py +0 -0
  377. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/recording/report.py +0 -0
  378. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/schema.py +0 -0
  379. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/seed/__init__.py +0 -0
  380. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/seed/cli.py +0 -0
  381. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/seed/executor.py +0 -0
  382. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/seed/template.py +0 -0
  383. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/selftest.py +0 -0
  384. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/smoke.py +0 -0
  385. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/step.py +0 -0
  386. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/steps.py +0 -0
  387. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/store.py +0 -0
  388. {hap_cli-0.9.6 → hap_cli-0.9.7}/live_tests/workflow_dsl.py +0 -0
  389. {hap_cli-0.9.6 → hap_cli-0.9.7}/setup.cfg +0 -0
  390. {hap_cli-0.9.6 → hap_cli-0.9.7}/setup.py +0 -0
  391. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/INDEX.json +0 -0
  392. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  393. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  394. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/create_optionset.yaml +0 -0
  395. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/create_role.yaml +0 -0
  396. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  397. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/delete_record.yaml +0 -0
  398. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/delete_role.yaml +0 -0
  399. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  400. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_info.yaml +0 -0
  401. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  402. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  403. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  404. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_details.yaml +0 -0
  405. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  406. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_list.yaml +0 -0
  407. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  408. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  409. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  410. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  411. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_regions.yaml +0 -0
  412. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_role_details.yaml +0 -0
  413. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/get_role_list.yaml +0 -0
  414. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  415. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  416. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  417. {hap_cli-0.9.6 → hap_cli-0.9.7}/sources/v3-api-schema/update_optionset.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.9.6
3
+ Version: 0.9.7
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -289,7 +289,7 @@ discover every subcommand.
289
289
 
290
290
  | Command | Description |
291
291
  |---|---|
292
- | `group list` | List open groups you have joined (`--org-id`, `--keywords`, and pagination options) |
292
+ | `group list` | List open groups you have joined (`--org-id`, `--keywords`, pagination; `has_more`/`next_page` when more exist, `--all` for every group, `--feed-only` for groups that can receive a post) |
293
293
  | `group info GROUP_ID` | Group metadata |
294
294
  | `group create` | Create a new group (`--name`, `--org-id`, optional `--member`) |
295
295
  | `group add-member` | Add one or more members (`--group-id`, `--account-id`) |
@@ -260,7 +260,7 @@ discover every subcommand.
260
260
 
261
261
  | Command | Description |
262
262
  |---|---|
263
- | `group list` | List open groups you have joined (`--org-id`, `--keywords`, and pagination options) |
263
+ | `group list` | List open groups you have joined (`--org-id`, `--keywords`, pagination; `has_more`/`next_page` when more exist, `--all` for every group, `--feed-only` for groups that can receive a post) |
264
264
  | `group info GROUP_ID` | Group metadata |
265
265
  | `group create` | Create a new group (`--name`, `--org-id`, optional `--member`) |
266
266
  | `group add-member` | Add one or more members (`--group-id`, `--account-id`) |
@@ -260,7 +260,7 @@ discover every subcommand.
260
260
 
261
261
  | Command | Description |
262
262
  |---|---|
263
- | `group list` | List open groups you have joined (`--org-id`, `--keywords`, and pagination options) |
263
+ | `group list` | List open groups you have joined (`--org-id`, `--keywords`, pagination; `has_more`/`next_page` when more exist, `--all` for every group, `--feed-only` for groups that can receive a post) |
264
264
  | `group info GROUP_ID` | Group metadata |
265
265
  | `group create` | Create a new group (`--name`, `--org-id`, optional `--member`) |
266
266
  | `group add-member` | Add one or more members (`--group-id`, `--account-id`) |
@@ -243,7 +243,7 @@ hap --json worksheet record list WORKSHEET_ID
243
243
 
244
244
  | 命令 | 说明 |
245
245
  |---|---|
246
- | `group list` | 分页列出当前用户已加入的正常群组(可用 `--org-id`、`--keywords` 筛选) |
246
+ | `group list` | 分页列出当前用户已加入的正常群组(可用 `--org-id`、`--keywords` 筛选;还有更多时返回 `has_more`/`next_page`;`--all` 取全部;`--feed-only` 只列能发动态的群) |
247
247
 
248
248
  ### region — 行政区划
249
249
 
@@ -3,7 +3,7 @@
3
3
  import sys as _sys
4
4
  import types as _types
5
5
 
6
- __version__ = "0.9.6"
6
+ __version__ = "0.9.7"
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
@@ -18,37 +18,71 @@ def group():
18
18
 
19
19
 
20
20
  def _output_group_list(data):
21
- rows = [
22
- {
23
- "groupId": row.get("groupId", ""),
24
- "name": row.get("name") or row.get("groupName", ""),
25
- "groupMemberCount": row.get("groupMemberCount", ""),
26
- }
27
- for row in (data or {}).get("list", [])
28
- ]
21
+ rows = (data or {}).get("list", [])
22
+ with_feed = any("isPost" in row for row in rows)
23
+ columns = ["groupId", "name", "groupMemberCount"]
24
+ headers = ["Group ID", "Name", "Members"]
25
+ if with_feed:
26
+ columns.append("isPost")
27
+ headers.append("Feed")
29
28
  output_table(
30
- rows,
31
- ["groupId", "name", "groupMemberCount"],
32
- ["Group ID", "Name", "Members"],
29
+ [
30
+ {
31
+ "groupId": row.get("groupId", ""),
32
+ "name": row.get("name") or row.get("groupName", ""),
33
+ "groupMemberCount": row.get("groupMemberCount", ""),
34
+ "isPost": "yes" if row.get("isPost") else "no",
35
+ }
36
+ for row in rows
37
+ ],
38
+ columns,
39
+ headers,
33
40
  )
41
+ if data.get("has_more"):
42
+ click.echo(
43
+ f"Showing {len(rows)} of {data.get('allCount')} groups. Next page: "
44
+ f"-p {data.get('next_page')}, or --all for every group.",
45
+ err=True,
46
+ )
34
47
 
35
48
 
36
49
  @group.command("list")
37
50
  @org_id_option()
38
51
  @click.option("--keywords", "-k", default="", help="Filter groups by name")
39
52
  @pagination_options
53
+ @click.option("--all", "all_pages", is_flag=True,
54
+ help="Return every group instead of one page.")
55
+ @click.option("--feed-only", is_flag=True,
56
+ help="Only groups with the feed on, which are the ones a post "
57
+ "can be published to. Checks every group, so it is slower.")
40
58
  @pass_context
41
- def group_list(ctx, org_id, keywords, page_size, page):
42
- """List open chat groups you have joined."""
59
+ def group_list(ctx, org_id, keywords, page_size, page, all_pages, feed_only):
60
+ """List open chat groups you have joined.
61
+
62
+ One page at a time by default; when more exist the output says so
63
+ (has_more / next_page) — use --all to get every group at once.
64
+ """
43
65
  try:
44
66
  session = ctx.get_session()
45
- data = group_mod.get_groups(
46
- session,
47
- project_id=require_api_project_id(session, org_id),
48
- keywords=keywords,
49
- page_index=page,
50
- page_size=page_size,
51
- )
67
+ project_id = require_api_project_id(session, org_id)
68
+ if all_pages or feed_only:
69
+ data = group_mod.get_all_groups(session, project_id, keywords=keywords)
70
+ else:
71
+ data = group_mod.get_groups(
72
+ session,
73
+ project_id=project_id,
74
+ keywords=keywords,
75
+ page_index=page,
76
+ page_size=page_size,
77
+ ) or {}
78
+ total = data.get("allCount")
79
+ shown = (page - 1) * page_size + len(data.get("list") or [])
80
+ data["has_more"] = isinstance(total, int) and shown < total
81
+ if data["has_more"]:
82
+ data["next_page"] = page + 1
83
+ if feed_only:
84
+ rows = group_mod.mark_feed_groups(session, data.get("list") or [])
85
+ data["list"] = [row for row in rows if row.get("isPost")]
52
86
  ctx.output(data, _output_group_list)
53
87
  except Exception as e:
54
88
  ctx.handle_error(e)
@@ -683,6 +683,50 @@ def post_get(ctx, post_id):
683
683
  ctx.handle_error(e)
684
684
 
685
685
 
686
+ def _upload_progress(event: str, info: dict[str, Any]) -> None:
687
+ """One line per file on stderr, so --json output stays parseable."""
688
+ size = info.get("size")
689
+ size_text = f" ({size / 1048576:.1f} MB)" if size else ""
690
+ where = f"[{info['index']}/{info['total']}]"
691
+ if event == "start":
692
+ click.echo(f"Uploading {where} {info['name']}{size_text} ...", err=True)
693
+ elif event == "done":
694
+ click.echo(f"Uploaded {where} {info['name']}{size_text} in "
695
+ f"{info.get('seconds', 0):.1f}s", err=True)
696
+
697
+
698
+ def _preview_mentions(ctx, org_id: str, message: str) -> list[dict[str, Any]]:
699
+ """Who a --dry-run message would mention, looked up by account id.
700
+
701
+ Only signs in when the message actually mentions someone, so a plain
702
+ dry run stays offline."""
703
+ ids = post_mod.extract_mentions(message)
704
+ if not ids:
705
+ return []
706
+ session = ctx.get_session()
707
+ names = post_mod.resolve_accounts(
708
+ session, require_api_project_id(session, org_id), ids)
709
+ return [{"accountId": i, "name": names.get(i), "found": i in names}
710
+ for i in ids]
711
+
712
+
713
+ def _render_dry_run(d: dict[str, Any]) -> None:
714
+ line = "Would publish to: " + ", ".join(d["audience"])
715
+ if d["cardToGroups"]:
716
+ line += f"; card to groups: {', '.join(d['cardToGroups'])}"
717
+ if d["files"]:
718
+ line += f"; files: {', '.join(d['files'])}"
719
+ click.echo(line)
720
+ if d["categories"]:
721
+ click.echo("Categories: " + ", ".join(d["categories"]))
722
+ for m in d["mentions"]:
723
+ if m["found"]:
724
+ click.echo(f"Mentions: {m['name']} ({m['accountId']})")
725
+ else:
726
+ click.echo(f"Warning: no one in this organization has account id "
727
+ f"{m['accountId']}; that mention will not notify anyone.")
728
+
729
+
686
730
  @post.command("create")
687
731
  @org_id_option()
688
732
  @click.option("--message", "-m", required=True, help="Post content")
@@ -732,6 +776,12 @@ def post_create(ctx, org_id, message, post_type, files, share_group_ids,
732
776
  --org-id when neither is given. Who can see a post is covered in
733
777
  `hap guide post audience`; the group-chat card is `hap guide post card`.
734
778
  Files given with --file are uploaded and attached to the post.
779
+
780
+ In the message, #name# files the post under the category (topic) of
781
+ that name, and [aid]<accountId>[/aid] mentions a person — get the id
782
+ with `hap contact resolve <name>`. --dry-run lists both, with the
783
+ mentioned names, before anything is published. Details in
784
+ `hap guide post tags`.
735
785
  """
736
786
  try:
737
787
  if post_type == post_mod.POST_TYPE_ATTACHMENT and not files:
@@ -744,17 +794,13 @@ def post_create(ctx, org_id, message, post_type, files, share_group_ids,
744
794
  [f"group:{g}" for g in share_group_ids]
745
795
  + [f"org:{o}" for o in share_org_ids]
746
796
  ) or [f"org:{org_id or '<current>'} (everyone, default)"]
747
- ctx.output(
748
- {"audience": audience, "cardToGroups": list(card_group_ids),
749
- "message": message, "files": list(files),
750
- "published": False},
751
- lambda d: click.echo(
752
- "Would publish to: " + ", ".join(d["audience"])
753
- + (f"; card to groups: {', '.join(d['cardToGroups'])}"
754
- if d["cardToGroups"] else "")
755
- + (f"; files: {', '.join(d['files'])}"
756
- if d["files"] else "")),
757
- )
797
+ preview = {"audience": audience,
798
+ "cardToGroups": list(card_group_ids),
799
+ "message": message, "files": list(files),
800
+ "categories": post_mod.extract_topics(message),
801
+ "mentions": _preview_mentions(ctx, org_id, message),
802
+ "published": False}
803
+ ctx.output(preview, _render_dry_run)
758
804
  return
759
805
  session = ctx.get_session()
760
806
  # Only a group with the feed turned on can be a post audience. The
@@ -778,8 +824,9 @@ def post_create(ctx, org_id, message, post_type, files, share_group_ids,
778
824
  + ". Pick a group whose feed is on, or send a card to the "
779
825
  "chat instead with --card-to-group.")
780
826
  pid = require_api_project_id(session, org_id)
781
- attachments = (post_mod.upload_post_files(session, list(files))
782
- if files else None)
827
+ attachments = (post_mod.upload_post_files(
828
+ session, list(files), progress=_upload_progress)
829
+ if files else None)
783
830
  raw = post_mod.add_post(
784
831
  session,
785
832
  project_id=pid,
@@ -871,8 +918,9 @@ def post_comment(ctx, post_id, message, reply_id, reply_account_id, files):
871
918
  """Add a comment to a post, optionally with files attached."""
872
919
  try:
873
920
  session = ctx.get_session()
874
- attachments = (post_mod.upload_post_files(session, list(files))
875
- if files else None)
921
+ attachments = (post_mod.upload_post_files(
922
+ session, list(files), progress=_upload_progress)
923
+ if files else None)
876
924
  raw = post_mod.add_comment(
877
925
  session, post_id, message,
878
926
  reply_id=reply_id, reply_account_id=reply_account_id,
@@ -1,10 +1,13 @@
1
1
  """Group management for Mingdao HAP."""
2
2
 
3
3
  import json
4
+ import logging
4
5
  from typing import Any, Optional
5
6
 
6
7
  from hap_cli.core.session import Session
7
8
 
9
+ logger = logging.getLogger("hap_cli.group")
10
+
8
11
 
9
12
  def get_groups(
10
13
  session: Session,
@@ -36,6 +39,46 @@ def get_groups(
36
39
  )
37
40
 
38
41
 
42
+ def get_all_groups(
43
+ session: Session,
44
+ project_id: str,
45
+ keywords: str = "",
46
+ page_size: int = 100,
47
+ ) -> dict[str, Any]:
48
+ """Every page of :func:`get_groups`, as one ``{list, allCount}``.
49
+
50
+ Stops on a short page or once ``allCount`` rows are in, whichever
51
+ comes first, so a server that miscounts cannot loop forever.
52
+ """
53
+ rows: list[dict[str, Any]] = []
54
+ total = None
55
+ page = 1
56
+ while True:
57
+ data = get_groups(session, project_id, keywords=keywords,
58
+ page_index=page, page_size=page_size) or {}
59
+ batch = data.get("list") or []
60
+ rows.extend(batch)
61
+ if total is None:
62
+ total = data.get("allCount")
63
+ if len(batch) < page_size or (isinstance(total, int) and len(rows) >= total):
64
+ break
65
+ page += 1
66
+ logger.info("group: fetched %d group(s) over %d page(s)", len(rows), page)
67
+ return {"list": rows, "allCount": total if total is not None else len(rows)}
68
+
69
+
70
+ def mark_feed_groups(session: Session, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
71
+ """Add ``isPost`` (feed on, so the group can be a post audience) to
72
+ each row. ``GetGroups`` rows do not carry it — only ``GetGroupInfo``
73
+ does — so this costs one request per group."""
74
+ for row in rows:
75
+ info = get_group_info(session, row.get("groupId", "")) or {}
76
+ if isinstance(info.get("data"), dict):
77
+ info = info["data"]
78
+ row["isPost"] = bool(info.get("isPost"))
79
+ return rows
80
+
81
+
39
82
  def get_group_info(
40
83
  session: Session,
41
84
  group_id: str,
@@ -26,7 +26,8 @@ Response envelopes (from a live probe on SaaS):
26
26
 
27
27
  import json
28
28
  import logging
29
- from typing import Any, Optional
29
+ import re
30
+ from typing import Any, Callable, Optional
30
31
 
31
32
  from hap_cli.core.session import APIError, Session
32
33
 
@@ -238,22 +239,78 @@ def _attachment_fields(attachments: list[dict[str, Any]]) -> dict[str, str]:
238
239
  }
239
240
 
240
241
 
241
- def upload_post_files(session: Session, sources: list[str]) -> list[dict[str, Any]]:
242
+ def upload_post_files(
243
+ session: Session,
244
+ sources: list[str],
245
+ progress: Optional[Callable[[str, dict[str, Any]], None]] = None,
246
+ ) -> list[dict[str, Any]]:
242
247
  """Upload local paths / http(s) addresses and return attachment items
243
248
  for :func:`add_post` / :func:`add_comment`.
244
249
 
245
250
  No organization is named for the upload credential: the feed mounts
246
251
  its uploader without a projectId (MDPublic createFeed/
247
252
  CreateFeedContent.jsx:124-136), same as chat does here.
253
+ ``progress`` is handed to ``upload.upload_files`` unchanged.
248
254
  """
249
255
  from hap_cli.core import upload as upload_mod
250
256
 
251
257
  descriptors = upload_mod.upload_files(
252
- session, upload_mod.source_descriptors(list(sources)))
258
+ session, upload_mod.source_descriptors(list(sources)),
259
+ progress=progress)
253
260
  logger.info("post: uploaded %d attachment(s)", len(descriptors))
254
261
  return upload_mod.attachment_items(descriptors)
255
262
 
256
263
 
264
+ # What the feed composer writes into a post body (MDPublic
265
+ # src/components/MentionsInput/index.jsx). The post composer opens it
266
+ # without a sourceType, so a person is `[aid]<accountId>[/aid]`
267
+ # (mentionItemSyntax, :75-76) and a topic is `#name#`, half- or full-width
268
+ # `#` (categoryLetterArr, :19; :308). The server files the post under the
269
+ # category of that name, creating it when new, and reads it back as
270
+ # `categories` — there is no category field on AddPost itself.
271
+ _TOPIC_RE = re.compile(r"[##]([^##\n]+?)[##]")
272
+ _MENTION_RE = re.compile(r"\[aid\]([^\[\]]+?)\[/aid\]")
273
+
274
+
275
+ def extract_topics(message: str) -> list[str]:
276
+ """Topic names written as ``#name#`` in a post body, in order, unique."""
277
+ seen: list[str] = []
278
+ for name in _TOPIC_RE.findall(message or ""):
279
+ name = name.strip()
280
+ if name and name not in seen:
281
+ seen.append(name)
282
+ return seen
283
+
284
+
285
+ def extract_mentions(message: str) -> list[str]:
286
+ """Account ids written as ``[aid]<id>[/aid]`` in a post body, unique."""
287
+ seen: list[str] = []
288
+ for account_id in _MENTION_RE.findall(message or ""):
289
+ account_id = account_id.strip()
290
+ if account_id and account_id not in seen:
291
+ seen.append(account_id)
292
+ return seen
293
+
294
+
295
+ def resolve_accounts(
296
+ session: Session, project_id: str, account_ids: list[str],
297
+ ) -> dict[str, str]:
298
+ """Map account ids to display names via ``User.GetUserListByAccountId``
299
+ (MDPublic src/api/user.js:12). ``projectId`` is mandatory — the server
300
+ answers "组织编号不能为空" without it. Ids it does not know are simply
301
+ missing from the result."""
302
+ if not account_ids:
303
+ return {}
304
+ raw = session.api_call("User", "GetUserListByAccountId", {
305
+ "projectId": project_id, "accountIds": list(account_ids)})
306
+ rows = raw.get("list") if isinstance(raw, dict) else raw
307
+ names: dict[str, str] = {}
308
+ for row in rows or []:
309
+ if isinstance(row, dict) and row.get("accountId"):
310
+ names[row["accountId"]] = str(row.get("fullname") or "")
311
+ return names
312
+
313
+
257
314
  def add_post(
258
315
  session: Session,
259
316
  project_id: str,
@@ -27,7 +27,7 @@ import random
27
27
  import re
28
28
  import time
29
29
  from datetime import datetime, timezone
30
- from typing import Any, Optional
30
+ from typing import Any, Callable, Optional
31
31
  from urllib.parse import parse_qs, quote, urlparse
32
32
 
33
33
  import requests
@@ -404,11 +404,30 @@ def upload_files(
404
404
  project_id: str = "",
405
405
  bucket: Optional[int] = None,
406
406
  server_name: str = "",
407
+ progress: Optional[Callable[[str, dict[str, Any]], None]] = None,
407
408
  ) -> list[dict[str, Any]]:
408
409
  """Upload a list of ``{name?, url}`` / ``{name?, path}`` descriptors and
409
- return one uploaded-file descriptor per file (order preserved)."""
410
+ return one uploaded-file descriptor per file (order preserved).
411
+
412
+ ``progress(event, info)`` is called with ``"start"`` before each file
413
+ and ``"done"`` after it; ``info`` carries ``index`` (1-based),
414
+ ``total``, ``name``, ``size`` (bytes, ``None`` for a URL not yet
415
+ fetched) and, on ``"done"``, ``seconds``. Large videos take a while
416
+ and the caller has nothing else to show in the meantime.
417
+ """
410
418
  out: list[dict[str, Any]] = []
411
- for f in files:
419
+ total = len(files)
420
+ for index, f in enumerate(files, start=1):
421
+ label = f.get("name") or os.path.basename(f.get("path") or f.get("url") or "")
422
+ size = None
423
+ if f.get("path") and not f.get("url"):
424
+ try:
425
+ size = os.path.getsize(f["path"])
426
+ except OSError:
427
+ size = None
428
+ info = {"index": index, "total": total, "name": label, "size": size}
429
+ _report(progress, "start", info)
430
+ started = time.monotonic()
412
431
  url = f.get("url")
413
432
  path = f.get("path")
414
433
  if url:
@@ -424,9 +443,20 @@ def upload_files(
424
443
  session, content=content, name=name, worksheet_id=worksheet_id,
425
444
  app_id=app_id, project_id=project_id, bucket=bucket,
426
445
  server_name=server_name))
446
+ _report(progress, "done", {**info, "name": name, "size": len(content),
447
+ "seconds": time.monotonic() - started})
427
448
  return out
428
449
 
429
450
 
451
+ def _report(progress, event: str, info: dict[str, Any]) -> None:
452
+ if progress is None:
453
+ return
454
+ try:
455
+ progress(event, info)
456
+ except Exception as exc:
457
+ logger.warning("Upload progress callback failed: %s", exc)
458
+
459
+
430
460
  def attachment_items(descriptors: list[dict[str, Any]]) -> list[dict[str, Any]]:
431
461
  """Uploaded-file descriptors as the attachment items the uploader hands
432
462
  to whatever it is attached to — the ``formatResponseData`` shape
@@ -41,7 +41,8 @@ audience. Naming a `--to-group` does **not** additionally publish company-wide.
41
41
  A plain chat group cannot receive a post. The server refuses with
42
42
  "选择了没有加入的群组" (you picked a group you have not joined), which is
43
43
  **misleading** — you are in the group; it simply has no feed. The CLI checks
44
- first and names the group instead. To check one: `hap group info <groupId>`
44
+ first and names the group instead. To list every group that can receive a
45
+ post: `hap group list --feed-only`. To check one: `hap group info <groupId>`
45
46
  and look at `isPost`. To merely drop a link into such a group, use
46
47
  `--card-to-group` below.
47
48
 
@@ -90,12 +91,25 @@ remove it too, recall that message with
90
91
  `hap chat recall <messageId> --group-id <groupId>` (your own messages within 5
91
92
  minutes of sending; a group admin can recall a member's message with no limit).
92
93
 
93
- ## 3. Topic tags
94
- <!-- hook: Writing #tag# in the body registers a topic; hap post topics lists what is available. -->
94
+ ## 3. Categories and mentions in the body
95
+ <!-- hook: #name# in the body sets the post's category; [aid]accountId[/aid] mentions a person; --dry-run previews both. -->
95
96
  <!-- alias: tags -->
96
97
 
97
- Writing `#tag#` in the body registers a topic (for example `#今日工作#`), which
98
- shows up in the post's categories. List available topics with `hap post topics`.
98
+ There is no category option: **the category comes from the body.** Writing
99
+ `#name#` (for example `#今日工作#`) files the post under the category of that
100
+ name, creating it if it is new. List existing ones with `hap post topics`.
101
+
102
+ To mention someone, write `[aid]<accountId>[/aid]` where the `@name` should
103
+ go. Get the id from the person's name first:
104
+
105
+ ```bash
106
+ hap contact resolve 刘禹 # candidates[].id is the accountId
107
+ hap post create -m "#今日工作# 今天和 [aid]<accountId>[/aid] 对齐了方案" --dry-run
108
+ ```
109
+
110
+ A published post's audience cannot be changed, so check with `--dry-run`
111
+ first: it lists the categories and each mention with the person's name, and
112
+ warns about an id that matches nobody in the organization.
99
113
 
100
114
  ## 4. Other common commands
101
115
  <!-- hook: Listing, searching, reading, commenting on and deleting posts, plus what post search --scope accepts. -->
@@ -51,8 +51,8 @@
51
51
  "zh-Hans": "发动态与发布范围"
52
52
  },
53
53
  "summary": {
54
- "en": "Audience decides who can see a post, and you cannot read it back off an existing one.",
55
- "zh-Hans": "发布范围决定谁能看到,且无法从旧动态读出来。"
54
+ "en": "Audience decides who can see a post, and you cannot read it back off an existing one; #name# in the body sets the category, [aid]…[/aid] mentions someone.",
55
+ "zh-Hans": "发布范围决定谁能看到,且无法从旧动态读出来;正文 #名称# 决定分类,[aid]…[/aid] 提及某人。"
56
56
  }
57
57
  },
58
58
  {
@@ -37,6 +37,7 @@ hap post create -m "内容" --to-org fe288386-xxxx
37
37
 
38
38
  纯聊天群不能接收动态。服务端对此的报错是「选择了没有加入的群组」,**具有误导性**
39
39
  ——你明明在群里,真实原因是这个群没开启动态功能。CLI 会先行检查并直接报出群名。
40
+ 列出所有能发动态的群:`hap group list --feed-only`。
40
41
  想查某个群是否可用:`hap group info <群组ID>`,看 `isPost` 是否为 true。
41
42
  只是想在这种群里贴个链接,用下面的 `--card-to-group`。
42
43
 
@@ -78,12 +79,22 @@ hap post create -m "内容" --to-group 9ccde712-xxxx --card-to-group 7f694ec7-xx
78
79
  指向一条已不存在的动态。要一并清掉,用 `hap chat recall <消息ID> --group-id <群组ID>`
79
80
  撤回那条卡片消息(自己的消息限发送后 5 分钟内,群管理员撤回他人消息不限时)。
80
81
 
81
- ## 3. 话题标签
82
- <!-- hook: 正文里写 #标签# 会登记话题,可用话题用 hap post topics 看 -->
82
+ ## 3. 正文里的分类与 @提及
83
+ <!-- hook: 正文 #名称# 定分类,[aid]id[/aid] 提及某人,--dry-run 可预览 -->
83
84
  <!-- alias: tags -->
84
85
 
85
- 正文里写 `#标签#` 会被识别为话题(例如 `#今日工作#`),发布后出现在动态的
86
- 分类里。可用的话题用 `hap post topics` 查。
86
+ 没有设置分类的选项:**分类由正文决定。** 正文里写 `#名称#`(例如 `#今日工作#`),
87
+ 动态就会归到同名分类下,分类不存在时会自动新建。已有分类用 `hap post topics` 查。
88
+
89
+ 要 @ 某人,在想出现「@姓名」的位置写 `[aid]<accountId>[/aid]`。先用姓名查出 id:
90
+
91
+ ```bash
92
+ hap contact resolve 刘禹 # candidates[].id 就是 accountId
93
+ hap post create -m "#今日工作# 今天和 [aid]<accountId>[/aid] 对齐了方案" --dry-run
94
+ ```
95
+
96
+ 动态发出后范围无法再改,所以先用 `--dry-run` 检查:它会列出分类,以及每个
97
+ 提及对应的姓名;某个 id 在组织里找不到人时会给出警告。
87
98
 
88
99
  ## 4. 其它常用
89
100
  <!-- hook: 动态的列表、搜索、读取、评论、删除,以及 search --scope 接受哪些值 -->
@@ -4347,6 +4347,38 @@
4347
4347
  "en": "Output in JSON format",
4348
4348
  "zh-Hans": "以 JSON 格式输出"
4349
4349
  },
4350
+ "group list": {
4351
+ "en": "List open chat groups you have joined.\n\nOne page at a time by default; when more exist the output says so\n(has_more / next_page) — use --all to get every group at once.",
4352
+ "zh-Hans": "列出你已加入的公开聊天群组。\n\n默认一次返回一页;还有更多时输出会注明(has_more / next_page)——用 --all 一次取回全部群组。"
4353
+ },
4354
+ "group list --all": {
4355
+ "en": "Return every group instead of one page.",
4356
+ "zh-Hans": "返回全部群组,而不是一页。"
4357
+ },
4358
+ "group list --feed-only": {
4359
+ "en": "Only groups with the feed on, which are the ones a post can be published to. Checks every group, so it is slower.",
4360
+ "zh-Hans": "只列出开启了动态的群组,也就是能作为动态发布范围的群组。需要逐个检查群组,所以较慢。"
4361
+ },
4362
+ "group list --json": {
4363
+ "en": "Output in JSON format",
4364
+ "zh-Hans": "以 JSON 格式输出"
4365
+ },
4366
+ "group list --keywords": {
4367
+ "en": "Filter groups by name",
4368
+ "zh-Hans": "按群组名称筛选"
4369
+ },
4370
+ "group list --org-id": {
4371
+ "en": "Organization ID",
4372
+ "zh-Hans": "组织 ID"
4373
+ },
4374
+ "group list --page": {
4375
+ "en": "Page number",
4376
+ "zh-Hans": "页码"
4377
+ },
4378
+ "group list --page-size": {
4379
+ "en": "Items per page",
4380
+ "zh-Hans": "每页条数"
4381
+ },
4350
4382
  "group remove-member": {
4351
4383
  "en": "Remove a single member from a chat group.",
4352
4384
  "zh-Hans": "从聊天群组移除单个成员"
@@ -4480,8 +4512,8 @@
4480
4512
  "zh-Hans": "以 JSON 格式输出"
4481
4513
  },
4482
4514
  "post create": {
4483
- "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.",
4484
- "zh-Hans": "在指定组织中发布新动态。用 --to-group / --to-org 指定发布范围(即谁能看到这条动态),两者都不给时默认发给 --org-id 组织的全体同事。谁能看到这条动态见 `hap guide post audience`;群聊卡片是另一回事,见 `hap guide post card`。用 --file 指定的文件会上传并作为附件随动态发布。"
4515
+ "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.\n\nIn the message, #name# files the post under the category (topic) of\nthat name, and [aid]<accountId>[/aid] mentions a person — get the id\nwith `hap contact resolve <name>`. --dry-run lists both, with the\nmentioned names, before anything is published. Details in\n`hap guide post tags`.",
4516
+ "zh-Hans": "在指定组织中发布一条新动态。\n\n发布范围用 --to-group / --to-org 指定,都不给时默认发给 --org-id 组织的全体同事。谁能看到动态见 `hap guide post audience`;群聊卡片见 `hap guide post card`。用 --file 指定的文件会上传并附加到动态上。\n\n正文里写 #名称# 会把动态归到同名分类(话题)下,写 [aid]<accountId>[/aid] 会提及某人——用 `hap contact resolve <姓名>` 查 id。--dry-run 会在发布前列出这两项以及被提及人的姓名。详见 `hap guide post tags`。"
4485
4517
  },
4486
4518
  "post create --card-to-group": {
4487
4519
  "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.",