hap-cli 0.8.21__tar.gz → 0.8.23__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 (282) hide show
  1. {hap_cli-0.8.21 → hap_cli-0.8.23}/MANIFEST.in +2 -5
  2. {hap_cli-0.8.21 → hap_cli-0.8.23}/PKG-INFO +18 -4
  3. {hap_cli-0.8.21/hap_cli → hap_cli-0.8.23}/README.md +17 -3
  4. {hap_cli-0.8.21 → hap_cli-0.8.23/hap_cli}/README.md +17 -3
  5. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/README_CN.md +17 -3
  6. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/__init__.py +1 -1
  7. hap_cli-0.8.23/hap_cli/commands/attachment_cmd.py +44 -0
  8. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/auth_cmd.py +5 -1
  9. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/chat_cmd.py +56 -6
  10. hap_cli-0.8.23/hap_cli/commands/guide_cmd.py +36 -0
  11. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/record_cmd.py +8 -0
  12. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/upload_cmd.py +13 -7
  13. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/worksheet_cmd.py +6 -1
  14. hap_cli-0.8.23/hap_cli/core/attachment.py +107 -0
  15. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/chat.py +208 -15
  16. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/editspec.py +15 -20
  17. hap_cli-0.8.23/hap_cli/core/guide.py +176 -0
  18. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/upload.py +67 -7
  19. hap_cli-0.8.23/hap_cli/guide/en/messages.md +54 -0
  20. hap_cli-0.8.23/hap_cli/guide/en/overview.md +45 -0
  21. hap_cli-0.8.23/hap_cli/guide/en/record.md +289 -0
  22. hap_cli-0.8.23/hap_cli/guide/en/setup.md +99 -0
  23. hap_cli-0.8.23/hap_cli/guide/en/skills.md +48 -0
  24. hap_cli-0.8.23/hap_cli/guide/index.json +36 -0
  25. hap_cli-0.8.23/hap_cli/guide/zh-Hans/messages.md +52 -0
  26. hap_cli-0.8.23/hap_cli/guide/zh-Hans/overview.md +39 -0
  27. hap_cli-0.8.23/hap_cli/guide/zh-Hans/record.md +276 -0
  28. hap_cli-0.8.23/hap_cli/guide/zh-Hans/setup.md +91 -0
  29. hap_cli-0.8.23/hap_cli/guide/zh-Hans/skills.md +45 -0
  30. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/hap_cli.py +10 -1
  31. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/i18n.py +15 -0
  32. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_app_editor.py +35 -0
  33. hap_cli-0.8.23/hap_cli/tests/test_attachment_share.py +148 -0
  34. hap_cli-0.8.23/hap_cli/tests/test_chat_attachment_send.py +358 -0
  35. hap_cli-0.8.23/hap_cli/tests/test_guide.py +162 -0
  36. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli.egg-info/PKG-INFO +18 -4
  37. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli.egg-info/SOURCES.txt +22 -4
  38. {hap_cli-0.8.21 → hap_cli-0.8.23}/setup.py +7 -15
  39. {hap_cli-0.8.21 → hap_cli-0.8.23}/app_live_tests/__init__.py +0 -0
  40. {hap_cli-0.8.21 → hap_cli-0.8.23}/app_live_tests/__main__.py +0 -0
  41. {hap_cli-0.8.21 → hap_cli-0.8.23}/app_live_tests/config.py +0 -0
  42. {hap_cli-0.8.21 → hap_cli-0.8.23}/app_live_tests/harness.py +0 -0
  43. {hap_cli-0.8.21 → hap_cli-0.8.23}/app_live_tests/smoke.py +0 -0
  44. {hap_cli-0.8.21 → hap_cli-0.8.23}/app_live_tests/state.py +0 -0
  45. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/__main__.py +0 -0
  46. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/__init__.py +0 -0
  47. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/app_cmd.py +0 -0
  48. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/app_creator_cmd.py +0 -0
  49. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/app_editor_cmd.py +0 -0
  50. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/calendar_cmd.py +0 -0
  51. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/chart_cmd.py +0 -0
  52. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/chatbot_cmd.py +0 -0
  53. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/config_cmd.py +0 -0
  54. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/contact_cmd.py +0 -0
  55. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/department_cmd.py +0 -0
  56. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/group_cmd.py +0 -0
  57. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/icon_cmd.py +0 -0
  58. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/instance_cmd.py +0 -0
  59. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/node_cmd.py +0 -0
  60. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/optionset_cmd.py +0 -0
  61. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/page_cmd.py +0 -0
  62. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/post_cmd.py +0 -0
  63. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/region_cmd.py +0 -0
  64. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/role_cmd.py +0 -0
  65. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/update_cmd.py +0 -0
  66. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/v3_registry.py +0 -0
  67. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/commands/workflow_cmd.py +0 -0
  68. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/context.py +0 -0
  69. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/__init__.py +0 -0
  70. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/action_spec_adapter.py +0 -0
  71. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app.py +0 -0
  72. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/__init__.py +0 -0
  73. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  74. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  75. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  76. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/charts.py +0 -0
  77. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/cleanup.py +0 -0
  78. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/compiler.py +0 -0
  79. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/config.py +0 -0
  80. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  81. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/errors.py +0 -0
  82. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  83. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/executor.py +0 -0
  84. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/fields.py +0 -0
  85. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/hap.py +0 -0
  86. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/merge.py +0 -0
  87. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  88. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/recording/console.py +0 -0
  89. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  90. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  91. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/recording/report.py +0 -0
  92. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/schema.py +0 -0
  93. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  94. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  95. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/cli.py +0 -0
  96. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/executor.py +0 -0
  97. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  98. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  99. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  100. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  101. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/seed/template.py +0 -0
  102. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/selftest.py +0 -0
  103. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/smoke.py +0 -0
  104. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/steps.py +0 -0
  105. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/store.py +0 -0
  106. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/validate.py +0 -0
  107. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  108. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/auth.py +0 -0
  109. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/calendar_mod.py +0 -0
  110. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/chart.py +0 -0
  111. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/chatbot.py +0 -0
  112. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/cli_access.py +0 -0
  113. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/contact.py +0 -0
  114. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/control_type_codes.py +0 -0
  115. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/department.py +0 -0
  116. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/__init__.py +0 -0
  117. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  118. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  119. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/apply.py +0 -0
  120. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/componentlower.py +0 -0
  121. {hap_cli-0.8.21/skills/cli/hap-cli-app-editor/scripts → hap_cli-0.8.23/hap_cli/core/editor}/editspec/component.schema.json +0 -0
  122. {hap_cli-0.8.21/skills/cli/hap-cli-app-editor/scripts → hap_cli-0.8.23/hap_cli/core/editor}/editspec/custom-action.schema.json +0 -0
  123. {hap_cli-0.8.21/skills/cli/hap-cli-app-editor/scripts → hap_cli-0.8.23/hap_cli/core/editor}/editspec/envelope.schema.json +0 -0
  124. {hap_cli-0.8.21/skills/cli/hap-cli-app-editor/scripts → hap_cli-0.8.23/hap_cli/core/editor}/editspec/field.schema.json +0 -0
  125. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/errors.py +0 -0
  126. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/fieldlower.py +0 -0
  127. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  128. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/models.py +0 -0
  129. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/ops.py +0 -0
  130. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/planner.py +0 -0
  131. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/editor/reader.py +0 -0
  132. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/field_normalizer.py +0 -0
  133. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/field_spec_adapter.py +0 -0
  134. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/filter_translator.py +0 -0
  135. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/flow_node.py +0 -0
  136. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/global_meta.py +0 -0
  137. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/group.py +0 -0
  138. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/icon_index.py +0 -0
  139. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/instance.py +0 -0
  140. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/logger.py +0 -0
  141. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/optionset.py +0 -0
  142. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/page.py +0 -0
  143. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/post.py +0 -0
  144. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/record.py +0 -0
  145. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/response_crypto.py +0 -0
  146. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/role.py +0 -0
  147. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/role_perm_builder.py +0 -0
  148. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/session.py +0 -0
  149. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/token_crypto.py +0 -0
  150. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/update.py +0 -0
  151. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/v3/__init__.py +0 -0
  152. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/v3/dispatcher.py +0 -0
  153. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/v3/render.py +0 -0
  154. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/v3/schema.py +0 -0
  155. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/view_spec_adapter.py +0 -0
  156. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/workflow.py +0 -0
  157. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/workflow_node_dsl.py +0 -0
  158. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/worksheet.py +0 -0
  159. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/core/worksheet_templates.py +0 -0
  160. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/locale/__init__.py +0 -0
  161. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/locale/messages.json +0 -0
  162. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/locale/messages.schema.json +0 -0
  163. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/__init__.py +0 -0
  164. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/conftest.py +0 -0
  165. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_app_creator.py +0 -0
  166. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_app_export_usage.py +0 -0
  167. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_auth_prerelease.py +0 -0
  168. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_bug_036430.py +0 -0
  169. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_bug_036750.py +0 -0
  170. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_chart.py +0 -0
  171. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_cli_access.py +0 -0
  172. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_config_cmd.py +0 -0
  173. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_content_language.py +0 -0
  174. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  175. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_core.py +0 -0
  176. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  177. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_full_e2e.py +0 -0
  178. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_global_meta.py +0 -0
  179. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_i18n.py +0 -0
  180. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_instance_history_cli.py +0 -0
  181. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration.py +0 -0
  182. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_approval.py +0 -0
  183. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  184. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_calendar.py +0 -0
  185. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_destructive.py +0 -0
  186. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_misc.py +0 -0
  187. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_post.py +0 -0
  188. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_social.py +0 -0
  189. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_v3.py +0 -0
  190. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_workflow.py +0 -0
  191. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  192. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  193. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_multi_profile.py +0 -0
  194. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  195. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  196. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_org_id_cli.py +0 -0
  197. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_org_id_docs.py +0 -0
  198. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_parameter_conventions.py +0 -0
  199. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  200. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  201. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_record_field_alias.py +0 -0
  202. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  203. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  204. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  205. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_update.py +0 -0
  206. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_update_mode.py +0 -0
  207. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  208. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  209. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  210. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  211. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_v3_session.py +0 -0
  212. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  213. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  214. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  215. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  216. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  217. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  218. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  219. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  220. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/utils/__init__.py +0 -0
  221. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/utils/formatting.py +0 -0
  222. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/utils/options.py +0 -0
  223. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli/utils/parameter_mapping.py +0 -0
  224. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli.egg-info/dependency_links.txt +0 -0
  225. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli.egg-info/entry_points.txt +0 -0
  226. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli.egg-info/requires.txt +0 -0
  227. {hap_cli-0.8.21 → hap_cli-0.8.23}/hap_cli.egg-info/top_level.txt +0 -0
  228. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/__init__.py +0 -0
  229. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/__main__.py +0 -0
  230. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/_wftest.py +0 -0
  231. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/charts.py +0 -0
  232. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/cleanup.py +0 -0
  233. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/compiler.py +0 -0
  234. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/config.py +0 -0
  235. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/errors.py +0 -0
  236. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/executor.py +0 -0
  237. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/fields.py +0 -0
  238. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/hap.py +0 -0
  239. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/recording/__init__.py +0 -0
  240. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/recording/console.py +0 -0
  241. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/recording/jsonl.py +0 -0
  242. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/recording/mirror.py +0 -0
  243. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/recording/report.py +0 -0
  244. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/schema.py +0 -0
  245. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/seed/__init__.py +0 -0
  246. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/seed/cli.py +0 -0
  247. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/seed/executor.py +0 -0
  248. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/seed/template.py +0 -0
  249. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/selftest.py +0 -0
  250. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/smoke.py +0 -0
  251. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/step.py +0 -0
  252. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/steps.py +0 -0
  253. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/store.py +0 -0
  254. {hap_cli-0.8.21 → hap_cli-0.8.23}/live_tests/workflow_dsl.py +0 -0
  255. {hap_cli-0.8.21 → hap_cli-0.8.23}/setup.cfg +0 -0
  256. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/INDEX.json +0 -0
  257. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  258. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  259. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/create_optionset.yaml +0 -0
  260. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/create_role.yaml +0 -0
  261. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  262. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/delete_record.yaml +0 -0
  263. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/delete_role.yaml +0 -0
  264. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  265. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_app_info.yaml +0 -0
  266. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  267. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  268. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  269. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_record_details.yaml +0 -0
  270. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  271. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_record_list.yaml +0 -0
  272. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  273. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  274. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  275. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  276. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_regions.yaml +0 -0
  277. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_role_details.yaml +0 -0
  278. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/get_role_list.yaml +0 -0
  279. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  280. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  281. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  282. {hap_cli-0.8.21 → hap_cli-0.8.23}/sources/v3-api-schema/update_optionset.yaml +0 -0
@@ -11,8 +11,5 @@ include sources/v3-api-schema/INDEX.json
11
11
  include hap_cli/README.md
12
12
  include hap_cli/README_CN.md
13
13
  recursive-include hap_cli/locale *.json
14
-
15
- # edit-spec schemas are canonical in the hap-cli-app-editor skill; ship them
16
- # in the sdist so setup.py's build step can mirror them into
17
- # hap_cli/core/editor/editspec/ for the ``hap app-editor`` runtime loader.
18
- recursive-include skills/cli/hap-cli-app-editor/scripts/editspec *.json
14
+ include hap_cli/guide/index.json
15
+ recursive-include hap_cli/guide *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.8.21
3
+ Version: 0.8.23
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,27 @@ 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
+
309
+ ### guide — built-in usage guide
310
+
311
+ | Command | Description |
312
+ |---|---|
313
+ | `guide` | Built-in usage guide: what this version can do, and the value formats that are easy to get wrong (`hap guide <topic>` for detail) |
314
+
301
315
  ### upload — file upload
302
316
 
303
317
  | Command | Description |
304
318
  |---|---|
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) |
319
+ | `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
320
 
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.
321
+ > 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
322
 
309
323
  ### config — local CLI settings
310
324
 
@@ -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,27 @@ 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
+
280
+ ### guide — built-in usage guide
281
+
282
+ | Command | Description |
283
+ |---|---|
284
+ | `guide` | Built-in usage guide: what this version can do, and the value formats that are easy to get wrong (`hap guide <topic>` for detail) |
285
+
272
286
  ### upload — file upload
273
287
 
274
288
  | Command | Description |
275
289
  |---|---|
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) |
290
+ | `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
291
 
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.
292
+ > 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
293
 
280
294
  ### config — local CLI settings
281
295
 
@@ -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,27 @@ 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
+
280
+ ### guide — built-in usage guide
281
+
282
+ | Command | Description |
283
+ |---|---|
284
+ | `guide` | Built-in usage guide: what this version can do, and the value formats that are easy to get wrong (`hap guide <topic>` for detail) |
285
+
272
286
  ### upload — file upload
273
287
 
274
288
  | Command | Description |
275
289
  |---|---|
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) |
290
+ | `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
291
 
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.
292
+ > 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
293
 
280
294
  ### config — local CLI settings
281
295
 
@@ -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,27 @@ 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
+
254
+ ### guide — 内置使用指南
255
+
256
+ | 命令 | 说明 |
257
+ |---|---|
258
+ | `guide` | 内置使用指南:当前版本能做什么、哪些取值格式容易写错(`hap guide <主题>` 看细节) |
259
+
246
260
  ### upload — 文件上传
247
261
 
248
262
  | 命令 | 说明 |
249
263
  |---|---|
250
- | `upload [SOURCES...]` | 上传本地路径或 http(s) 直链到文件存储,返回中性的「已上传文件描述符」(fileID/key/url/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
264
+ | `upload [SOURCES...]` | 上传本地路径或 http(s) 直链到文件存储,返回中性的「已上传文件描述符」(fileID/key/url/urlExpiresAt/serverName/filePath/fileName/fileExt/originalFileName/fileSize) |
251
265
 
252
- > 跨功能的通用能力——同一份描述符可用于工作表附件字段、聊天、动态、日程、评论等。用 `--files '[{"name":"封面.png","url":"https://..."}]'` 指定显示名;`--worksheet-id` / `--app-id` 优化上传 token 路由。
266
+ > 跨功能的通用能力——同一份描述符可用于工作表附件字段、动态、日程、评论等。返回的 `url` 只是给你自己临时取用的地址,到 `urlExpiresAt` 就失效,不要发给别人;要把文件给别人,请用对应的附件入口,例如 `hap chat send-to-one --file <路径>`。用 `--files '[{"name":"封面.png","url":"https://..."}]'` 指定显示名;`--worksheet-id` / `--app-id` 优化上传 token 路由。
253
267
 
254
268
  ### config — 本地 CLI 配置
255
269
 
@@ -1,3 +1,3 @@
1
1
  """CLI harness for Mingdao HAP (hap-cli)."""
2
2
 
3
- __version__ = "0.8.21"
3
+ __version__ = "0.8.23"
@@ -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']}")
@@ -30,7 +30,11 @@ SERVER_HELP = (
30
30
  )
31
31
 
32
32
 
33
- @click.group(help=t('Manage authentication and session identity.'))
33
+ @click.group(help=t(
34
+ 'Manage authentication and session identity.\n\n'
35
+ 'Signing in, keeping several environments and accounts, and choosing the '
36
+ 'current organization are walked through in `hap guide setup`.'
37
+ ))
34
38
  def auth():
35
39
  pass
36
40
 
@@ -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
 
@@ -72,7 +73,11 @@ def _output_chat_list_table(rows):
72
73
  )
73
74
 
74
75
 
75
- @click.group(help=t('Chat and messaging management.'))
76
+ @click.group(help=t(
77
+ 'Chat and messaging management.\n\n'
78
+ 'To follow a notification back to the app record it belongs to, see '
79
+ '`hap guide messages`.'
80
+ ))
76
81
  def chat():
77
82
  pass
78
83
 
@@ -251,19 +256,64 @@ def chat_messages(
251
256
 
252
257
  @chat.command("send-to-one")
253
258
  @click.option("--to", "-t", multiple=True, help="Target user account ID (can repeat)")
254
- @click.option("--message", "-m", required=True, help="Message content")
259
+ @click.option("--message", "-m", default="", help="Message text")
260
+ @click.option(
261
+ "--file", "-f", "files", multiple=True,
262
+ help="Attach a file: a local path or an http(s) address. Repeat for "
263
+ "several. The file arrives as an attachment the recipient can "
264
+ "open at any time, not as a link that stops working.",
265
+ )
266
+ @click.option(
267
+ "--dry-run", is_flag=True, default=False,
268
+ help="Show what would be sent, without uploading or sending anything.",
269
+ )
255
270
  @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)."""
271
+ def chat_send_to_one(ctx, to, message, files, dry_run):
272
+ """Send a message, optionally with files, to one or more users (1-on-1).
273
+
274
+ \b
275
+ hap chat send-to-one -t <accountId> -m "spec attached" -f ./spec.pdf
276
+
277
+ Text and each file arrive as separate messages, in that order. You
278
+ cannot message yourself — your own account is skipped.
279
+ """
258
280
  if not to:
259
281
  ctx.handle_error(click.UsageError(
260
282
  "Provide at least one --to <accountId>."
261
283
  ))
262
284
  return
285
+ if not message and not files:
286
+ ctx.handle_error(click.UsageError(
287
+ "Provide --message, --file, or both."
288
+ ))
289
+ return
290
+ sources = upload_mod.source_descriptors(list(files))
291
+ if dry_run:
292
+ ctx.output(
293
+ {"to": list(to), "message": message, "files": list(files),
294
+ "sent": False},
295
+ lambda d: click.echo(
296
+ "Dry run — nothing sent."
297
+ + f"\n To: {', '.join(to)}"
298
+ + (f"\n Message: {message}" if message else "")
299
+ + "".join(f"\n File: {f}" for f in files)
300
+ ),
301
+ )
302
+ return
263
303
  try:
264
304
  session = ctx.get_session()
265
- data = chat_mod.send_message(session, list(to), message)
266
- ctx.output(data, lambda d: click.echo("Message sent."))
305
+ if not sources:
306
+ data = chat_mod.send_message(session, list(to), message)
307
+ ctx.output(data, lambda d: click.echo("Message sent."))
308
+ return
309
+ descriptors = chat_mod.upload_chat_files(session, sources)
310
+ acks = chat_mod.send_files(session, list(to), descriptors, message)
311
+ ctx.output(
312
+ {"sent": len(acks), "files": len(descriptors), "acks": acks},
313
+ lambda d: click.echo(
314
+ f"Sent {len(descriptors)} file(s) to {len(to)} recipient(s)."
315
+ ),
316
+ )
267
317
  except Exception as e:
268
318
  ctx.handle_error(e)
269
319
 
@@ -0,0 +1,36 @@
1
+ """CLI command for the built-in usage guide."""
2
+
3
+ import click
4
+
5
+ from hap_cli.context import pass_context
6
+ from hap_cli.core import guide as guide_mod
7
+ from hap_cli.i18n import t
8
+
9
+
10
+ @click.command("guide")
11
+ @click.argument("topic", required=False)
12
+ @pass_context
13
+ def guide(ctx, topic):
14
+ """How to use this tool: what it can do, and how to get the values right.
15
+
16
+ \b
17
+ hap guide what hap can do, and which topics go deeper
18
+ hap guide record writing records: what value each field type wants
19
+
20
+ Run this before composing commands — it describes the version of hap
21
+ installed on this machine, so it never disagrees with what you get.
22
+ """
23
+ try:
24
+ from hap_cli.hap_cli import cli as root
25
+
26
+ if topic:
27
+ body = guide_mod.render_topic(topic)
28
+ ctx.output({"topic": topic, "text": body},
29
+ lambda d: click.echo(d["text"]))
30
+ return
31
+ payload = guide_mod.overview_payload(root)
32
+ ctx.output(payload, lambda d: click.echo(d["text"]))
33
+ except guide_mod.UnknownTopic as e:
34
+ ctx.handle_error(e)
35
+ except Exception as e:
36
+ ctx.handle_error(e)
@@ -90,6 +90,10 @@ def record_create(ctx, worksheet_id, field, fields_json, no_workflow):
90
90
 
91
91
  Pass values either as repeated -f CONTROL_ID=VALUE pairs or as one
92
92
  --fields-json array.
93
+
94
+ What to put in a value depends on the field type, and a wrong shape is
95
+ usually accepted and stored broken rather than rejected — see
96
+ `hap guide record` before writing, and read the record back after.
93
97
  """
94
98
  if field and fields_json:
95
99
  raise click.UsageError("Pass exactly one of -f or --fields-json.")
@@ -162,6 +166,10 @@ def record_update(ctx, worksheet_id, row_id, field, fields_json, no_workflow):
162
166
  fields change), a child row without a "rowid" adds a new row, and a
163
167
  child row with "_delete": true removes that row. Rows you don't list
164
168
  are left untouched. An empty list (or null) clears the sub-table.
169
+
170
+ Every other field type replaces rather than merges: passing a new value
171
+ for a multi-select, attachment or relation field drops whatever was
172
+ there. Value formats per field type are in `hap guide record`.
165
173
  """
166
174
  if field and fields_json:
167
175
  raise click.UsageError("Pass exactly one of -f or --fields-json.")
@@ -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(
@@ -11,7 +11,12 @@ from hap_cli.utils.options import org_id_option, require_api_project_id
11
11
  from hap_cli.i18n import t
12
12
 
13
13
 
14
- @click.group(help=t('Worksheet operations.'))
14
+ @click.group(help=t(
15
+ 'Worksheet operations.\n\n'
16
+ 'Writing records: the value each field type expects (options, members, '
17
+ 'relations, sub-tables, attachments) is covered by `hap guide record` — '
18
+ 'a wrong shape is usually accepted and stored broken.'
19
+ ))
15
20
  def worksheet():
16
21
  pass
17
22
 
@@ -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
+ }