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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. {hap_cli-0.9.5 → hap_cli-0.9.7}/PKG-INFO +3 -2
  2. {hap_cli-0.9.5/hap_cli → hap_cli-0.9.7}/README.md +2 -1
  3. {hap_cli-0.9.5 → hap_cli-0.9.7/hap_cli}/README.md +2 -1
  4. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/README_CN.md +2 -1
  5. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/__init__.py +1 -1
  6. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/config_cmd.py +42 -0
  7. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/group_cmd.py +54 -20
  8. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/post_cmd.py +63 -15
  9. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/context.py +26 -1
  10. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/chat.py +36 -19
  11. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/group.py +43 -0
  12. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/post.py +60 -3
  13. hap_cli-0.9.7/hap_cli/core/relogin.py +255 -0
  14. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/session.py +50 -1
  15. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/upload.py +33 -3
  16. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/post.md +19 -5
  17. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/index.json +2 -2
  18. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/post.md +15 -4
  19. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/locale/messages.json +50 -2
  20. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/conftest.py +12 -0
  21. hap_cli-0.9.7/hap_cli/tests/test_post_group_friction.py +157 -0
  22. hap_cli-0.9.7/hap_cli/tests/test_relogin.py +234 -0
  23. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/PKG-INFO +3 -2
  24. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/SOURCES.txt +3 -0
  25. {hap_cli-0.9.5 → hap_cli-0.9.7}/MANIFEST.in +0 -0
  26. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/__init__.py +0 -0
  27. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/__main__.py +0 -0
  28. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/config.py +0 -0
  29. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/fixtures/__init__.py +0 -0
  30. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/fixtures/feedback_scenario.py +0 -0
  31. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/harness.py +0 -0
  32. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/smoke.py +0 -0
  33. {hap_cli-0.9.5 → hap_cli-0.9.7}/app_live_tests/state.py +0 -0
  34. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/__main__.py +0 -0
  35. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/_entry.py +0 -0
  36. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/__init__.py +0 -0
  37. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/app_cmd.py +0 -0
  38. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/app_creator_cmd.py +0 -0
  39. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/app_editor_cmd.py +0 -0
  40. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/attachment_cmd.py +0 -0
  41. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/auth_cmd.py +0 -0
  42. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/calendar_cmd.py +0 -0
  43. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/chart_cmd.py +0 -0
  44. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/chat_cmd.py +0 -0
  45. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/chatbot_cmd.py +0 -0
  46. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/contact_cmd.py +0 -0
  47. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/department_cmd.py +0 -0
  48. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/guide_cmd.py +0 -0
  49. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/icon_cmd.py +0 -0
  50. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/instance_cmd.py +0 -0
  51. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/node_cmd.py +0 -0
  52. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/optionset_cmd.py +0 -0
  53. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/page_cmd.py +0 -0
  54. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/record_cmd.py +0 -0
  55. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/region_cmd.py +0 -0
  56. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/role_cmd.py +0 -0
  57. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/update_cmd.py +0 -0
  58. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/upload_cmd.py +0 -0
  59. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/v3_registry.py +0 -0
  60. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/workflow_cmd.py +0 -0
  61. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/commands/worksheet_cmd.py +0 -0
  62. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/__init__.py +0 -0
  63. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/action_spec_adapter.py +0 -0
  64. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app.py +0 -0
  65. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/__init__.py +0 -0
  66. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  67. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  68. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  69. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/charts.py +0 -0
  70. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/cleanup.py +0 -0
  71. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/compiler.py +0 -0
  72. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/config.py +0 -0
  73. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  74. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/errors.py +0 -0
  75. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  76. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/executor.py +0 -0
  77. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/fields.py +0 -0
  78. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/hap.py +0 -0
  79. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/merge.py +0 -0
  80. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  81. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/console.py +0 -0
  82. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  83. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  84. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/recording/report.py +0 -0
  85. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/schema.py +0 -0
  86. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  87. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  88. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/cli.py +0 -0
  89. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/executor.py +0 -0
  90. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  91. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  92. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  93. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  94. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/seed/template.py +0 -0
  95. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/selftest.py +0 -0
  96. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/smoke.py +0 -0
  97. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/steps.py +0 -0
  98. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/store.py +0 -0
  99. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/validate.py +0 -0
  100. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  101. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/attachment.py +0 -0
  102. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/auth.py +0 -0
  103. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/calendar_mod.py +0 -0
  104. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/chart.py +0 -0
  105. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/chatbot.py +0 -0
  106. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/cli_access.py +0 -0
  107. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/condition_dsl.py +0 -0
  108. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/contact.py +0 -0
  109. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/control_type_codes.py +0 -0
  110. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/department.py +0 -0
  111. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/__init__.py +0 -0
  112. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  113. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  114. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/apply.py +0 -0
  115. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/componentlower.py +0 -0
  116. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/component.schema.json +0 -0
  117. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
  118. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
  119. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec/field.schema.json +0 -0
  120. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/editspec.py +0 -0
  121. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/errors.py +0 -0
  122. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/fieldlower.py +0 -0
  123. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  124. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/models.py +0 -0
  125. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/ops.py +0 -0
  126. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/planner.py +0 -0
  127. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/editor/reader.py +0 -0
  128. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/field_normalizer.py +0 -0
  129. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/field_spec_adapter.py +0 -0
  130. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/filter_translator.py +0 -0
  131. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/filter_v3.py +0 -0
  132. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/flow_node.py +0 -0
  133. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/global_meta.py +0 -0
  134. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/guide.py +0 -0
  135. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/icon_index.py +0 -0
  136. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/instance.py +0 -0
  137. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/layout_check.py +0 -0
  138. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/logger.py +0 -0
  139. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/optionset.py +0 -0
  140. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/page.py +0 -0
  141. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record.py +0 -0
  142. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_defaults.py +0 -0
  143. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_form_checks.py +0 -0
  144. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_lookups.py +0 -0
  145. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/record_rules.py +0 -0
  146. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/relation_scope.py +0 -0
  147. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/response_crypto.py +0 -0
  148. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/role.py +0 -0
  149. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/role_perm_builder.py +0 -0
  150. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/rule_spec_adapter.py +0 -0
  151. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/token_crypto.py +0 -0
  152. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/update.py +0 -0
  153. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/__init__.py +0 -0
  154. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/dispatcher.py +0 -0
  155. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/render.py +0 -0
  156. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/v3/schema.py +0 -0
  157. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/view_spec_adapter.py +0 -0
  158. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/workflow.py +0 -0
  159. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/workflow_node_dsl.py +0 -0
  160. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet.py +0 -0
  161. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet_form.py +0 -0
  162. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet_reference.py +0 -0
  163. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/core/worksheet_templates.py +0 -0
  164. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/approval.md +0 -0
  165. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/chart.md +0 -0
  166. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/messages.md +0 -0
  167. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/overview.md +0 -0
  168. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/record.md +0 -0
  169. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/setup.md +0 -0
  170. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/skills.md +0 -0
  171. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/workflow.md +0 -0
  172. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/en/worksheet.md +0 -0
  173. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/approval.md +0 -0
  174. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/chart.md +0 -0
  175. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/messages.md +0 -0
  176. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/overview.md +0 -0
  177. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/record.md +0 -0
  178. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/setup.md +0 -0
  179. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/skills.md +0 -0
  180. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/workflow.md +0 -0
  181. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
  182. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/i18n.py +0 -0
  183. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/locale/__init__.py +0 -0
  184. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/locale/messages.schema.json +0 -0
  185. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/__init__.py +0 -0
  186. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
  187. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
  188. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_creator.py +0 -0
  189. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_editor.py +0 -0
  190. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_export_usage.py +0 -0
  191. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_light_color.py +0 -0
  192. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_app_section_name.py +0 -0
  193. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
  194. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_approval_wire.py +0 -0
  195. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_atomic_write.py +0 -0
  196. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_attachment_share.py +0 -0
  197. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_auth_prerelease.py +0 -0
  198. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_batch_add_atomic.py +0 -0
  199. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_bug_036430.py +0 -0
  200. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_bug_036750.py +0 -0
  201. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_calendar_date_range.py +0 -0
  202. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
  203. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chart.py +0 -0
  204. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chart_filter_merge.py +0 -0
  205. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_attachment_send.py +0 -0
  206. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_messages_cli.py +0 -0
  207. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_recall_cli.py +0 -0
  208. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_chat_send_socket.py +0 -0
  209. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_cli_access.py +0 -0
  210. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_coded_responses.py +0 -0
  211. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl.py +0 -0
  212. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
  213. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
  214. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_config_cmd.py +0 -0
  215. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_content_language.py +0 -0
  216. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  217. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_core.py +0 -0
  218. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_create_worksheet_partial.py +0 -0
  219. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  220. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
  221. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
  222. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
  223. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_engine_core_parity.py +0 -0
  224. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
  225. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_bidirectional.py +0 -0
  226. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
  227. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_entry_module.py +0 -0
  228. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
  229. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_http_error_body.py +0 -0
  230. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
  231. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
  232. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_layout_check.py +0 -0
  233. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
  234. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_node_summary.py +0 -0
  235. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_post_cursor.py +0 -0
  236. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
  237. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
  238. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
  239. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_subgroups.py +0 -0
  240. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_subtable_read.py +0 -0
  241. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
  242. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
  243. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
  244. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
  245. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_full_e2e.py +0 -0
  246. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_chart_parity.py +0 -0
  247. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_record_parity.py +0 -0
  248. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_upload_parity.py +0 -0
  249. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
  250. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
  251. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
  252. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g4_approval_parity.py +0 -0
  253. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
  254. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
  255. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
  256. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
  257. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
  258. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g8_filter_operator.py +0 -0
  259. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
  260. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
  261. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
  262. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_global_meta.py +0 -0
  263. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_guide.py +0 -0
  264. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_guide_sections.py +0 -0
  265. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_i18n.py +0 -0
  266. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
  267. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_instance_envelope.py +0 -0
  268. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_instance_history_cli.py +0 -0
  269. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration.py +0 -0
  270. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_approval.py +0 -0
  271. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  272. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_calendar.py +0 -0
  273. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_destructive.py +0 -0
  274. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_misc.py +0 -0
  275. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_post.py +0 -0
  276. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_social.py +0 -0
  277. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_v3.py +0 -0
  278. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_workflow.py +0 -0
  279. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  280. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
  281. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  282. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_multi_profile.py +0 -0
  283. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  284. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
  285. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  286. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_org_id_cli.py +0 -0
  287. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_org_id_docs.py +0 -0
  288. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_page_filters_group_read.py +0 -0
  289. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_page_filters_group_update.py +0 -0
  290. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_page_save_page_id.py +0 -0
  291. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_parameter_conventions.py +0 -0
  292. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  293. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_post_attachments.py +0 -0
  294. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_post_scope_cli.py +0 -0
  295. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  296. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_business_rules.py +0 -0
  297. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_defaults.py +0 -0
  298. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
  299. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_field_alias.py +0 -0
  300. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_form_checks.py +0 -0
  301. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_run_action.py +0 -0
  302. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  303. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_record_write_fixes.py +0 -0
  304. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_relation_scope.py +0 -0
  305. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
  306. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_role_app_scope.py +0 -0
  307. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
  308. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
  309. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_save_rule_spec.py +0 -0
  310. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_select_add_option.py +0 -0
  311. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  312. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  313. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
  314. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_update.py +0 -0
  315. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_update_mode.py +0 -0
  316. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_upload_allow_down.py +0 -0
  317. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  318. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
  319. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  320. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_bool_flags.py +0 -0
  321. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  322. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  323. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_response_format_json.py +0 -0
  324. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_session.py +0 -0
  325. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  326. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
  327. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_config_keys.py +0 -0
  328. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_inner_scope.py +0 -0
  329. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
  330. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
  331. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
  332. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_structure_listing.py +0 -0
  333. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
  334. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
  335. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_wo4_result_handling.py +0 -0
  336. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_code_node.py +0 -0
  337. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
  338. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
  339. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
  340. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  341. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
  342. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  343. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_node_wire.py +0 -0
  344. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
  345. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  346. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  347. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  348. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  349. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
  350. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_switch.py +0 -0
  351. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  352. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/__init__.py +0 -0
  353. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/atomic_write.py +0 -0
  354. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/confirm.py +0 -0
  355. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/formatting.py +0 -0
  356. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/options.py +0 -0
  357. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli/utils/parameter_mapping.py +0 -0
  358. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/dependency_links.txt +0 -0
  359. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/entry_points.txt +0 -0
  360. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/requires.txt +0 -0
  361. {hap_cli-0.9.5 → hap_cli-0.9.7}/hap_cli.egg-info/top_level.txt +0 -0
  362. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/__init__.py +0 -0
  363. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/__main__.py +0 -0
  364. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/_wftest.py +0 -0
  365. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/charts.py +0 -0
  366. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/cleanup.py +0 -0
  367. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/compiler.py +0 -0
  368. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/config.py +0 -0
  369. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/errors.py +0 -0
  370. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/executor.py +0 -0
  371. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/fields.py +0 -0
  372. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/hap.py +0 -0
  373. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/__init__.py +0 -0
  374. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/console.py +0 -0
  375. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/jsonl.py +0 -0
  376. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/mirror.py +0 -0
  377. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/recording/report.py +0 -0
  378. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/schema.py +0 -0
  379. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/__init__.py +0 -0
  380. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/cli.py +0 -0
  381. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/executor.py +0 -0
  382. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/seed/template.py +0 -0
  383. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/selftest.py +0 -0
  384. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/smoke.py +0 -0
  385. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/step.py +0 -0
  386. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/steps.py +0 -0
  387. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/store.py +0 -0
  388. {hap_cli-0.9.5 → hap_cli-0.9.7}/live_tests/workflow_dsl.py +0 -0
  389. {hap_cli-0.9.5 → hap_cli-0.9.7}/setup.cfg +0 -0
  390. {hap_cli-0.9.5 → hap_cli-0.9.7}/setup.py +0 -0
  391. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/INDEX.json +0 -0
  392. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  393. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  394. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/create_optionset.yaml +0 -0
  395. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/create_role.yaml +0 -0
  396. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  397. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_record.yaml +0 -0
  398. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_role.yaml +0 -0
  399. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  400. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_info.yaml +0 -0
  401. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  402. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  403. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  404. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_details.yaml +0 -0
  405. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  406. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_list.yaml +0 -0
  407. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  408. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  409. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  410. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  411. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_regions.yaml +0 -0
  412. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_role_details.yaml +0 -0
  413. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/get_role_list.yaml +0 -0
  414. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  415. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  416. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  417. {hap_cli-0.9.5 → hap_cli-0.9.7}/sources/v3-api-schema/update_optionset.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.9.5
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`) |
@@ -339,6 +339,7 @@ discover every subcommand.
339
339
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
340
340
  | `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
341
341
  | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
342
+ | `config auto-login [on\|off]` | Renew an expired login in the browser (one click on Authorize) and carry on; `off` stops with "not logged in" |
342
343
  | `config log on / off / status` | Toggle and inspect API logging |
343
344
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
344
345
  | `config log view [--lines N] [--no-follow]` | Tail the log file |
@@ -260,7 +260,7 @@ discover every subcommand.
260
260
 
261
261
  | Command | Description |
262
262
  |---|---|
263
- | `group list` | List open groups you have joined (`--org-id`, `--keywords`, 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`) |
@@ -310,6 +310,7 @@ discover every subcommand.
310
310
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
311
311
  | `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
312
312
  | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
313
+ | `config auto-login [on\|off]` | Renew an expired login in the browser (one click on Authorize) and carry on; `off` stops with "not logged in" |
313
314
  | `config log on / off / status` | Toggle and inspect API logging |
314
315
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
315
316
  | `config log view [--lines N] [--no-follow]` | Tail the log file |
@@ -260,7 +260,7 @@ discover every subcommand.
260
260
 
261
261
  | Command | Description |
262
262
  |---|---|
263
- | `group list` | List open groups you have joined (`--org-id`, `--keywords`, 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`) |
@@ -310,6 +310,7 @@ discover every subcommand.
310
310
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
311
311
  | `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
312
312
  | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
313
+ | `config auto-login [on\|off]` | Renew an expired login in the browser (one click on Authorize) and carry on; `off` stops with "not logged in" |
313
314
  | `config log on / off / status` | Toggle and inspect API logging |
314
315
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
315
316
  | `config log view [--lines N] [--no-follow]` | Tail the log file |
@@ -243,7 +243,7 @@ hap --json worksheet record list WORKSHEET_ID
243
243
 
244
244
  | 命令 | 说明 |
245
245
  |---|---|
246
- | `group list` | 分页列出当前用户已加入的正常群组(可用 `--org-id`、`--keywords` 筛选) |
246
+ | `group list` | 分页列出当前用户已加入的正常群组(可用 `--org-id`、`--keywords` 筛选;还有更多时返回 `has_more`/`next_page`;`--all` 取全部;`--feed-only` 只列能发动态的群) |
247
247
 
248
248
  ### region — 行政区划
249
249
 
@@ -289,6 +289,7 @@ hap --json worksheet record list WORKSHEET_ID
289
289
  | `config completion [bash\|zsh\|fish] [--install]` | 启用 `<Tab>` 补全 |
290
290
  | `config language [LANG]` | 切换显示语言(`en` / `zh-Hans`;`auto` 跟随登录账号的语言) |
291
291
  | `config update [auto\|notify\|off]` | 自动升级开关;`notify` 只提示不安装,`off` 不检查 |
292
+ | `config auto-login [on\|off]` | 登录过期时自动打开浏览器授权页,点一下「授权」命令继续执行;`off` 则直接报未登录 |
292
293
  | `config log on / off / status` | 日志开关与状态 |
293
294
  | `config log level LEVEL` | 设置日志级别(`DEBUG/INFO/WARNING/ERROR/CRITICAL`) |
294
295
  | `config log view [--lines N] [--no-follow]` | 跟踪日志文件 |
@@ -3,7 +3,7 @@
3
3
  import sys as _sys
4
4
  import types as _types
5
5
 
6
- __version__ = "0.9.5"
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
@@ -126,6 +126,7 @@ def config_completion(shell, install):
126
126
  @pass_context
127
127
  def config_show(ctx):
128
128
  """Display current harness configuration."""
129
+ from hap_cli.core.relogin import get_mode as get_auto_login_mode
129
130
  from hap_cli.core.update import get_update_mode
130
131
 
131
132
  log_cfg = load_logging_config()
@@ -136,6 +137,7 @@ def config_show(ctx):
136
137
  "file": str(resolve_log_path(log_cfg)),
137
138
  },
138
139
  "update": get_update_mode(),
140
+ "auto_login": get_auto_login_mode(),
139
141
  }
140
142
 
141
143
  def _human(d):
@@ -144,6 +146,7 @@ def config_show(ctx):
144
146
  click.echo(f" level : {d['logging']['level']}")
145
147
  click.echo(f" file : {d['logging']['file']}")
146
148
  click.echo(f"update : {d['update']}")
149
+ click.echo(f"auto-login: {d['auto_login']}")
147
150
 
148
151
  ctx.output(data, _human)
149
152
 
@@ -253,6 +256,45 @@ def config_update(ctx, mode):
253
256
  click.echo(f"Self-update set to: {mode}")
254
257
 
255
258
 
259
+ # ── `hap config auto-login` ───────────────────────────────────────────────
260
+
261
+
262
+ @config.command("auto-login")
263
+ @click.argument("mode", required=False, type=click.Choice(["on", "off"]))
264
+ @pass_context
265
+ def config_auto_login(ctx, mode):
266
+ """Control whether an expired login is renewed in the browser.
267
+
268
+ \b
269
+ on when a command finds your login expired, open the browser
270
+ authorization page, wait for you to click Authorize, then
271
+ carry on (default)
272
+ off stop with a "not logged in" error instead
273
+
274
+ Without an argument, shows the current setting. It never applies over
275
+ SSH, in CI, or where no browser can be shown. Setting the environment
276
+ variable HAP_AUTO_LOGIN=0 turns it off for one shell.
277
+ """
278
+ from hap_cli.core.relogin import CONFIG_KEY, get_mode
279
+ from hap_cli.core.session import save_config
280
+
281
+ if mode is None:
282
+ current = get_mode()
283
+ if ctx.json_mode:
284
+ from hap_cli.utils.formatting import output_json
285
+ output_json({"auto_login": current})
286
+ else:
287
+ click.echo(f"Auto-login: {current}")
288
+ return
289
+
290
+ save_config({CONFIG_KEY: mode})
291
+ if ctx.json_mode:
292
+ from hap_cli.utils.formatting import output_json
293
+ output_json({"status": "ok", "auto_login": mode})
294
+ else:
295
+ click.echo(f"Auto-login set to: {mode}")
296
+
297
+
256
298
  # ── `hap config language` ─────────────────────────────────────────────────
257
299
 
258
300
 
@@ -18,37 +18,71 @@ def group():
18
18
 
19
19
 
20
20
  def _output_group_list(data):
21
- rows = [
22
- {
23
- "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,
@@ -20,6 +20,27 @@ NOT_LOGGED_IN_HINT = (
20
20
  )
21
21
 
22
22
 
23
+ def _relogin_notice(login_url: str, timeout: int) -> None:
24
+ """Tell the user an authorization page is waiting for one click.
25
+
26
+ Written to stderr so ``--json`` output on stdout stays parseable; the
27
+ desktop notification is for callers (AI agents) whose terminal output
28
+ the user only sees after the command ends.
29
+ """
30
+ from hap_cli.core.relogin import desktop_notify
31
+ from hap_cli.i18n import t
32
+
33
+ click.echo(
34
+ t("Your HAP login has expired. A browser page is open — click "
35
+ "Authorize (授权) to continue (waiting up to {seconds}s).").format(
36
+ seconds=timeout),
37
+ err=True,
38
+ )
39
+ click.echo(f" {login_url}", err=True)
40
+ desktop_notify(t("Login expired — click Authorize (授权) in the browser "
41
+ "to let the command continue."))
42
+
43
+
23
44
  class Context:
24
45
  """Shared CLI context."""
25
46
 
@@ -43,6 +64,7 @@ class Context:
43
64
  """
44
65
  if self.session is None:
45
66
  self.session = Session.load(self.profile)
67
+ self.session.relogin_notice = _relogin_notice
46
68
  # One-time migration for sessions logged in before account_id
47
69
  # was persisted. Some HAP controllers (notably
48
70
  # Invitation/InviteUser) silently degrade when the AccountId
@@ -55,7 +77,10 @@ class Context:
55
77
  ):
56
78
  self.session._lazy_resolve_account_id()
57
79
  if require_auth and not self.session.auth_token:
58
- raise SessionError("Not logged in. Run 'hap auth login' first.")
80
+ # A saved environment without a token: authorize it again in
81
+ # the browser rather than stopping the command.
82
+ if not (self.session.login_url and self.session.recover_auth()):
83
+ raise SessionError("Not logged in. Run 'hap auth login' first.")
59
84
  return self.session
60
85
 
61
86
  def output(self, data, table_fn=None):
@@ -48,26 +48,43 @@ def _chatmq_get(
48
48
  # mirror the other transport hooks.
49
49
  from hap_cli.core import cli_access
50
50
 
51
+ from hap_cli.core.session import AuthenticationError
52
+
51
53
  cli_access.ensure_before_request(session, url)
52
- headers = {
53
- "Authorization": f"md_pss_id {session.auth_token}",
54
- "X-Requested-With": "XMLHttpRequest",
55
- "User-Agent": hap_user_agent(),
56
- }
57
- auth_for_log = redact_authorization(headers["Authorization"])
58
- logger.info(
59
- "HAP GET %s params=%s Authorization=%s",
60
- url,
61
- format_body_for_log(params) or "{}",
62
- auth_for_log,
63
- )
64
- response = requests.get(url, params=params, headers=headers, timeout=timeout)
65
- logger.info(
66
- "HAP GET %s -> %d %s",
67
- url,
68
- response.status_code,
69
- format_response_for_log(getattr(response, "text", "") or ""),
70
- )
54
+
55
+ def _send():
56
+ headers = {
57
+ "Authorization": f"md_pss_id {session.auth_token}",
58
+ "X-Requested-With": "XMLHttpRequest",
59
+ "User-Agent": hap_user_agent(),
60
+ }
61
+ auth_for_log = redact_authorization(headers["Authorization"])
62
+ logger.info(
63
+ "HAP GET %s params=%s Authorization=%s",
64
+ url,
65
+ format_body_for_log(params) or "{}",
66
+ auth_for_log,
67
+ )
68
+ response = requests.get(url, params=params, headers=headers, timeout=timeout)
69
+ logger.info(
70
+ "HAP GET %s -> %d %s",
71
+ url,
72
+ response.status_code,
73
+ format_response_for_log(getattr(response, "text", "") or ""),
74
+ )
75
+ return response
76
+
77
+ response = _send()
78
+ # An expired login gets the same one-shot browser re-authorization as
79
+ # the main transport (Session._post); the read is then resent once.
80
+ if response.status_code == 401:
81
+ if not session.recover_auth():
82
+ raise AuthenticationError(
83
+ "Authentication failed (HTTP 401). "
84
+ "Your session token is missing, invalid, or expired.",
85
+ status_code=401,
86
+ )
87
+ response = _send()
71
88
  response.raise_for_status()
72
89
  return response.json()
73
90
 
@@ -1,10 +1,13 @@
1
1
  """Group management for Mingdao HAP."""
2
2
 
3
3
  import json
4
+ import logging
4
5
  from typing import Any, Optional
5
6
 
6
7
  from hap_cli.core.session import Session
7
8
 
9
+ logger = logging.getLogger("hap_cli.group")
10
+
8
11
 
9
12
  def get_groups(
10
13
  session: Session,
@@ -36,6 +39,46 @@ def get_groups(
36
39
  )
37
40
 
38
41
 
42
+ def get_all_groups(
43
+ session: Session,
44
+ project_id: str,
45
+ keywords: str = "",
46
+ page_size: int = 100,
47
+ ) -> dict[str, Any]:
48
+ """Every page of :func:`get_groups`, as one ``{list, allCount}``.
49
+
50
+ Stops on a short page or once ``allCount`` rows are in, whichever
51
+ comes first, so a server that miscounts cannot loop forever.
52
+ """
53
+ rows: list[dict[str, Any]] = []
54
+ total = None
55
+ page = 1
56
+ while True:
57
+ data = get_groups(session, project_id, keywords=keywords,
58
+ page_index=page, page_size=page_size) or {}
59
+ batch = data.get("list") or []
60
+ rows.extend(batch)
61
+ if total is None:
62
+ total = data.get("allCount")
63
+ if len(batch) < page_size or (isinstance(total, int) and len(rows) >= total):
64
+ break
65
+ page += 1
66
+ logger.info("group: fetched %d group(s) over %d page(s)", len(rows), page)
67
+ return {"list": rows, "allCount": total if total is not None else len(rows)}
68
+
69
+
70
+ def mark_feed_groups(session: Session, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
71
+ """Add ``isPost`` (feed on, so the group can be a post audience) to
72
+ each row. ``GetGroups`` rows do not carry it — only ``GetGroupInfo``
73
+ does — so this costs one request per group."""
74
+ for row in rows:
75
+ info = get_group_info(session, row.get("groupId", "")) or {}
76
+ if isinstance(info.get("data"), dict):
77
+ info = info["data"]
78
+ row["isPost"] = bool(info.get("isPost"))
79
+ return rows
80
+
81
+
39
82
  def get_group_info(
40
83
  session: Session,
41
84
  group_id: str,
@@ -26,7 +26,8 @@ Response envelopes (from a live probe on SaaS):
26
26
 
27
27
  import json
28
28
  import logging
29
- 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,