hap-cli 0.8.24__tar.gz → 0.8.25__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 (285) hide show
  1. {hap_cli-0.8.24 → hap_cli-0.8.25}/PKG-INFO +3 -3
  2. {hap_cli-0.8.24/hap_cli → hap_cli-0.8.25}/README.md +2 -2
  3. {hap_cli-0.8.24 → hap_cli-0.8.25/hap_cli}/README.md +2 -2
  4. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/README_CN.md +2 -1
  5. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/__init__.py +1 -1
  6. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/chat_cmd.py +58 -10
  7. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/config_cmd.py +93 -16
  8. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/instance_cmd.py +15 -4
  9. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/chat.py +73 -0
  10. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/instance.py +147 -13
  11. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/hap_cli.py +4 -0
  12. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/i18n.py +24 -7
  13. hap_cli-0.8.25/hap_cli/locale/messages.json +4817 -0
  14. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_chat_attachment_send.py +137 -0
  15. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_core.py +173 -1
  16. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_i18n.py +115 -16
  17. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_approval_actions.py +34 -10
  18. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +6 -1
  19. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli.egg-info/PKG-INFO +3 -3
  20. hap_cli-0.8.24/hap_cli/locale/messages.json +0 -3271
  21. {hap_cli-0.8.24 → hap_cli-0.8.25}/MANIFEST.in +0 -0
  22. {hap_cli-0.8.24 → hap_cli-0.8.25}/app_live_tests/__init__.py +0 -0
  23. {hap_cli-0.8.24 → hap_cli-0.8.25}/app_live_tests/__main__.py +0 -0
  24. {hap_cli-0.8.24 → hap_cli-0.8.25}/app_live_tests/config.py +0 -0
  25. {hap_cli-0.8.24 → hap_cli-0.8.25}/app_live_tests/harness.py +0 -0
  26. {hap_cli-0.8.24 → hap_cli-0.8.25}/app_live_tests/smoke.py +0 -0
  27. {hap_cli-0.8.24 → hap_cli-0.8.25}/app_live_tests/state.py +0 -0
  28. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/__main__.py +0 -0
  29. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/__init__.py +0 -0
  30. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/app_cmd.py +0 -0
  31. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/app_creator_cmd.py +0 -0
  32. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/app_editor_cmd.py +0 -0
  33. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/attachment_cmd.py +0 -0
  34. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/auth_cmd.py +0 -0
  35. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/calendar_cmd.py +0 -0
  36. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/chart_cmd.py +0 -0
  37. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/chatbot_cmd.py +0 -0
  38. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/contact_cmd.py +0 -0
  39. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/department_cmd.py +0 -0
  40. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/group_cmd.py +0 -0
  41. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/guide_cmd.py +0 -0
  42. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/icon_cmd.py +0 -0
  43. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/node_cmd.py +0 -0
  44. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/optionset_cmd.py +0 -0
  45. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/page_cmd.py +0 -0
  46. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/post_cmd.py +0 -0
  47. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/record_cmd.py +0 -0
  48. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/region_cmd.py +0 -0
  49. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/role_cmd.py +0 -0
  50. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/update_cmd.py +0 -0
  51. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/upload_cmd.py +0 -0
  52. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/v3_registry.py +0 -0
  53. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/workflow_cmd.py +0 -0
  54. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/commands/worksheet_cmd.py +0 -0
  55. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/context.py +0 -0
  56. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/__init__.py +0 -0
  57. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/action_spec_adapter.py +0 -0
  58. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app.py +0 -0
  59. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/__init__.py +0 -0
  60. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  61. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  62. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  63. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/charts.py +0 -0
  64. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/cleanup.py +0 -0
  65. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/compiler.py +0 -0
  66. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/config.py +0 -0
  67. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  68. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/errors.py +0 -0
  69. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  70. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/executor.py +0 -0
  71. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/fields.py +0 -0
  72. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/hap.py +0 -0
  73. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/merge.py +0 -0
  74. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  75. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/recording/console.py +0 -0
  76. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  77. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  78. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/recording/report.py +0 -0
  79. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/schema.py +0 -0
  80. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  81. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  82. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/cli.py +0 -0
  83. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/executor.py +0 -0
  84. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  85. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  86. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  87. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  88. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/seed/template.py +0 -0
  89. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/selftest.py +0 -0
  90. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/smoke.py +0 -0
  91. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/steps.py +0 -0
  92. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/store.py +0 -0
  93. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/validate.py +0 -0
  94. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  95. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/attachment.py +0 -0
  96. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/auth.py +0 -0
  97. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/calendar_mod.py +0 -0
  98. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/chart.py +0 -0
  99. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/chatbot.py +0 -0
  100. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/cli_access.py +0 -0
  101. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/contact.py +0 -0
  102. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/control_type_codes.py +0 -0
  103. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/department.py +0 -0
  104. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/__init__.py +0 -0
  105. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  106. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  107. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/apply.py +0 -0
  108. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/componentlower.py +0 -0
  109. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/editspec/component.schema.json +0 -0
  110. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
  111. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
  112. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/editspec/field.schema.json +0 -0
  113. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/editspec.py +0 -0
  114. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/errors.py +0 -0
  115. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/fieldlower.py +0 -0
  116. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  117. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/models.py +0 -0
  118. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/ops.py +0 -0
  119. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/planner.py +0 -0
  120. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/editor/reader.py +0 -0
  121. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/field_normalizer.py +0 -0
  122. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/field_spec_adapter.py +0 -0
  123. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/filter_translator.py +0 -0
  124. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/flow_node.py +0 -0
  125. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/global_meta.py +0 -0
  126. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/group.py +0 -0
  127. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/guide.py +0 -0
  128. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/icon_index.py +0 -0
  129. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/logger.py +0 -0
  130. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/optionset.py +0 -0
  131. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/page.py +0 -0
  132. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/post.py +0 -0
  133. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/record.py +0 -0
  134. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/response_crypto.py +0 -0
  135. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/role.py +0 -0
  136. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/role_perm_builder.py +0 -0
  137. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/session.py +0 -0
  138. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/token_crypto.py +0 -0
  139. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/update.py +0 -0
  140. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/upload.py +0 -0
  141. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/v3/__init__.py +0 -0
  142. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/v3/dispatcher.py +0 -0
  143. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/v3/render.py +0 -0
  144. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/v3/schema.py +0 -0
  145. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/view_spec_adapter.py +0 -0
  146. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/workflow.py +0 -0
  147. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/workflow_node_dsl.py +0 -0
  148. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/worksheet.py +0 -0
  149. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/core/worksheet_templates.py +0 -0
  150. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/en/messages.md +0 -0
  151. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/en/overview.md +0 -0
  152. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/en/record.md +0 -0
  153. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/en/setup.md +0 -0
  154. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/en/skills.md +0 -0
  155. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/index.json +0 -0
  156. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/zh-Hans/messages.md +0 -0
  157. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/zh-Hans/overview.md +0 -0
  158. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/zh-Hans/record.md +0 -0
  159. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/zh-Hans/setup.md +0 -0
  160. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/guide/zh-Hans/skills.md +0 -0
  161. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/locale/__init__.py +0 -0
  162. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/locale/messages.schema.json +0 -0
  163. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/__init__.py +0 -0
  164. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/conftest.py +0 -0
  165. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_app_creator.py +0 -0
  166. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_app_editor.py +0 -0
  167. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_app_export_usage.py +0 -0
  168. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_attachment_share.py +0 -0
  169. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_auth_prerelease.py +0 -0
  170. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_bug_036430.py +0 -0
  171. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_bug_036750.py +0 -0
  172. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_chart.py +0 -0
  173. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_cli_access.py +0 -0
  174. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_config_cmd.py +0 -0
  175. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_content_language.py +0 -0
  176. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  177. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  178. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_full_e2e.py +0 -0
  179. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_global_meta.py +0 -0
  180. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_guide.py +0 -0
  181. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_instance_history_cli.py +0 -0
  182. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration.py +0 -0
  183. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_approval.py +0 -0
  184. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_calendar.py +0 -0
  185. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_destructive.py +0 -0
  186. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_misc.py +0 -0
  187. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_post.py +0 -0
  188. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_social.py +0 -0
  189. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_v3.py +0 -0
  190. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_workflow.py +0 -0
  191. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  192. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  193. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_multi_profile.py +0 -0
  194. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  195. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_org_id_cli.py +0 -0
  196. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_org_id_docs.py +0 -0
  197. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_parameter_conventions.py +0 -0
  198. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  199. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  200. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_record_field_alias.py +0 -0
  201. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  202. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_record_write_fixes.py +0 -0
  203. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_select_add_option.py +0 -0
  204. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  205. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  206. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_update.py +0 -0
  207. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_update_mode.py +0 -0
  208. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  209. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  210. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  211. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  212. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_v3_session.py +0 -0
  213. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  214. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  215. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  216. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  217. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  218. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  219. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  220. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  221. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/utils/__init__.py +0 -0
  222. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/utils/formatting.py +0 -0
  223. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/utils/options.py +0 -0
  224. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli/utils/parameter_mapping.py +0 -0
  225. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli.egg-info/SOURCES.txt +0 -0
  226. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli.egg-info/dependency_links.txt +0 -0
  227. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli.egg-info/entry_points.txt +0 -0
  228. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli.egg-info/requires.txt +0 -0
  229. {hap_cli-0.8.24 → hap_cli-0.8.25}/hap_cli.egg-info/top_level.txt +0 -0
  230. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/__init__.py +0 -0
  231. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/__main__.py +0 -0
  232. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/_wftest.py +0 -0
  233. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/charts.py +0 -0
  234. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/cleanup.py +0 -0
  235. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/compiler.py +0 -0
  236. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/config.py +0 -0
  237. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/errors.py +0 -0
  238. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/executor.py +0 -0
  239. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/fields.py +0 -0
  240. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/hap.py +0 -0
  241. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/recording/__init__.py +0 -0
  242. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/recording/console.py +0 -0
  243. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/recording/jsonl.py +0 -0
  244. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/recording/mirror.py +0 -0
  245. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/recording/report.py +0 -0
  246. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/schema.py +0 -0
  247. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/seed/__init__.py +0 -0
  248. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/seed/cli.py +0 -0
  249. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/seed/executor.py +0 -0
  250. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/seed/template.py +0 -0
  251. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/selftest.py +0 -0
  252. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/smoke.py +0 -0
  253. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/step.py +0 -0
  254. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/steps.py +0 -0
  255. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/store.py +0 -0
  256. {hap_cli-0.8.24 → hap_cli-0.8.25}/live_tests/workflow_dsl.py +0 -0
  257. {hap_cli-0.8.24 → hap_cli-0.8.25}/setup.cfg +0 -0
  258. {hap_cli-0.8.24 → hap_cli-0.8.25}/setup.py +0 -0
  259. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/INDEX.json +0 -0
  260. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  261. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  262. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/create_optionset.yaml +0 -0
  263. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/create_role.yaml +0 -0
  264. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  265. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/delete_record.yaml +0 -0
  266. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/delete_role.yaml +0 -0
  267. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  268. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_app_info.yaml +0 -0
  269. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  270. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  271. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  272. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_record_details.yaml +0 -0
  273. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  274. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_record_list.yaml +0 -0
  275. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  276. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  277. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  278. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  279. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_regions.yaml +0 -0
  280. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_role_details.yaml +0 -0
  281. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/get_role_list.yaml +0 -0
  282. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  283. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  284. {hap_cli-0.8.24 → hap_cli-0.8.25}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  285. {hap_cli-0.8.24 → hap_cli-0.8.25}/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.8.24
3
+ Version: 0.8.25
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -273,7 +273,7 @@ discover every subcommand.
273
273
  | `chat messages` | 1-on-1 / group / inbox messages |
274
274
  | `chat files` | Files shared in a chat |
275
275
  | `chat send-to-one` | Send a message, optionally with files (`--file`), to one or more users (1-on-1) |
276
- | `chat send-to-group` | Send a plain-text message to a group chat |
276
+ | `chat send-to-group` | Send a message, optionally with files, to a group chat |
277
277
  | `chat card-detail` | Resolve embedded card references |
278
278
 
279
279
  ### group — chat group lifecycle
@@ -326,7 +326,7 @@ discover every subcommand.
326
326
  |---|---|
327
327
  | `config show` | Display the current configuration summary |
328
328
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
329
- | `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
329
+ | `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
330
330
  | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
331
331
  | `config log on / off / status` | Toggle and inspect API logging |
332
332
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
@@ -244,7 +244,7 @@ discover every subcommand.
244
244
  | `chat messages` | 1-on-1 / group / inbox messages |
245
245
  | `chat files` | Files shared in a chat |
246
246
  | `chat send-to-one` | Send a message, optionally with files (`--file`), to one or more users (1-on-1) |
247
- | `chat send-to-group` | Send a plain-text message to a group chat |
247
+ | `chat send-to-group` | Send a message, optionally with files, to a group chat |
248
248
  | `chat card-detail` | Resolve embedded card references |
249
249
 
250
250
  ### group — chat group lifecycle
@@ -297,7 +297,7 @@ discover every subcommand.
297
297
  |---|---|
298
298
  | `config show` | Display the current configuration summary |
299
299
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
300
- | `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
300
+ | `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
301
301
  | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
302
302
  | `config log on / off / status` | Toggle and inspect API logging |
303
303
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
@@ -244,7 +244,7 @@ discover every subcommand.
244
244
  | `chat messages` | 1-on-1 / group / inbox messages |
245
245
  | `chat files` | Files shared in a chat |
246
246
  | `chat send-to-one` | Send a message, optionally with files (`--file`), to one or more users (1-on-1) |
247
- | `chat send-to-group` | Send a plain-text message to a group chat |
247
+ | `chat send-to-group` | Send a message, optionally with files, to a group chat |
248
248
  | `chat card-detail` | Resolve embedded card references |
249
249
 
250
250
  ### group — chat group lifecycle
@@ -297,7 +297,7 @@ discover every subcommand.
297
297
  |---|---|
298
298
  | `config show` | Display the current configuration summary |
299
299
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
300
- | `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
300
+ | `config language [LANG]` | Switch UI language (`en` / `zh-Hans`, or `auto` to follow the account language) |
301
301
  | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
302
302
  | `config log on / off / status` | Toggle and inspect API logging |
303
303
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
@@ -227,6 +227,7 @@ hap --json worksheet record list WORKSHEET_ID
227
227
  | `chat messages` | 单聊 / 群聊 / 收件箱(系统、应用、工作流等) |
228
228
  | `chat files` | 聊天中分享的文件 |
229
229
  | `chat send-to-one` | 向一个或多个用户发消息(单聊),可带附件 `--file`(文件作为聊天附件送达,不是链接) |
230
+ | `chat send-to-group` | 向群聊发消息,可带附件 `--file`,支持 `--at` / `--at-all` 提醒 |
230
231
  | `chat group-info` | 群组信息 |
231
232
  | `chat card-detail` | 解析消息中嵌入的卡片引用 |
232
233
 
@@ -271,7 +272,7 @@ hap --json worksheet record list WORKSHEET_ID
271
272
  |---|---|
272
273
  | `config show` | 显示 CLI 配置摘要 |
273
274
  | `config completion [bash\|zsh\|fish] [--install]` | 启用 `<Tab>` 补全 |
274
- | `config language [LANG]` | 切换显示语言(`en` / `zh-Hans` / `zh-Hant` / `ja`) |
275
+ | `config language [LANG]` | 切换显示语言(`en` / `zh-Hans`;`auto` 跟随登录账号的语言) |
275
276
  | `config update [auto\|notify\|off]` | 自动升级开关;`notify` 只提示不安装,`off` 不检查 |
276
277
  | `config log on / off / status` | 日志开关与状态 |
277
278
  | `config log level LEVEL` | 设置日志级别(`DEBUG/INFO/WARNING/ERROR/CRITICAL`) |
@@ -1,3 +1,3 @@
1
1
  """CLI harness for Mingdao HAP (hap-cli)."""
2
2
 
3
- __version__ = "0.8.24"
3
+ __version__ = "0.8.25"
@@ -320,7 +320,13 @@ def chat_send_to_one(ctx, to, message, files, dry_run):
320
320
 
321
321
  @chat.command("send-to-group")
322
322
  @click.option("--group-id", "-g", required=True, help="Target group ID")
323
- @click.option("--message", "-m", required=True, help="Message content")
323
+ @click.option("--message", "-m", default="", help="Message text")
324
+ @click.option(
325
+ "--file", "-f", "files", multiple=True,
326
+ help="Attach a file: a local path or an http(s) address. Repeat for "
327
+ "several. The file arrives as an attachment members can open at "
328
+ "any time, not as a link that stops working.",
329
+ )
324
330
  @click.option(
325
331
  "--at", "at_account_ids", multiple=True,
326
332
  help="Account ID to @-mention (can repeat). Sends a separate "
@@ -331,19 +337,61 @@ def chat_send_to_one(ctx, to, message, files, dry_run):
331
337
  "--at-all", is_flag=True, default=False,
332
338
  help="@-mention everyone in the group.",
333
339
  )
340
+ @click.option(
341
+ "--dry-run", is_flag=True, default=False,
342
+ help="Show what would be sent, without uploading or sending anything.",
343
+ )
334
344
  @pass_context
335
- def chat_send_to_group(ctx, group_id, message, at_account_ids, at_all):
336
- """Send a plain-text message to a group chat."""
345
+ def chat_send_to_group(ctx, group_id, message, files, at_account_ids, at_all, dry_run):
346
+ """Send a message, optionally with files, to a group chat.
347
+
348
+ \b
349
+ hap chat send-to-group -g <groupId> -m "spec attached" -f ./spec.pdf
350
+
351
+ Text and each file arrive as separate messages, in that order. An
352
+ @-mention applies to the text line, so it needs --message.
353
+ """
354
+ if not message and not files:
355
+ ctx.handle_error(click.UsageError(
356
+ "Provide --message, --file, or both."
357
+ ))
358
+ return
359
+ sources = upload_mod.source_descriptors(list(files))
360
+ at_ids = list(at_account_ids) if at_account_ids else None
361
+ if dry_run:
362
+ ctx.output(
363
+ {"group_id": group_id, "message": message, "files": list(files),
364
+ "at": at_ids or [], "at_all": at_all, "sent": False},
365
+ lambda d: click.echo(
366
+ "Dry run — nothing sent."
367
+ + f"\n Group: {group_id}"
368
+ + (f"\n Message: {message}" if message else "")
369
+ + "".join(f"\n File: {f}" for f in files)
370
+ + (f"\n @: {', '.join(at_ids)}" if at_ids else "")
371
+ + ("\n @: everyone" if at_all else "")
372
+ ),
373
+ )
374
+ return
337
375
  try:
338
376
  session = ctx.get_session()
339
- data = chat_mod.send_group_message(
340
- session,
341
- group_id,
342
- message,
343
- at_account_ids=list(at_account_ids) if at_account_ids else None,
344
- at_all=at_all,
377
+ if not sources:
378
+ data = chat_mod.send_group_message(
379
+ session, group_id, message,
380
+ at_account_ids=at_ids, at_all=at_all,
381
+ )
382
+ ctx.output(data, lambda d: click.echo("Message sent."))
383
+ return
384
+ descriptors = chat_mod.upload_chat_files(session, sources)
385
+ acks = chat_mod.send_group_files(
386
+ session, group_id, descriptors, message,
387
+ at_account_ids=at_ids, at_all=at_all,
388
+ )
389
+ ctx.output(
390
+ {"sent": len(acks), "files": len(descriptors), "acks": acks},
391
+ lambda d: click.echo(
392
+ f"Sent {len(descriptors)} file(s) to the group."
393
+ ),
345
394
  )
346
- ctx.output(data, lambda d: click.echo("Message sent."))
347
395
  except Exception as e:
348
396
  ctx.handle_error(e)
349
397
 
@@ -2,12 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
5
6
  import os
6
7
  import re
7
8
  import time
8
9
  from pathlib import Path
9
10
 
10
11
  import click
12
+ from typing import Optional
11
13
 
12
14
  from hap_cli.context import pass_context
13
15
  from hap_cli.core.logger import (
@@ -20,6 +22,8 @@ from hap_cli.core.logger import (
20
22
  )
21
23
  from hap_cli.i18n import t
22
24
 
25
+ logger = logging.getLogger("hap_cli.config")
26
+
23
27
 
24
28
  # ── Shell completion helpers (moved from hap_cli.hap_cli) ────────────────
25
29
 
@@ -252,46 +256,119 @@ def config_update(ctx, mode):
252
256
  # ── `hap config language` ─────────────────────────────────────────────────
253
257
 
254
258
 
259
+ # Languages a user can pin here. The catalog and the resolution logic
260
+ # (HAP_LANG, config.json, login auto-detect) still understand every code in
261
+ # ``i18n.LANG_OPTIONS`` — zh-Hant / ja keep working and keep falling back
262
+ # to English where untranslated — they are only hidden from this picker.
263
+ _SELECTABLE_LANGS = ("en", "zh-Hans")
264
+ # Picker entry 0 / argument value "auto": drop the manual pin and follow
265
+ # the language of the HAP account (re-read now, and again at every login).
266
+ _AUTO = "auto"
267
+
268
+
269
+ def _detect_account_language(ctx) -> Optional[str]:
270
+ """Read the HAP account language of the active profile, or ``None``.
271
+
272
+ Uses the same lookup as ``hap auth whoami`` (``Global/GetGlobalMeta`` →
273
+ ``Account.lang``). Any failure — not logged in, expired token, network —
274
+ yields ``None`` so the caller can still switch to auto mode and let the
275
+ next login fill the language in.
276
+ """
277
+ from hap_cli.core import auth as auth_mod
278
+ from hap_cli.core.session import (
279
+ _normalize_login_url, load_profile, resolve_profile_name,
280
+ )
281
+ from hap_cli.i18n import lang_from_hap_account
282
+
283
+ try:
284
+ session = ctx.get_session(require_auth=False)
285
+ if not session.auth_token:
286
+ return None
287
+ prof = load_profile(resolve_profile_name(ctx.profile))
288
+ login_url = _normalize_login_url(
289
+ prof.get("login_url") or prof.get("server_url", "")
290
+ ) or "https://www.mingdao.com"
291
+ info = auth_mod.get_user_info(login_url, session.auth_token)
292
+ return lang_from_hap_account(str(info.get("lang") or ""))
293
+ except Exception as exc: # noqa: BLE001 — auto mode must not fail on lookup
294
+ logger.warning("Could not read the account language: %s", exc)
295
+ return None
296
+
297
+
255
298
  @config.command("language")
256
299
  @click.argument(
257
300
  "lang",
258
301
  required=False,
259
- type=click.Choice(["en", "zh-Hans", "zh-Hant", "ja"]),
302
+ type=click.Choice([_AUTO, *_SELECTABLE_LANGS]),
260
303
  )
261
304
  @pass_context
262
305
  def config_language(ctx, lang):
263
306
  """Switch the CLI display language.
264
307
 
265
- Without an argument, shows an interactive picker. Pass one of
266
- ``en`` / ``zh-Hans`` / ``zh-Hant`` / ``ja`` to set non-interactively.
308
+ Without an argument, shows an interactive picker. Pass ``en`` or
309
+ ``zh-Hans`` to pin a language, or ``auto`` (``0`` in the picker) to
310
+ follow the language of the HAP account you log in with.
267
311
 
268
- The chosen language is saved as a manual preference and pinned —
269
- subsequent ``hap auth login`` calls won't overwrite it from the
270
- user's HAP account language.
312
+ A pinned language is saved as a manual preference subsequent
313
+ ``hap auth login`` calls won't overwrite it. ``auto`` removes the pin
314
+ and re-reads the language from your current account right away.
271
315
  """
272
316
  from hap_cli.core.session import load_config, save_config
273
- from hap_cli.i18n import LANG_LABELS, LANG_OPTIONS, reset_language_cache
317
+ from hap_cli.i18n import DEFAULT_LANGUAGE, LANG_LABELS, reset_language_cache
274
318
 
275
319
  if lang is None:
276
320
  cfg = load_config() or {}
277
- current = cfg.get("language") or "en"
278
- click.echo("Choose a language / 选择语言 / 選擇語言 / 言語を選択:")
279
- for i, code in enumerate(LANG_OPTIONS, start=1):
280
- mark = " (current)" if code == current else ""
321
+ current = cfg.get("language") or DEFAULT_LANGUAGE
322
+ is_auto = cfg.get("language_source") != "manual"
323
+ click.echo("Choose a language / 选择语言:")
324
+ mark = " (current)" if is_auto else ""
325
+ click.echo(f" 0. Auto — follow your HAP account language [auto]{mark}")
326
+ for i, code in enumerate(_SELECTABLE_LANGS, start=1):
327
+ mark = " (current)" if (not is_auto and code == current) else ""
281
328
  click.echo(f" {i}. {LANG_LABELS[code]} [{code}]{mark}")
329
+ if is_auto or current not in _SELECTABLE_LANGS:
330
+ default_index = 0
331
+ else:
332
+ default_index = _SELECTABLE_LANGS.index(current) + 1
282
333
  choice = click.prompt(
283
- "Enter 1-4",
284
- type=click.IntRange(1, len(LANG_OPTIONS)),
285
- default=LANG_OPTIONS.index(current) + 1,
334
+ f"Enter 0-{len(_SELECTABLE_LANGS)}",
335
+ type=click.IntRange(0, len(_SELECTABLE_LANGS)),
336
+ default=default_index,
286
337
  )
287
- lang = LANG_OPTIONS[choice - 1]
338
+ lang = _AUTO if choice == 0 else _SELECTABLE_LANGS[choice - 1]
339
+
340
+ if lang == _AUTO:
341
+ detected = _detect_account_language(ctx)
342
+ save_config({
343
+ "language": detected or DEFAULT_LANGUAGE,
344
+ "language_source": "auto",
345
+ })
346
+ reset_language_cache()
347
+ if ctx.json_mode:
348
+ from hap_cli.utils.formatting import output_json
349
+ output_json({
350
+ "status": "ok", "mode": "auto",
351
+ "language": detected or DEFAULT_LANGUAGE,
352
+ "detected": bool(detected),
353
+ })
354
+ elif detected:
355
+ click.echo(
356
+ "Language set to: Auto — following your HAP account language "
357
+ f"({LANG_LABELS[detected]} [{detected}])"
358
+ )
359
+ else:
360
+ click.echo(
361
+ "Language set to: Auto — it will follow your HAP account "
362
+ "language after the next login."
363
+ )
364
+ return
288
365
 
289
366
  save_config({"language": lang, "language_source": "manual"})
290
367
  reset_language_cache()
291
368
  label = LANG_LABELS[lang]
292
369
  if ctx.json_mode:
293
370
  from hap_cli.utils.formatting import output_json
294
- output_json({"status": "ok", "language": lang, "label": label})
371
+ output_json({"status": "ok", "mode": "manual", "language": lang, "label": label})
295
372
  else:
296
373
  click.echo(f"Language set to: {label} [{lang}]")
297
374
 
@@ -182,11 +182,17 @@ def instance_submit(ctx, instance_id, work_id, opinion):
182
182
 
183
183
  @instance.command("revoke")
184
184
  @click.argument("instance_id")
185
- @click.option("--work-id", "-w", default="", help="Work item ID")
185
+ @click.option("--work-id", "-w", default="",
186
+ help="Work item ID of the initiating step. Looked up from the "
187
+ "flow automatically when omitted.")
186
188
  @click.option("--opinion", "-o", default="", help="Revocation reason")
187
189
  @pass_context
188
190
  def instance_revoke(ctx, instance_id, work_id, opinion):
189
- """Revoke/withdraw a submitted instance."""
191
+ """Revoke/withdraw a submitted instance.
192
+
193
+ Only the initiator can withdraw a flow, and only while it is still
194
+ running.
195
+ """
190
196
  try:
191
197
  session = ctx.get_session()
192
198
  data = inst_mod.revoke(session, instance_id, work_id, opinion)
@@ -240,13 +246,18 @@ def instance_retry(ctx, instance_id):
240
246
 
241
247
  @instance.command("restart")
242
248
  @click.argument("instance_id")
243
- @click.option("--work-id", "-w", default="", help="Work item ID")
249
+ @click.option("--work-id", "-w", default="",
250
+ help="Work item ID of the pending initiating step created by "
251
+ "the revoke. Looked up from the flow automatically when "
252
+ "omitted.")
244
253
  @pass_context
245
254
  def instance_restart(ctx, instance_id, work_id):
246
255
  """Re-initiate a flow the initiator has revoked.
247
256
 
248
257
  Use this after the initiator revoked (withdrew) a still-running flow to
249
- start it again. It does not apply to flows that already finished.
258
+ start it again. It requires the approval flow to allow re-initiating
259
+ after a revoke; the initiator then gets a new fill task to submit. It
260
+ does not apply to flows that already finished.
250
261
  """
251
262
  try:
252
263
  session = ctx.get_session()
@@ -438,6 +438,79 @@ def send_group_message(
438
438
  return ack_data
439
439
 
440
440
 
441
+ def send_group_files(
442
+ session: Session,
443
+ group_id: str,
444
+ descriptors: list[dict[str, Any]],
445
+ message: str = "",
446
+ at_account_ids: Optional[list[str]] = None,
447
+ at_all: bool = False,
448
+ ) -> list[Any]:
449
+ """Send already-uploaded files (and an optional line of text) to a group.
450
+
451
+ Group file messages ride the same socket.io event as group text —
452
+ ``'send group message'`` (``src/pages/chat/utils/socket.js``
453
+ ``Message.send`` GROUP branch) — with the same per-message shape the
454
+ 1-on-1 path uses (SendToolbar ``FileUploaded`` → ``onSendFileMsg({file,
455
+ type}, msg)`` → ``ChatPanelSession.sendMessage``), the only difference
456
+ being ``togroup`` instead of ``touser`` (ChatPanelSession/index.js:355).
457
+ Order mirrors the chat panel: the typed text first, then one message
458
+ per file, all over one connection.
459
+
460
+ An @-mention (``at_account_ids`` / ``at_all``) only makes sense on the
461
+ text line, so the ``'group shake'`` follow-up carries the text
462
+ message's ack id and is skipped when there is no text — same
463
+ best-effort contract as :func:`send_group_message`.
464
+
465
+ Returns the acks in send order (unwrapped from the ``(err, data)``
466
+ tuple python-socketio surfaces). Emitting is strict — a failure to
467
+ deliver raises rather than reporting a phantom success.
468
+ """
469
+ import uuid
470
+
471
+ if not group_id:
472
+ raise ValueError("group_id is required")
473
+ if not descriptors and not message:
474
+ raise ValueError("nothing to send: provide files, a message, or both")
475
+
476
+ emits: list[tuple[str, dict[str, Any]]] = []
477
+ if message:
478
+ emits.append(("send group message", {
479
+ "type": MSGTYPE_TEXT,
480
+ "msg": message,
481
+ "togroup": group_id,
482
+ "waitingid": str(uuid.uuid4()),
483
+ }))
484
+ for descriptor in descriptors:
485
+ msg_type, file_obj = _chat_file_message(descriptor)
486
+ emits.append(("send group message", {
487
+ "type": msg_type,
488
+ "msg": _chat_file_label(msg_type, file_obj["name"]),
489
+ "file": file_obj,
490
+ "togroup": group_id,
491
+ "waitingid": str(uuid.uuid4()),
492
+ }))
493
+ raw_acks = _socket_emit_batch(session, emits, strict=True)
494
+ acks = [
495
+ ack[1] if isinstance(ack, tuple) and len(ack) >= 2 else ack
496
+ for ack in raw_acks
497
+ ]
498
+
499
+ to_user: Any = None
500
+ if at_all:
501
+ to_user = "all"
502
+ elif at_account_ids:
503
+ to_user = list(at_account_ids)
504
+ if to_user and message:
505
+ text_ack = acks[0]
506
+ message_id = text_ack.get("id") if isinstance(text_ack, dict) else None
507
+ shake_payload: dict[str, Any] = {"gid": group_id, "toUser": to_user}
508
+ if message_id:
509
+ shake_payload["messageId"] = message_id
510
+ _socket_emit(session, "group shake", shake_payload, strict=False)
511
+ return acks
512
+
513
+
441
514
  def get_group_info(
442
515
  session: Session,
443
516
  group_id: str,