hap-cli 0.8.20__tar.gz → 0.8.22__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 (268) hide show
  1. {hap_cli-0.8.20 → hap_cli-0.8.22}/PKG-INFO +13 -4
  2. {hap_cli-0.8.20/hap_cli → hap_cli-0.8.22}/README.md +12 -3
  3. {hap_cli-0.8.20 → hap_cli-0.8.22/hap_cli}/README.md +12 -3
  4. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/README_CN.md +12 -3
  5. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/__init__.py +1 -1
  6. hap_cli-0.8.22/hap_cli/commands/attachment_cmd.py +44 -0
  7. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/chat_cmd.py +51 -5
  8. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/config_cmd.py +44 -0
  9. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/upload_cmd.py +13 -7
  10. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/action_spec_adapter.py +9 -3
  11. hap_cli-0.8.22/hap_cli/core/attachment.py +107 -0
  12. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/chat.py +208 -15
  13. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/flow_node.py +41 -18
  14. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/record.py +31 -2
  15. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/session.py +9 -1
  16. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/update.py +39 -0
  17. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/upload.py +98 -20
  18. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/worksheet.py +38 -3
  19. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/hap_cli.py +2 -0
  20. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/i18n.py +64 -0
  21. hap_cli-0.8.22/hap_cli/tests/test_attachment_share.py +148 -0
  22. hap_cli-0.8.22/hap_cli/tests/test_chat_attachment_send.py +358 -0
  23. hap_cli-0.8.22/hap_cli/tests/test_content_language.py +120 -0
  24. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_core.py +22 -6
  25. hap_cli-0.8.22/hap_cli/tests/test_no_workflow_unsupported.py +92 -0
  26. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +20 -10
  27. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_record_v3_semantic_cli.py +3 -1
  28. hap_cli-0.8.22/hap_cli/tests/test_update_mode.py +90 -0
  29. hap_cli-0.8.22/hap_cli/tests/test_upload_host_resolution.py +93 -0
  30. hap_cli-0.8.22/hap_cli/tests/test_workflow_read_write_keys.py +71 -0
  31. hap_cli-0.8.22/hap_cli/tests/test_worksheet_create_section.py +91 -0
  32. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +4 -0
  33. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli.egg-info/PKG-INFO +13 -4
  34. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli.egg-info/SOURCES.txt +10 -0
  35. {hap_cli-0.8.20 → hap_cli-0.8.22}/MANIFEST.in +0 -0
  36. {hap_cli-0.8.20 → hap_cli-0.8.22}/app_live_tests/__init__.py +0 -0
  37. {hap_cli-0.8.20 → hap_cli-0.8.22}/app_live_tests/__main__.py +0 -0
  38. {hap_cli-0.8.20 → hap_cli-0.8.22}/app_live_tests/config.py +0 -0
  39. {hap_cli-0.8.20 → hap_cli-0.8.22}/app_live_tests/harness.py +0 -0
  40. {hap_cli-0.8.20 → hap_cli-0.8.22}/app_live_tests/smoke.py +0 -0
  41. {hap_cli-0.8.20 → hap_cli-0.8.22}/app_live_tests/state.py +0 -0
  42. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/__main__.py +0 -0
  43. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/__init__.py +0 -0
  44. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/app_cmd.py +0 -0
  45. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/app_creator_cmd.py +0 -0
  46. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/app_editor_cmd.py +0 -0
  47. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/auth_cmd.py +0 -0
  48. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/calendar_cmd.py +0 -0
  49. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/chart_cmd.py +0 -0
  50. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/chatbot_cmd.py +0 -0
  51. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/contact_cmd.py +0 -0
  52. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/department_cmd.py +0 -0
  53. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/group_cmd.py +0 -0
  54. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/icon_cmd.py +0 -0
  55. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/instance_cmd.py +0 -0
  56. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/node_cmd.py +0 -0
  57. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/optionset_cmd.py +0 -0
  58. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/page_cmd.py +0 -0
  59. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/post_cmd.py +0 -0
  60. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/record_cmd.py +0 -0
  61. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/region_cmd.py +0 -0
  62. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/role_cmd.py +0 -0
  63. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/update_cmd.py +0 -0
  64. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/v3_registry.py +0 -0
  65. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/workflow_cmd.py +0 -0
  66. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/commands/worksheet_cmd.py +0 -0
  67. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/context.py +0 -0
  68. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/__init__.py +0 -0
  69. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app.py +0 -0
  70. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/__init__.py +0 -0
  71. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  72. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  73. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  74. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/charts.py +0 -0
  75. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/cleanup.py +0 -0
  76. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/compiler.py +0 -0
  77. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/config.py +0 -0
  78. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  79. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/errors.py +0 -0
  80. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  81. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/executor.py +0 -0
  82. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/fields.py +0 -0
  83. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/hap.py +0 -0
  84. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/merge.py +0 -0
  85. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  86. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/recording/console.py +0 -0
  87. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  88. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  89. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/recording/report.py +0 -0
  90. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/schema.py +0 -0
  91. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  92. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  93. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/cli.py +0 -0
  94. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/executor.py +0 -0
  95. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  96. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  97. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  98. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  99. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/seed/template.py +0 -0
  100. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/selftest.py +0 -0
  101. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/smoke.py +0 -0
  102. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/steps.py +0 -0
  103. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/store.py +0 -0
  104. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/validate.py +0 -0
  105. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  106. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/auth.py +0 -0
  107. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/calendar_mod.py +0 -0
  108. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/chart.py +0 -0
  109. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/chatbot.py +0 -0
  110. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/cli_access.py +0 -0
  111. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/contact.py +0 -0
  112. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/control_type_codes.py +0 -0
  113. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/department.py +0 -0
  114. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/__init__.py +0 -0
  115. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  116. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  117. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/apply.py +0 -0
  118. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/componentlower.py +0 -0
  119. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/editspec.py +0 -0
  120. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/errors.py +0 -0
  121. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/fieldlower.py +0 -0
  122. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  123. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/models.py +0 -0
  124. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/ops.py +0 -0
  125. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/planner.py +0 -0
  126. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/editor/reader.py +0 -0
  127. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/field_normalizer.py +0 -0
  128. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/field_spec_adapter.py +0 -0
  129. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/filter_translator.py +0 -0
  130. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/global_meta.py +0 -0
  131. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/group.py +0 -0
  132. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/icon_index.py +0 -0
  133. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/instance.py +0 -0
  134. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/logger.py +0 -0
  135. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/optionset.py +0 -0
  136. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/page.py +0 -0
  137. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/post.py +0 -0
  138. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/response_crypto.py +0 -0
  139. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/role.py +0 -0
  140. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/role_perm_builder.py +0 -0
  141. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/token_crypto.py +0 -0
  142. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/v3/__init__.py +0 -0
  143. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/v3/dispatcher.py +0 -0
  144. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/v3/render.py +0 -0
  145. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/v3/schema.py +0 -0
  146. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/view_spec_adapter.py +0 -0
  147. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/workflow.py +0 -0
  148. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/workflow_node_dsl.py +0 -0
  149. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/core/worksheet_templates.py +0 -0
  150. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/locale/__init__.py +0 -0
  151. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/locale/messages.json +0 -0
  152. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/locale/messages.schema.json +0 -0
  153. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/__init__.py +0 -0
  154. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/conftest.py +0 -0
  155. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_app_creator.py +0 -0
  156. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_app_editor.py +0 -0
  157. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_app_export_usage.py +0 -0
  158. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_auth_prerelease.py +0 -0
  159. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_bug_036430.py +0 -0
  160. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_bug_036750.py +0 -0
  161. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_chart.py +0 -0
  162. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_cli_access.py +0 -0
  163. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_config_cmd.py +0 -0
  164. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  165. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  166. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_full_e2e.py +0 -0
  167. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_global_meta.py +0 -0
  168. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_i18n.py +0 -0
  169. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_instance_history_cli.py +0 -0
  170. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration.py +0 -0
  171. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_approval.py +0 -0
  172. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  173. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_calendar.py +0 -0
  174. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_destructive.py +0 -0
  175. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_misc.py +0 -0
  176. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_post.py +0 -0
  177. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_social.py +0 -0
  178. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_v3.py +0 -0
  179. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_workflow.py +0 -0
  180. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  181. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  182. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_multi_profile.py +0 -0
  183. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  184. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_org_id_cli.py +0 -0
  185. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_org_id_docs.py +0 -0
  186. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_parameter_conventions.py +0 -0
  187. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  188. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_record_field_alias.py +0 -0
  189. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  190. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  191. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_update.py +0 -0
  192. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  193. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  194. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  195. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_v3_session.py +0 -0
  196. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  197. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  198. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  199. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  200. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  201. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/utils/__init__.py +0 -0
  202. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/utils/formatting.py +0 -0
  203. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/utils/options.py +0 -0
  204. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli/utils/parameter_mapping.py +0 -0
  205. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli.egg-info/dependency_links.txt +0 -0
  206. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli.egg-info/entry_points.txt +0 -0
  207. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli.egg-info/requires.txt +0 -0
  208. {hap_cli-0.8.20 → hap_cli-0.8.22}/hap_cli.egg-info/top_level.txt +0 -0
  209. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/__init__.py +0 -0
  210. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/__main__.py +0 -0
  211. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/_wftest.py +0 -0
  212. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/charts.py +0 -0
  213. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/cleanup.py +0 -0
  214. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/compiler.py +0 -0
  215. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/config.py +0 -0
  216. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/errors.py +0 -0
  217. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/executor.py +0 -0
  218. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/fields.py +0 -0
  219. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/hap.py +0 -0
  220. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/recording/__init__.py +0 -0
  221. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/recording/console.py +0 -0
  222. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/recording/jsonl.py +0 -0
  223. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/recording/mirror.py +0 -0
  224. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/recording/report.py +0 -0
  225. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/schema.py +0 -0
  226. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/seed/__init__.py +0 -0
  227. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/seed/cli.py +0 -0
  228. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/seed/executor.py +0 -0
  229. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/seed/template.py +0 -0
  230. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/selftest.py +0 -0
  231. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/smoke.py +0 -0
  232. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/step.py +0 -0
  233. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/steps.py +0 -0
  234. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/store.py +0 -0
  235. {hap_cli-0.8.20 → hap_cli-0.8.22}/live_tests/workflow_dsl.py +0 -0
  236. {hap_cli-0.8.20 → hap_cli-0.8.22}/setup.cfg +0 -0
  237. {hap_cli-0.8.20 → hap_cli-0.8.22}/setup.py +0 -0
  238. {hap_cli-0.8.20 → hap_cli-0.8.22}/skills/cli/hap-cli-app-editor/scripts/editspec/component.schema.json +0 -0
  239. {hap_cli-0.8.20 → hap_cli-0.8.22}/skills/cli/hap-cli-app-editor/scripts/editspec/custom-action.schema.json +0 -0
  240. {hap_cli-0.8.20 → hap_cli-0.8.22}/skills/cli/hap-cli-app-editor/scripts/editspec/envelope.schema.json +0 -0
  241. {hap_cli-0.8.20 → hap_cli-0.8.22}/skills/cli/hap-cli-app-editor/scripts/editspec/field.schema.json +0 -0
  242. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/INDEX.json +0 -0
  243. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  244. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  245. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/create_optionset.yaml +0 -0
  246. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/create_role.yaml +0 -0
  247. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  248. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/delete_record.yaml +0 -0
  249. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/delete_role.yaml +0 -0
  250. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  251. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_app_info.yaml +0 -0
  252. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  253. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  254. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  255. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_record_details.yaml +0 -0
  256. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  257. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_record_list.yaml +0 -0
  258. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  259. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  260. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  261. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  262. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_regions.yaml +0 -0
  263. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_role_details.yaml +0 -0
  264. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/get_role_list.yaml +0 -0
  265. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  266. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  267. {hap_cli-0.8.20 → hap_cli-0.8.22}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  268. {hap_cli-0.8.20 → hap_cli-0.8.22}/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.20
3
+ Version: 0.8.22
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -272,7 +272,7 @@ discover every subcommand.
272
272
  | `chat list` | Recent sessions |
273
273
  | `chat messages` | 1-on-1 / group / inbox messages |
274
274
  | `chat files` | Files shared in a chat |
275
- | `chat send-to-one` | Send a plain-text message to one or more users (1-on-1) |
275
+ | `chat send-to-one` | Send a message, optionally with files (`--file`), to one or more users (1-on-1) |
276
276
  | `chat send-to-group` | Send a plain-text message to a group chat |
277
277
  | `chat card-detail` | Resolve embedded card references |
278
278
 
@@ -298,13 +298,21 @@ discover every subcommand.
298
298
  | `icon search` | Search icons by Chinese/English keyword (OR across terms, more matches rank higher); fewer than 10 matches get random suggestions appended; no keyword lists all |
299
299
  | `icon list [-n SIZE] [-p PAGE]` | List icons page by page with keywords (50 per page by default) |
300
300
 
301
+ ### attachment — stored files
302
+
303
+ | Command | Description |
304
+ |---|---|
305
+ | `attachment share ADDRESS` | Create a link to a stored file that you can pass on (`--hours`, default 48) |
306
+
307
+ > ADDRESS is the file's link — one that has already stopped working is fine, paste it as-is — or just the path part of it. The file itself does not expire, only the link does, so a dead link can always be revived.
308
+
301
309
  ### upload — file upload
302
310
 
303
311
  | Command | Description |
304
312
  |---|---|
305
- | `upload [SOURCES...]` | Upload local paths or http(s) URLs to the file store; prints neutral uploaded-file descriptors (fileID/key/url/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
313
+ | `upload [SOURCES...]` | Upload local paths or http(s) URLs to the file store; prints neutral uploaded-file descriptors (fileID/key/url/urlExpiresAt/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
306
314
 
307
- > A cross-feature capability — the same descriptors can be attached to a worksheet Attachment field, a chat message, a post, a calendar event, or a comment. Use `--files '[{"name":"cover.png","url":"https://..."}]'` to set display names; `--worksheet-id` / `--app-id` refine upload-token routing.
315
+ > A cross-feature capability — the same descriptors can be attached to a worksheet Attachment field, a post, a calendar event, or a comment. The returned `url` is a temporary address for your own immediate use and stops working at `urlExpiresAt`; never send it to anyone. To share a file, attach it where it belongs — e.g. `hap chat send-to-one --file <path>`. Use `--files '[{"name":"cover.png","url":"https://..."}]'` to set display names; `--worksheet-id` / `--app-id` refine upload-token routing.
308
316
 
309
317
  ### config — local CLI settings
310
318
 
@@ -313,6 +321,7 @@ discover every subcommand.
313
321
  | `config show` | Display the current configuration summary |
314
322
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
315
323
  | `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
324
+ | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
316
325
  | `config log on / off / status` | Toggle and inspect API logging |
317
326
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
318
327
  | `config log view [--lines N] [--no-follow]` | Tail the log file |
@@ -243,7 +243,7 @@ discover every subcommand.
243
243
  | `chat list` | Recent sessions |
244
244
  | `chat messages` | 1-on-1 / group / inbox messages |
245
245
  | `chat files` | Files shared in a chat |
246
- | `chat send-to-one` | Send a plain-text message to one or more users (1-on-1) |
246
+ | `chat send-to-one` | Send a message, optionally with files (`--file`), to one or more users (1-on-1) |
247
247
  | `chat send-to-group` | Send a plain-text message to a group chat |
248
248
  | `chat card-detail` | Resolve embedded card references |
249
249
 
@@ -269,13 +269,21 @@ discover every subcommand.
269
269
  | `icon search` | Search icons by Chinese/English keyword (OR across terms, more matches rank higher); fewer than 10 matches get random suggestions appended; no keyword lists all |
270
270
  | `icon list [-n SIZE] [-p PAGE]` | List icons page by page with keywords (50 per page by default) |
271
271
 
272
+ ### attachment — stored files
273
+
274
+ | Command | Description |
275
+ |---|---|
276
+ | `attachment share ADDRESS` | Create a link to a stored file that you can pass on (`--hours`, default 48) |
277
+
278
+ > ADDRESS is the file's link — one that has already stopped working is fine, paste it as-is — or just the path part of it. The file itself does not expire, only the link does, so a dead link can always be revived.
279
+
272
280
  ### upload — file upload
273
281
 
274
282
  | Command | Description |
275
283
  |---|---|
276
- | `upload [SOURCES...]` | Upload local paths or http(s) URLs to the file store; prints neutral uploaded-file descriptors (fileID/key/url/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
284
+ | `upload [SOURCES...]` | Upload local paths or http(s) URLs to the file store; prints neutral uploaded-file descriptors (fileID/key/url/urlExpiresAt/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
277
285
 
278
- > A cross-feature capability — the same descriptors can be attached to a worksheet Attachment field, a chat message, a post, a calendar event, or a comment. Use `--files '[{"name":"cover.png","url":"https://..."}]'` to set display names; `--worksheet-id` / `--app-id` refine upload-token routing.
286
+ > A cross-feature capability — the same descriptors can be attached to a worksheet Attachment field, a post, a calendar event, or a comment. The returned `url` is a temporary address for your own immediate use and stops working at `urlExpiresAt`; never send it to anyone. To share a file, attach it where it belongs — e.g. `hap chat send-to-one --file <path>`. Use `--files '[{"name":"cover.png","url":"https://..."}]'` to set display names; `--worksheet-id` / `--app-id` refine upload-token routing.
279
287
 
280
288
  ### config — local CLI settings
281
289
 
@@ -284,6 +292,7 @@ discover every subcommand.
284
292
  | `config show` | Display the current configuration summary |
285
293
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
286
294
  | `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
295
+ | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
287
296
  | `config log on / off / status` | Toggle and inspect API logging |
288
297
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
289
298
  | `config log view [--lines N] [--no-follow]` | Tail the log file |
@@ -243,7 +243,7 @@ discover every subcommand.
243
243
  | `chat list` | Recent sessions |
244
244
  | `chat messages` | 1-on-1 / group / inbox messages |
245
245
  | `chat files` | Files shared in a chat |
246
- | `chat send-to-one` | Send a plain-text message to one or more users (1-on-1) |
246
+ | `chat send-to-one` | Send a message, optionally with files (`--file`), to one or more users (1-on-1) |
247
247
  | `chat send-to-group` | Send a plain-text message to a group chat |
248
248
  | `chat card-detail` | Resolve embedded card references |
249
249
 
@@ -269,13 +269,21 @@ discover every subcommand.
269
269
  | `icon search` | Search icons by Chinese/English keyword (OR across terms, more matches rank higher); fewer than 10 matches get random suggestions appended; no keyword lists all |
270
270
  | `icon list [-n SIZE] [-p PAGE]` | List icons page by page with keywords (50 per page by default) |
271
271
 
272
+ ### attachment — stored files
273
+
274
+ | Command | Description |
275
+ |---|---|
276
+ | `attachment share ADDRESS` | Create a link to a stored file that you can pass on (`--hours`, default 48) |
277
+
278
+ > ADDRESS is the file's link — one that has already stopped working is fine, paste it as-is — or just the path part of it. The file itself does not expire, only the link does, so a dead link can always be revived.
279
+
272
280
  ### upload — file upload
273
281
 
274
282
  | Command | Description |
275
283
  |---|---|
276
- | `upload [SOURCES...]` | Upload local paths or http(s) URLs to the file store; prints neutral uploaded-file descriptors (fileID/key/url/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
284
+ | `upload [SOURCES...]` | Upload local paths or http(s) URLs to the file store; prints neutral uploaded-file descriptors (fileID/key/url/urlExpiresAt/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
277
285
 
278
- > A cross-feature capability — the same descriptors can be attached to a worksheet Attachment field, a chat message, a post, a calendar event, or a comment. Use `--files '[{"name":"cover.png","url":"https://..."}]'` to set display names; `--worksheet-id` / `--app-id` refine upload-token routing.
286
+ > A cross-feature capability — the same descriptors can be attached to a worksheet Attachment field, a post, a calendar event, or a comment. The returned `url` is a temporary address for your own immediate use and stops working at `urlExpiresAt`; never send it to anyone. To share a file, attach it where it belongs — e.g. `hap chat send-to-one --file <path>`. Use `--files '[{"name":"cover.png","url":"https://..."}]'` to set display names; `--worksheet-id` / `--app-id` refine upload-token routing.
279
287
 
280
288
  ### config — local CLI settings
281
289
 
@@ -284,6 +292,7 @@ discover every subcommand.
284
292
  | `config show` | Display the current configuration summary |
285
293
  | `config completion [bash\|zsh\|fish] [--install]` | Enable `<Tab>` completion |
286
294
  | `config language [LANG]` | Switch UI language (`en` / `zh-Hans` / `zh-Hant` / `ja`) |
295
+ | `config update [auto\|notify\|off]` | Self-upgrade mode; `notify` only reports, `off` never checks |
287
296
  | `config log on / off / status` | Toggle and inspect API logging |
288
297
  | `config log level LEVEL` | `DEBUG / INFO / WARNING / ERROR / CRITICAL` |
289
298
  | `config log view [--lines N] [--no-follow]` | Tail the log file |
@@ -226,7 +226,7 @@ hap --json worksheet record list WORKSHEET_ID
226
226
  | `chat list` | 最近会话 |
227
227
  | `chat messages` | 单聊 / 群聊 / 收件箱(系统、应用、工作流等) |
228
228
  | `chat files` | 聊天中分享的文件 |
229
- | `chat send` | 向一个或多个用户发送纯文本消息 |
229
+ | `chat send-to-one` | 向一个或多个用户发消息(单聊),可带附件 `--file`(文件作为聊天附件送达,不是链接) |
230
230
  | `chat group-info` | 群组信息 |
231
231
  | `chat card-detail` | 解析消息中嵌入的卡片引用 |
232
232
 
@@ -243,13 +243,21 @@ hap --json worksheet record list WORKSHEET_ID
243
243
  | `icon search` | 按中英文关键词搜索图标(多关键词取并集,命中越多排越前);命中不足 10 个时随机补足推荐;无关键词列出全部 |
244
244
  | `icon list [-n SIZE] [-p PAGE]` | 分页列出图标及其关键词(默认每页 50 个) |
245
245
 
246
+ ### attachment — 已存储文件
247
+
248
+ | 命令 | 说明 |
249
+ |---|---|
250
+ | `attachment share ADDRESS` | 为已存储的文件生成一条可以转给别人的链接(`--hours`,默认 48 小时) |
251
+
252
+ > ADDRESS 传文件的链接即可——**已经失效的链接照样能传,原样粘贴**——也可以只传路径部分。文件本身不会过期,过期的只是链接,所以失效链接随时可以重新生成。
253
+
246
254
  ### upload — 文件上传
247
255
 
248
256
  | 命令 | 说明 |
249
257
  |---|---|
250
- | `upload [SOURCES...]` | 上传本地路径或 http(s) 直链到文件存储,返回中性的「已上传文件描述符」(fileID/key/url/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
258
+ | `upload [SOURCES...]` | 上传本地路径或 http(s) 直链到文件存储,返回中性的「已上传文件描述符」(fileID/key/url/urlExpiresAt/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
251
259
 
252
- > 跨功能的通用能力——同一份描述符可用于工作表附件字段、聊天、动态、日程、评论等。用 `--files '[{"name":"封面.png","url":"https://..."}]'` 指定显示名;`--worksheet-id` / `--app-id` 优化上传 token 路由。
260
+ > 跨功能的通用能力——同一份描述符可用于工作表附件字段、动态、日程、评论等。返回的 `url` 只是给你自己临时取用的地址,到 `urlExpiresAt` 就失效,不要发给别人;要把文件给别人,请用对应的附件入口,例如 `hap chat send-to-one --file <路径>`。用 `--files '[{"name":"封面.png","url":"https://..."}]'` 指定显示名;`--worksheet-id` / `--app-id` 优化上传 token 路由。
253
261
 
254
262
  ### config — 本地 CLI 配置
255
263
 
@@ -258,6 +266,7 @@ hap --json worksheet record list WORKSHEET_ID
258
266
  | `config show` | 显示 CLI 配置摘要 |
259
267
  | `config completion [bash\|zsh\|fish] [--install]` | 启用 `<Tab>` 补全 |
260
268
  | `config language [LANG]` | 切换显示语言(`en` / `zh-Hans` / `zh-Hant` / `ja`) |
269
+ | `config update [auto\|notify\|off]` | 自动升级开关;`notify` 只提示不安装,`off` 不检查 |
261
270
  | `config log on / off / status` | 日志开关与状态 |
262
271
  | `config log level LEVEL` | 设置日志级别(`DEBUG/INFO/WARNING/ERROR/CRITICAL`) |
263
272
  | `config log view [--lines N] [--no-follow]` | 跟踪日志文件 |
@@ -1,3 +1,3 @@
1
1
  """CLI harness for Mingdao HAP (hap-cli)."""
2
2
 
3
- __version__ = "0.8.20"
3
+ __version__ = "0.8.22"
@@ -0,0 +1,44 @@
1
+ """CLI commands for files stored as attachments."""
2
+
3
+ import click
4
+
5
+ from hap_cli.context import pass_context
6
+ from hap_cli.core import attachment as attachment_mod
7
+ from hap_cli.utils.formatting import output_json
8
+ from hap_cli.i18n import t
9
+
10
+
11
+ @click.group("attachment", help=t("Files stored as attachments."))
12
+ def attachment():
13
+ """Attachment group."""
14
+
15
+
16
+ @attachment.command("share")
17
+ @click.argument("address")
18
+ @click.option(
19
+ "--hours", type=int, default=attachment_mod.DEFAULT_SHARE_HOURS,
20
+ show_default=True, help="How long the link stays usable.",
21
+ )
22
+ @pass_context
23
+ def attachment_share(ctx, address, hours):
24
+ """Create a link to a stored file that you can pass on.
25
+
26
+ \b
27
+ hap attachment share https://doc.example.com/doc/20260830/abc.html
28
+ hap attachment share doc/20260830/abc.html --hours 168
29
+
30
+ ADDRESS is the file's link — a link that has already stopped working
31
+ is fine, paste it as-is — or just the path part of it.
32
+ """
33
+ try:
34
+ session = ctx.get_session()
35
+ data = attachment_mod.share_url(session, address, hours=hours)
36
+ ctx.output(data, _human)
37
+ except Exception as e:
38
+ ctx.handle_error(e)
39
+
40
+
41
+ def _human(d):
42
+ click.echo(d["url"])
43
+ if d.get("expiresAt"):
44
+ click.echo(f"Usable until {d['expiresAt']}")
@@ -6,6 +6,7 @@ import click
6
6
 
7
7
  from hap_cli.context import pass_context
8
8
  from hap_cli.core import chat as chat_mod
9
+ from hap_cli.core import upload as upload_mod
9
10
  from hap_cli.utils.formatting import output_json, output_table
10
11
  from hap_cli.i18n import t
11
12
 
@@ -251,19 +252,64 @@ def chat_messages(
251
252
 
252
253
  @chat.command("send-to-one")
253
254
  @click.option("--to", "-t", multiple=True, help="Target user account ID (can repeat)")
254
- @click.option("--message", "-m", required=True, help="Message content")
255
+ @click.option("--message", "-m", default="", help="Message text")
256
+ @click.option(
257
+ "--file", "-f", "files", multiple=True,
258
+ help="Attach a file: a local path or an http(s) address. Repeat for "
259
+ "several. The file arrives as an attachment the recipient can "
260
+ "open at any time, not as a link that stops working.",
261
+ )
262
+ @click.option(
263
+ "--dry-run", is_flag=True, default=False,
264
+ help="Show what would be sent, without uploading or sending anything.",
265
+ )
255
266
  @pass_context
256
- def chat_send_to_one(ctx, to, message):
257
- """Send a plain-text message to one or more users (1-on-1)."""
267
+ def chat_send_to_one(ctx, to, message, files, dry_run):
268
+ """Send a message, optionally with files, to one or more users (1-on-1).
269
+
270
+ \b
271
+ hap chat send-to-one -t <accountId> -m "spec attached" -f ./spec.pdf
272
+
273
+ Text and each file arrive as separate messages, in that order. You
274
+ cannot message yourself — your own account is skipped.
275
+ """
258
276
  if not to:
259
277
  ctx.handle_error(click.UsageError(
260
278
  "Provide at least one --to <accountId>."
261
279
  ))
262
280
  return
281
+ if not message and not files:
282
+ ctx.handle_error(click.UsageError(
283
+ "Provide --message, --file, or both."
284
+ ))
285
+ return
286
+ sources = upload_mod.source_descriptors(list(files))
287
+ if dry_run:
288
+ ctx.output(
289
+ {"to": list(to), "message": message, "files": list(files),
290
+ "sent": False},
291
+ lambda d: click.echo(
292
+ "Dry run — nothing sent."
293
+ + f"\n To: {', '.join(to)}"
294
+ + (f"\n Message: {message}" if message else "")
295
+ + "".join(f"\n File: {f}" for f in files)
296
+ ),
297
+ )
298
+ return
263
299
  try:
264
300
  session = ctx.get_session()
265
- data = chat_mod.send_message(session, list(to), message)
266
- ctx.output(data, lambda d: click.echo("Message sent."))
301
+ if not sources:
302
+ data = chat_mod.send_message(session, list(to), message)
303
+ ctx.output(data, lambda d: click.echo("Message sent."))
304
+ return
305
+ descriptors = chat_mod.upload_chat_files(session, sources)
306
+ acks = chat_mod.send_files(session, list(to), descriptors, message)
307
+ ctx.output(
308
+ {"sent": len(acks), "files": len(descriptors), "acks": acks},
309
+ lambda d: click.echo(
310
+ f"Sent {len(descriptors)} file(s) to {len(to)} recipient(s)."
311
+ ),
312
+ )
267
313
  except Exception as e:
268
314
  ctx.handle_error(e)
269
315
 
@@ -122,6 +122,8 @@ def config_completion(shell, install):
122
122
  @pass_context
123
123
  def config_show(ctx):
124
124
  """Display current harness configuration."""
125
+ from hap_cli.core.update import get_update_mode
126
+
125
127
  log_cfg = load_logging_config()
126
128
  data = {
127
129
  "logging": {
@@ -129,6 +131,7 @@ def config_show(ctx):
129
131
  "level": log_cfg["level"],
130
132
  "file": str(resolve_log_path(log_cfg)),
131
133
  },
134
+ "update": get_update_mode(),
132
135
  }
133
136
 
134
137
  def _human(d):
@@ -136,6 +139,7 @@ def config_show(ctx):
136
139
  click.echo(f" enabled : {d['logging']['enabled']}")
137
140
  click.echo(f" level : {d['logging']['level']}")
138
141
  click.echo(f" file : {d['logging']['file']}")
142
+ click.echo(f"update : {d['update']}")
139
143
 
140
144
  ctx.output(data, _human)
141
145
 
@@ -205,6 +209,46 @@ def log_status(ctx):
205
209
  _echo_log_status(ctx, cfg)
206
210
 
207
211
 
212
+ # ── `hap config update` ───────────────────────────────────────────────────
213
+
214
+
215
+ @config.command("update")
216
+ @click.argument(
217
+ "mode", required=False, type=click.Choice(["auto", "notify", "off"]),
218
+ )
219
+ @pass_context
220
+ def config_update(ctx, mode):
221
+ """Control whether hap-cli upgrades itself.
222
+
223
+ \b
224
+ auto check hourly and install new versions (default)
225
+ notify check hourly and tell you, but never install
226
+ off never check
227
+
228
+ Use ``notify`` or ``off`` to pin the version in scripted or
229
+ production environments. Without an argument, shows the current
230
+ setting. ``hap update`` still upgrades on demand in every mode.
231
+ """
232
+ from hap_cli.core.session import load_config, save_config
233
+ from hap_cli.core.update import get_update_mode
234
+
235
+ if mode is None:
236
+ current = get_update_mode()
237
+ if ctx.json_mode:
238
+ from hap_cli.utils.formatting import output_json
239
+ output_json({"update": current})
240
+ else:
241
+ click.echo(f"Self-update: {current}")
242
+ return
243
+
244
+ save_config({"update": mode})
245
+ if ctx.json_mode:
246
+ from hap_cli.utils.formatting import output_json
247
+ output_json({"status": "ok", "update": mode})
248
+ else:
249
+ click.echo(f"Self-update set to: {mode}")
250
+
251
+
208
252
  # ── `hap config language` ─────────────────────────────────────────────────
209
253
 
210
254
 
@@ -10,7 +10,13 @@ from hap_cli.utils.formatting import output_json
10
10
  from hap_cli.i18n import t
11
11
 
12
12
 
13
- @click.command("upload", help=t("Upload file(s) to the file store."))
13
+ @click.command("upload", help=t(
14
+ "Upload file(s) to the file store.\n\n"
15
+ "The returned \"url\" is a temporary address for your own immediate "
16
+ "use and stops working at the moment shown in \"urlExpiresAt\". Do not "
17
+ "send it to anyone — to share a file, attach it where it belongs, "
18
+ "e.g. 'hap chat send-to-one --file <path>'."
19
+ ))
14
20
  @click.argument("sources", nargs=-1)
15
21
  @click.option("--files", "files_json", default="",
16
22
  help="JSON array of {name,url} or {name,path} descriptors "
@@ -33,18 +39,18 @@ def upload(ctx, sources, files_json, worksheet_id, app_id):
33
39
  originalFileName/fileSize) is returned. Consumers wrap these for their
34
40
  own attachment shape — e.g. a worksheet Attachment field, a chat
35
41
  message, a post, a comment, or a calendar event.
42
+
43
+ The returned "url" is a temporary address for your own immediate use
44
+ and stops working at the moment shown in "urlExpiresAt". Do not send
45
+ it to anyone — to share a file, attach it where it belongs, e.g.
46
+ 'hap chat send-to-one --file <path>'.
36
47
  """
37
48
  try:
38
49
  session = ctx.get_session()
39
50
  if files_json:
40
51
  files = json.loads(files_json)
41
52
  else:
42
- files = []
43
- for s in sources:
44
- if s.startswith("http://") or s.startswith("https://"):
45
- files.append({"url": s})
46
- else:
47
- files.append({"path": s})
53
+ files = upload_mod.source_descriptors(sources)
48
54
  if not files:
49
55
  raise click.UsageError("provide SOURCES or --files")
50
56
  descriptors = upload_mod.upload_files(
@@ -110,9 +110,15 @@ def build_button_payload(spec: dict[str, Any]) -> dict[str, Any]:
110
110
  # in the UI; for the dedicated 二次确认 button use clickType 2.
111
111
  if out["clickType"] == 1 and confirm:
112
112
  out["clickType"] = 2
113
- out["confirmMsg"] = confirm_msg or "你确认执行此操作吗?"
114
- out["sureName"] = spec.get("sureName", "确认")
115
- out["cancelName"] = spec.get("cancelName", "取消")
113
+ # Dialog text is stored on the button itself, so it must be
114
+ # written in the application's language, not ours. Follows the
115
+ # active CLI language; an explicit value always wins.
116
+ from hap_cli.i18n import content_default
117
+
118
+ out["confirmMsg"] = confirm_msg or content_default("button.confirmMsg")
119
+ out["sureName"] = spec.get("sureName") or content_default("button.sure")
120
+ out["cancelName"] = (
121
+ spec.get("cancelName") or content_default("button.cancel"))
116
122
 
117
123
  # enableWhen → filters (reuse the shared filter translator).
118
124
  enable_when = spec.get("enableWhen")
@@ -0,0 +1,107 @@
1
+ """Attachments in the file store: shareable links for stored files.
2
+
3
+ The address handed out at upload time is a private-bucket signature that
4
+ expires within the hour (``upload.url_expires_at``). The file behind it
5
+ does not expire — the object path *is* the URL path — so a dead link is
6
+ re-signable at any time.
7
+
8
+ ``Attachment/GetShareLocalAttachmentUrl`` is what the web UI's
9
+ "分享附件" action calls: pd-openweb
10
+ src/components/shareAttachment/toMobile.js:121 passes
11
+ ``{filePath: file.qiniuPath, hours: 48}``, where ``qiniuPath`` is built
12
+ as ``window.config.FilePath + file.key``
13
+ (src/pages/chat/components/Message/MessageToolbar/index.js:222).
14
+
15
+ That prefix is not decorative. Verified live on 2026-08-31, the server
16
+ accepts the ``config.FilePath`` form and nothing else — a bare key, a
17
+ ``documentHost``-prefixed URL, or the original signed URL with its query
18
+ intact each come back as an empty string with no error state. So this
19
+ module normalizes any address the user has to hand down to a key and
20
+ rebuilds the one prefix that works.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import logging
25
+ from typing import Any, Optional
26
+ from urllib.parse import urlparse
27
+
28
+ from hap_cli.core.session import APIError, Session, SessionError
29
+
30
+ logger = logging.getLogger("hap_cli.attachment")
31
+
32
+ # What the web UI uses when it shares an attachment (toMobile.js:123).
33
+ DEFAULT_SHARE_HOURS = 48
34
+
35
+
36
+ def store_key(address: str) -> str:
37
+ """Reduce any form of a stored file's address to its store key.
38
+
39
+ Accepts a full URL on any of the store's hosts (with or without the
40
+ ``?e=…&token=…`` signature still attached — a dead link pasted out of
41
+ a chat message is the common case), a leading-slash path, or a bare
42
+ key.
43
+ """
44
+ if not address or not address.strip():
45
+ raise ValueError("a file address or key is required")
46
+ address = address.strip()
47
+ if "://" in address:
48
+ address = urlparse(address).path
49
+ else:
50
+ # A bare key may still carry a query if it came from a copied link.
51
+ address = address.split("?", 1)[0]
52
+ return address.lstrip("/")
53
+
54
+
55
+ def _file_path_prefix(session: Session) -> str:
56
+ """The store prefix this endpoint requires in ``filePath``.
57
+
58
+ Bootstrap publishes it as ``config.FilePath`` — the same value chat
59
+ file addresses are built from (``chat.chat_file_host``).
60
+ """
61
+ meta = session.get_global_meta()
62
+ for scope in (meta.get("config"), meta):
63
+ if isinstance(scope, dict) and scope.get("FilePath"):
64
+ return scope["FilePath"]
65
+ raise SessionError(
66
+ "This server did not publish a file address, so a link cannot be "
67
+ "created. Check that you are signed in to the right server, then "
68
+ "try again."
69
+ )
70
+
71
+
72
+ def share_url(
73
+ session: Session,
74
+ address: str,
75
+ *,
76
+ hours: int = DEFAULT_SHARE_HOURS,
77
+ ) -> dict[str, Any]:
78
+ """Create a fresh, time-limited link to a file already in the store.
79
+
80
+ ``address`` is anything that identifies the file: its current or
81
+ expired link, or its bare key. Returns
82
+ ``{url, expiresAt, hours, key}``.
83
+ """
84
+ from hap_cli.core.upload import _iso, url_expires_at
85
+
86
+ if hours <= 0:
87
+ raise ValueError("hours must be a positive number")
88
+ key = store_key(address)
89
+ prefix = _file_path_prefix(session)
90
+ url = session.api_call(
91
+ "Attachment", "GetShareLocalAttachmentUrl",
92
+ {"filePath": prefix + key, "hours": hours},
93
+ )
94
+ if not url or not isinstance(url, str):
95
+ # An unknown or unreachable file is answered with an empty string
96
+ # and a success state, so there is nothing to detect but this.
97
+ logger.warning("GetShareLocalAttachmentUrl returned %r for key=%s", url, key)
98
+ raise APIError(
99
+ "Could not create a link for that file. Check the address is "
100
+ "complete and that the file still exists."
101
+ )
102
+ return {
103
+ "url": url,
104
+ "expiresAt": _iso(url_expires_at(url)),
105
+ "hours": hours,
106
+ "key": key,
107
+ }