hap-cli 0.8.10__tar.gz → 0.8.12__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 (254) hide show
  1. {hap_cli-0.8.10 → hap_cli-0.8.12}/PKG-INFO +6 -6
  2. {hap_cli-0.8.10/hap_cli → hap_cli-0.8.12}/README.md +4 -4
  3. {hap_cli-0.8.10 → hap_cli-0.8.12/hap_cli}/README.md +4 -4
  4. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/README_CN.md +1 -1
  5. hap_cli-0.8.12/hap_cli/__init__.py +3 -0
  6. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/auth_cmd.py +1 -1
  7. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/chart_cmd.py +1 -1
  8. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/role_cmd.py +46 -0
  9. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app.py +1 -1
  10. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/design/design.schema.json +22 -0
  11. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/selftest.py +95 -0
  12. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/steps.py +41 -0
  13. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/validate.py +48 -2
  14. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/auth.py +2 -2
  15. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/calendar_mod.py +1 -1
  16. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/chart.py +1 -1
  17. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/chat.py +129 -9
  18. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/chatbot.py +1 -1
  19. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/contact.py +1 -1
  20. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/department.py +1 -1
  21. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/flow_node.py +22 -1
  22. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/group.py +1 -1
  23. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/instance.py +1 -1
  24. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/optionset.py +1 -1
  25. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/page.py +1 -1
  26. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/post.py +1 -1
  27. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/record.py +1 -1
  28. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/role.py +49 -1
  29. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/session.py +6 -6
  30. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/workflow.py +1 -1
  31. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/worksheet.py +1 -1
  32. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/hap_cli.py +1 -1
  33. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_core.py +264 -1
  34. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_full_e2e.py +1 -1
  35. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_global_meta.py +1 -1
  36. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/PKG-INFO +6 -6
  37. {hap_cli-0.8.10 → hap_cli-0.8.12}/setup.py +1 -1
  38. hap_cli-0.8.10/hap_cli/__init__.py +0 -3
  39. {hap_cli-0.8.10 → hap_cli-0.8.12}/MANIFEST.in +0 -0
  40. {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/__init__.py +0 -0
  41. {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/__main__.py +0 -0
  42. {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/config.py +0 -0
  43. {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/harness.py +0 -0
  44. {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/smoke.py +0 -0
  45. {hap_cli-0.8.10 → hap_cli-0.8.12}/app_live_tests/state.py +0 -0
  46. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/__main__.py +0 -0
  47. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/__init__.py +0 -0
  48. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/app_cmd.py +0 -0
  49. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/app_creator_cmd.py +0 -0
  50. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/app_editor_cmd.py +0 -0
  51. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/calendar_cmd.py +0 -0
  52. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/chat_cmd.py +0 -0
  53. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/chatbot_cmd.py +0 -0
  54. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/config_cmd.py +0 -0
  55. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/contact_cmd.py +0 -0
  56. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/department_cmd.py +0 -0
  57. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/group_cmd.py +0 -0
  58. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/icon_cmd.py +0 -0
  59. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/instance_cmd.py +0 -0
  60. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/node_cmd.py +0 -0
  61. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/optionset_cmd.py +0 -0
  62. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/page_cmd.py +0 -0
  63. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/post_cmd.py +0 -0
  64. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/record_cmd.py +0 -0
  65. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/region_cmd.py +0 -0
  66. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/update_cmd.py +0 -0
  67. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/upload_cmd.py +0 -0
  68. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/v3_registry.py +0 -0
  69. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/workflow_cmd.py +0 -0
  70. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/commands/worksheet_cmd.py +0 -0
  71. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/context.py +0 -0
  72. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/__init__.py +0 -0
  73. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/action_spec_adapter.py +0 -0
  74. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/__init__.py +0 -0
  75. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  76. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  77. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  78. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/charts.py +0 -0
  79. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/cleanup.py +0 -0
  80. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/compiler.py +0 -0
  81. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/config.py +0 -0
  82. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/errors.py +0 -0
  83. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  84. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/executor.py +0 -0
  85. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/fields.py +0 -0
  86. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/hap.py +0 -0
  87. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/merge.py +0 -0
  88. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  89. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/console.py +0 -0
  90. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  91. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  92. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/recording/report.py +0 -0
  93. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/schema.py +0 -0
  94. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  95. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  96. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/cli.py +0 -0
  97. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/executor.py +0 -0
  98. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  99. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  100. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  101. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  102. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/seed/template.py +0 -0
  103. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/smoke.py +0 -0
  104. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/store.py +0 -0
  105. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  106. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/cli_access.py +0 -0
  107. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/control_type_codes.py +0 -0
  108. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/__init__.py +0 -0
  109. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  110. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  111. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/apply.py +0 -0
  112. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/componentlower.py +0 -0
  113. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/editspec.py +0 -0
  114. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/errors.py +0 -0
  115. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/fieldlower.py +0 -0
  116. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  117. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/models.py +0 -0
  118. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/ops.py +0 -0
  119. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/planner.py +0 -0
  120. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/editor/reader.py +0 -0
  121. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/field_normalizer.py +0 -0
  122. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/field_spec_adapter.py +0 -0
  123. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/filter_translator.py +0 -0
  124. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/global_meta.py +0 -0
  125. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/icon_index.py +0 -0
  126. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/logger.py +0 -0
  127. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/response_crypto.py +0 -0
  128. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/role_perm_builder.py +0 -0
  129. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/token_crypto.py +0 -0
  130. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/update.py +0 -0
  131. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/upload.py +0 -0
  132. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/__init__.py +0 -0
  133. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/dispatcher.py +0 -0
  134. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/render.py +0 -0
  135. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/v3/schema.py +0 -0
  136. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/view_spec_adapter.py +0 -0
  137. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/workflow_node_dsl.py +0 -0
  138. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/core/worksheet_templates.py +0 -0
  139. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/i18n.py +0 -0
  140. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/locale/__init__.py +0 -0
  141. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/locale/messages.json +0 -0
  142. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/locale/messages.schema.json +0 -0
  143. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/__init__.py +0 -0
  144. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/conftest.py +0 -0
  145. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_app_creator.py +0 -0
  146. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_app_editor.py +0 -0
  147. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_app_export_usage.py +0 -0
  148. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_auth_prerelease.py +0 -0
  149. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_bug_036430.py +0 -0
  150. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_chart.py +0 -0
  151. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_cli_access.py +0 -0
  152. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_config_cmd.py +0 -0
  153. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  154. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_i18n.py +0 -0
  155. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_instance_history_cli.py +0 -0
  156. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration.py +0 -0
  157. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_approval.py +0 -0
  158. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  159. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_calendar.py +0 -0
  160. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_destructive.py +0 -0
  161. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_misc.py +0 -0
  162. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_post.py +0 -0
  163. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_social.py +0 -0
  164. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_v3.py +0 -0
  165. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_workflow.py +0 -0
  166. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  167. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_multi_profile.py +0 -0
  168. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  169. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_org_id_cli.py +0 -0
  170. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_org_id_docs.py +0 -0
  171. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_parameter_conventions.py +0 -0
  172. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  173. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  174. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_record_field_alias.py +0 -0
  175. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  176. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  177. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_update.py +0 -0
  178. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  179. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  180. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  181. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_session.py +0 -0
  182. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  183. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  184. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  185. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  186. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  187. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/__init__.py +0 -0
  188. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/formatting.py +0 -0
  189. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/options.py +0 -0
  190. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli/utils/parameter_mapping.py +0 -0
  191. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/SOURCES.txt +0 -0
  192. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/dependency_links.txt +0 -0
  193. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/entry_points.txt +0 -0
  194. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/requires.txt +0 -0
  195. {hap_cli-0.8.10 → hap_cli-0.8.12}/hap_cli.egg-info/top_level.txt +0 -0
  196. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/__init__.py +0 -0
  197. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/__main__.py +0 -0
  198. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/_wftest.py +0 -0
  199. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/charts.py +0 -0
  200. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/cleanup.py +0 -0
  201. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/compiler.py +0 -0
  202. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/config.py +0 -0
  203. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/errors.py +0 -0
  204. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/executor.py +0 -0
  205. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/fields.py +0 -0
  206. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/hap.py +0 -0
  207. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/__init__.py +0 -0
  208. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/console.py +0 -0
  209. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/jsonl.py +0 -0
  210. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/mirror.py +0 -0
  211. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/recording/report.py +0 -0
  212. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/schema.py +0 -0
  213. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/__init__.py +0 -0
  214. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/cli.py +0 -0
  215. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/executor.py +0 -0
  216. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/seed/template.py +0 -0
  217. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/selftest.py +0 -0
  218. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/smoke.py +0 -0
  219. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/step.py +0 -0
  220. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/steps.py +0 -0
  221. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/store.py +0 -0
  222. {hap_cli-0.8.10 → hap_cli-0.8.12}/live_tests/workflow_dsl.py +0 -0
  223. {hap_cli-0.8.10 → hap_cli-0.8.12}/setup.cfg +0 -0
  224. {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/component.schema.json +0 -0
  225. {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/custom-action.schema.json +0 -0
  226. {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/envelope.schema.json +0 -0
  227. {hap_cli-0.8.10 → hap_cli-0.8.12}/skills/cli/hap-cli-app-editor/scripts/editspec/field.schema.json +0 -0
  228. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/INDEX.json +0 -0
  229. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  230. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  231. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/create_optionset.yaml +0 -0
  232. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/create_role.yaml +0 -0
  233. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  234. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_record.yaml +0 -0
  235. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_role.yaml +0 -0
  236. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  237. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_app_info.yaml +0 -0
  238. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  239. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  240. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  241. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_details.yaml +0 -0
  242. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  243. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_list.yaml +0 -0
  244. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  245. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  246. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  247. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  248. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_regions.yaml +0 -0
  249. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_role_details.yaml +0 -0
  250. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/get_role_list.yaml +0 -0
  251. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  252. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  253. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  254. {hap_cli-0.8.10 → hap_cli-0.8.12}/sources/v3-api-schema/update_optionset.yaml +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.8.10
4
- Summary: CLI harness for MingDAO HAP - Enterprise no-code platform
3
+ Version: 0.8.12
4
+ Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
7
7
  Classifier: Development Status :: 3 - Alpha
@@ -29,7 +29,7 @@ Dynamic: summary
29
29
 
30
30
  # hap-cli
31
31
 
32
- CLI harness for **MingDAO HAP** (明道云) — an enterprise no-code platform.
32
+ CLI harness for **Mingdao HAP** (明道云) — an enterprise no-code platform.
33
33
 
34
34
  ## Installation
35
35
 
@@ -54,7 +54,7 @@ each organization's status (`CLI:on/off`).
54
54
  ### 1. Login
55
55
 
56
56
  ```bash
57
- # MingDAO SaaS (default)
57
+ # Mingdao SaaS (default)
58
58
  hap auth login
59
59
 
60
60
  # Specific environment
@@ -66,7 +66,7 @@ hap auth login https://hap.example.com # self-hosted
66
66
  hap auth login https://your-server.com --token YOUR_TOKEN
67
67
  ```
68
68
 
69
- The browser flow opens the MingDAO login page and captures the token. If the
69
+ The browser flow opens the Mingdao login page and captures the token. If the
70
70
  callback can't reach the CLI (no GUI, restricted network), interrupt the
71
71
  command — it falls back to a paste prompt for the token shown on the success
72
72
  page.
@@ -78,7 +78,7 @@ hap auth logout # log out the current account
78
78
 
79
79
  #### Multiple environments / accounts
80
80
 
81
- You can stay logged in to several environments (MingDAO SaaS, Nocoly, self-hosted) and
81
+ You can stay logged in to several environments (Mingdao SaaS, Nocoly, self-hosted) and
82
82
  several accounts at once. Each is saved under a name and you switch between them:
83
83
 
84
84
  ```bash
@@ -1,6 +1,6 @@
1
1
  # hap-cli
2
2
 
3
- CLI harness for **MingDAO HAP** (明道云) — an enterprise no-code platform.
3
+ CLI harness for **Mingdao HAP** (明道云) — an enterprise no-code platform.
4
4
 
5
5
  ## Installation
6
6
 
@@ -25,7 +25,7 @@ each organization's status (`CLI:on/off`).
25
25
  ### 1. Login
26
26
 
27
27
  ```bash
28
- # MingDAO SaaS (default)
28
+ # Mingdao SaaS (default)
29
29
  hap auth login
30
30
 
31
31
  # Specific environment
@@ -37,7 +37,7 @@ hap auth login https://hap.example.com # self-hosted
37
37
  hap auth login https://your-server.com --token YOUR_TOKEN
38
38
  ```
39
39
 
40
- The browser flow opens the MingDAO login page and captures the token. If the
40
+ The browser flow opens the Mingdao login page and captures the token. If the
41
41
  callback can't reach the CLI (no GUI, restricted network), interrupt the
42
42
  command — it falls back to a paste prompt for the token shown on the success
43
43
  page.
@@ -49,7 +49,7 @@ hap auth logout # log out the current account
49
49
 
50
50
  #### Multiple environments / accounts
51
51
 
52
- You can stay logged in to several environments (MingDAO SaaS, Nocoly, self-hosted) and
52
+ You can stay logged in to several environments (Mingdao SaaS, Nocoly, self-hosted) and
53
53
  several accounts at once. Each is saved under a name and you switch between them:
54
54
 
55
55
  ```bash
@@ -1,6 +1,6 @@
1
1
  # hap-cli
2
2
 
3
- CLI harness for **MingDAO HAP** (明道云) — an enterprise no-code platform.
3
+ CLI harness for **Mingdao HAP** (明道云) — an enterprise no-code platform.
4
4
 
5
5
  ## Installation
6
6
 
@@ -25,7 +25,7 @@ each organization's status (`CLI:on/off`).
25
25
  ### 1. Login
26
26
 
27
27
  ```bash
28
- # MingDAO SaaS (default)
28
+ # Mingdao SaaS (default)
29
29
  hap auth login
30
30
 
31
31
  # Specific environment
@@ -37,7 +37,7 @@ hap auth login https://hap.example.com # self-hosted
37
37
  hap auth login https://your-server.com --token YOUR_TOKEN
38
38
  ```
39
39
 
40
- The browser flow opens the MingDAO login page and captures the token. If the
40
+ The browser flow opens the Mingdao login page and captures the token. If the
41
41
  callback can't reach the CLI (no GUI, restricted network), interrupt the
42
42
  command — it falls back to a paste prompt for the token shown on the success
43
43
  page.
@@ -49,7 +49,7 @@ hap auth logout # log out the current account
49
49
 
50
50
  #### Multiple environments / accounts
51
51
 
52
- You can stay logged in to several environments (MingDAO SaaS, Nocoly, self-hosted) and
52
+ You can stay logged in to several environments (Mingdao SaaS, Nocoly, self-hosted) and
53
53
  several accounts at once. Each is saved under a name and you switch between them:
54
54
 
55
55
  ```bash
@@ -40,7 +40,7 @@ hap auth logout # 登出当前账号
40
40
 
41
41
  #### 多环境 / 多账号
42
42
 
43
- 可同时授权多个环境(MingDAO SaaS、Nocoly、私有部署)与多个账号,各存成一个具名档案,长期共存、随时切换:
43
+ 可同时授权多个环境(Mingdao SaaS、Nocoly、私有部署)与多个账号,各存成一个具名档案,长期共存、随时切换:
44
44
 
45
45
  ```bash
46
46
  hap auth login mingdao --profile work-prod # 登录并起名保存
@@ -0,0 +1,3 @@
1
+ """CLI harness for Mingdao HAP (hap-cli)."""
2
+
3
+ __version__ = "0.8.12"
@@ -378,7 +378,7 @@ def auth_whoami(ctx):
378
378
  # Read login_url from the active (or --profile) credential profile —
379
379
  # the in-memory Session may have been mutated, and we want whoami to
380
380
  # probe the server recorded for the profile in use. When the profile
381
- # has no URL, fall back to the MingDAO SaaS host. Token failures
381
+ # has no URL, fall back to the Mingdao SaaS host. Token failures
382
382
  # (missing / expired) surface uniformly via AuthenticationError
383
383
  # → "Not logged in" hint from handle_error.
384
384
  profile_name = resolve_profile_name(ctx.profile)
@@ -21,7 +21,7 @@ def chart():
21
21
  pass
22
22
 
23
23
 
24
- # Report-type enum values used by the MingDAO statistics module. The CLI
24
+ # Report-type enum values used by the Mingdao statistics module. The CLI
25
25
  # accepts the integer directly so users can pass uncommon types without us
26
26
  # having to enumerate every one. Note: there is NO type 4 — passing 4 is
27
27
  # coerced to 10 (number) by the server, so it is omitted here (bug #036584).
@@ -208,6 +208,52 @@ def role_set_permissions(ctx, app_id, role_id, permissions, permission_way):
208
208
  ctx.handle_error(e)
209
209
 
210
210
 
211
+ @role.command("set-chatbots")
212
+ @click.argument("app_id")
213
+ @click.argument("role_id")
214
+ @click.option("--enable-all", is_flag=True,
215
+ help="Grant this role access to every AI assistant in the app.")
216
+ @click.option("--disable-all", is_flag=True,
217
+ help="Remove this role's access to every AI assistant in the app.")
218
+ @click.option("--chatbots-json", default="",
219
+ help='Per-assistant access as a JSON array, e.g. '
220
+ '[{"id":"<assistant-id>","enable":true}].')
221
+ @pass_context
222
+ def role_set_chatbots(ctx, app_id, role_id, enable_all, disable_all, chatbots_json):
223
+ """Set which AI assistants a role can access.
224
+
225
+ A newly created fine-grained role has no AI-assistant access until it
226
+ is granted here. Provide exactly one of --enable-all, --disable-all,
227
+ or --chatbots-json.
228
+ """
229
+ try:
230
+ chosen = sum(bool(x) for x in (enable_all, disable_all, chatbots_json))
231
+ if chosen != 1:
232
+ raise click.UsageError(
233
+ "Provide exactly one of --enable-all, --disable-all, "
234
+ "or --chatbots-json."
235
+ )
236
+ session = ctx.get_session()
237
+ enables = None
238
+ default = None
239
+ if enable_all:
240
+ default = True
241
+ elif disable_all:
242
+ default = False
243
+ else:
244
+ entries = json.loads(chatbots_json)
245
+ enables = {
246
+ e["id"]: bool(e.get("enable", True))
247
+ for e in entries if e.get("id")
248
+ }
249
+ data = role_mod.set_role_chatbots(
250
+ session, app_id, role_id, enables=enables, default=default,
251
+ )
252
+ ctx.output(data, lambda d: click.echo("AI assistant access updated."))
253
+ except Exception as e:
254
+ ctx.handle_error(e)
255
+
256
+
211
257
  @role.command("rename")
212
258
  @click.argument("app_id")
213
259
  @click.argument("role_id")
@@ -1,4 +1,4 @@
1
- """Application management module for MingDAO HAP."""
1
+ """Application management module for Mingdao HAP."""
2
2
 
3
3
  from datetime import datetime, timedelta
4
4
  from typing import Any, Optional
@@ -3485,6 +3485,28 @@
3485
3485
  },
3486
3486
  "description": "permission_scope=0 时的自定义页面权限明细(页面按逻辑名)。"
3487
3487
  },
3488
+ "chatbot_permissions": {
3489
+ "type": "array",
3490
+ "items": {
3491
+ "type": "object",
3492
+ "required": [
3493
+ "chatbot"
3494
+ ],
3495
+ "additionalProperties": false,
3496
+ "properties": {
3497
+ "chatbot": {
3498
+ "type": "string",
3499
+ "description": "AI 助手逻辑名 -> id。"
3500
+ },
3501
+ "enable": {
3502
+ "type": "boolean",
3503
+ "default": true,
3504
+ "description": "是否可访问该 AI 助手。"
3505
+ }
3506
+ }
3507
+ },
3508
+ "description": "可访问的 AI 助手(chatbot)明细,按 chatbot 逻辑名引用。省略该键 = 该角色可访问应用内全部 AI 助手(默认开放)。一旦提供本数组,则只有列出且 enable=true 的助手可访问,其余助手一律不可访问。"
3509
+ },
3488
3510
  "members": {
3489
3511
  "type": "object",
3490
3512
  "additionalProperties": false,
@@ -67,6 +67,45 @@ def test_store() -> None:
67
67
  pass
68
68
 
69
69
 
70
+ def test_role_chatbot_enables() -> None:
71
+ from hap_cli.core.app_creator.store import Store
72
+ from hap_cli.core.app_creator.steps import _role_chatbot_enables
73
+
74
+ d = Path(tempfile.mkdtemp()) / "appCB"
75
+ s = Store(d)
76
+ s.put_app("appCB", "Demo", "org1", [{"id": "sec1", "name": "分析"}])
77
+ # No chatbots in the app -> nothing to grant.
78
+ assert _role_chatbot_enables(s, {"name": "r"}) == {}
79
+
80
+ s.put_entity("chatbot", "cb1", "订单助手", {"name": "订单助手"})
81
+ s.put_entity("chatbot", "cb2", "客服助手", {"name": "客服助手"})
82
+ # chatbot_permissions omitted -> every assistant is granted (the fix).
83
+ assert _role_chatbot_enables(s, {"name": "r"}) == {"cb1": True, "cb2": True}
84
+ # Provided -> whitelist: only listed enable:true granted, the rest off.
85
+ role = {"name": "r",
86
+ "chatbot_permissions": [{"chatbot": "订单助手", "enable": True}]}
87
+ assert _role_chatbot_enables(s, role) == {"cb1": True, "cb2": False}
88
+ # enable defaults true when omitted; explicit false revokes a listed one.
89
+ role2 = {"name": "r", "chatbot_permissions": [
90
+ {"chatbot": "订单助手", "enable": False},
91
+ {"chatbot": "客服助手"},
92
+ ]}
93
+ assert _role_chatbot_enables(s, role2) == {"cb1": False, "cb2": True}
94
+
95
+
96
+ def test_role_chatbot_permissions_schema() -> None:
97
+ design = {
98
+ "app": {"name": "x"},
99
+ "worksheets": [{"name": "t", "fields": [
100
+ {"type": "Text", "name": "标题", "is_title": True}]}],
101
+ "chatbots": [{"name": "助手"}],
102
+ "roles": [{"name": "r", "permission_scope": "0",
103
+ "chatbot_permissions": [{"chatbot": "助手", "enable": True}]}],
104
+ }
105
+ errs = _vd(design)
106
+ assert not any("chatbot_permissions" in e for e in errs), errs
107
+
108
+
70
109
  def test_schema() -> None:
71
110
  from hap_cli.core.app_creator import schema
72
111
 
@@ -1607,6 +1646,62 @@ def test_icon_validation() -> None:
1607
1646
  assert any("worksheets[0].icon" in e and "1_2_order" in e for e in errs), errs
1608
1647
 
1609
1648
 
1649
+ def test_icon_uniqueness() -> None:
1650
+ """Nav-level entities (worksheets + custom_pages) must each carry a DISTINCT
1651
+ icon — the 零冗余 golden rule. Reusing one icon across two worksheets/pages
1652
+ is a hard validation error, caught offline before build. Buttons and
1653
+ page-component icons are exempt (icon reuse there is legitimate and common).
1654
+ Pure check — no catalogue lookup needed."""
1655
+ from hap_cli.core.app_creator import validate
1656
+
1657
+ # Two worksheets sharing one icon -> a single error that names both owners.
1658
+ dup = {
1659
+ "app": {"name": "D", "icon": "sys_1_2_order"},
1660
+ "worksheets": [
1661
+ {"name": "入库单", "icon": "sys_form_symbol"},
1662
+ {"name": "出库单", "icon": "sys_form_symbol"},
1663
+ {"name": "物料", "icon": "sys_18_5_warehouse"},
1664
+ ],
1665
+ }
1666
+ errs = validate._check_icon_uniqueness(dup)
1667
+ assert len(errs) == 1, errs
1668
+ assert "sys_form_symbol" in errs[0], errs
1669
+ assert "入库单" in errs[0] and "出库单" in errs[0], errs
1670
+
1671
+ # A worksheet and a custom_page sharing an icon also collide — they sit in
1672
+ # the same left-nav strip.
1673
+ cross = {
1674
+ "worksheets": [{"name": "订单", "icon": "sys_form_symbol"}],
1675
+ "custom_pages": [{"name": "看板", "icon": "sys_form_symbol"}],
1676
+ }
1677
+ errs = validate._check_icon_uniqueness(cross)
1678
+ assert len(errs) == 1, errs
1679
+ assert "订单" in errs[0] and "看板" in errs[0], errs
1680
+
1681
+ # All-distinct nav icons pass clean (app icon does not participate).
1682
+ ok = {
1683
+ "app": {"name": "D", "icon": "sys_1_2_order"},
1684
+ "worksheets": [
1685
+ {"name": "A", "icon": "sys_1_2_order"}, # may coincide with app
1686
+ {"name": "B", "icon": "sys_18_5_warehouse"},
1687
+ ],
1688
+ "custom_pages": [{"name": "P", "icon": "sys_form_symbol"}],
1689
+ }
1690
+ assert validate._check_icon_uniqueness(ok) == []
1691
+
1692
+ # Button / page-component icon reuse is EXEMPT: a worksheet icon repeated on
1693
+ # a page-component button is not a collision.
1694
+ exempt = {
1695
+ "worksheets": [{"name": "订单", "icon": "sys_form_symbol"}],
1696
+ "custom_pages": [{
1697
+ "name": "工作台", "icon": "sys_1_2_order",
1698
+ "components": [{"name": "C",
1699
+ "button": {"buttons": [{"icon": "sys_form_symbol"}]}}],
1700
+ }],
1701
+ }
1702
+ assert validate._check_icon_uniqueness(exempt) == []
1703
+
1704
+
1610
1705
  def test_append_strips_client_control_ids() -> None:
1611
1706
  """Cross-sheet controls (forward relations, lookups, rollups, …) are
1612
1707
  appended with AddWorksheetControls, which only mints a real 24-hex
@@ -962,6 +962,33 @@ def _build_ws_perm(store: Store, wp: dict[str, Any]) -> dict[str, Any]:
962
962
  }
963
963
 
964
964
 
965
+ def _role_chatbot_enables(store: Store, role: dict[str, Any]) -> dict[str, bool]:
966
+ """Map every app chatbot id -> whether this role may access it.
967
+
968
+ The fine-grained role-create endpoint can't grant AI-assistant
969
+ access, so the role handler patches it in afterwards (via
970
+ ``app role set-chatbots`` -> EditAppRole). Default: every assistant in
971
+ the app is accessible to the role. A role narrows this with
972
+ ``chatbot_permissions`` (a list of ``{chatbot, enable}``); when that
973
+ key is present only the listed assistants with ``enable: true`` are
974
+ granted and all other assistants are revoked.
975
+
976
+ Returns an empty mapping when the app has no chatbots (nothing to do).
977
+ """
978
+ ids = [c["id"] for c in store.list("chatbot") if c.get("id")]
979
+ if not ids:
980
+ return {}
981
+ cp = role.get("chatbot_permissions")
982
+ if cp is None:
983
+ return {cid: True for cid in ids}
984
+ wanted: dict[str, bool] = {}
985
+ for item in cp:
986
+ wanted[store.resolve("chatbot", item["chatbot"])] = bool(
987
+ item.get("enable", True)
988
+ )
989
+ return {cid: bool(wanted.get(cid, False)) for cid in ids}
990
+
991
+
965
992
  @handler("role")
966
993
  def _h_role(ctx: ExecCtx, step: Step) -> StepOutcome:
967
994
  store = _require_store(ctx)
@@ -1021,6 +1048,20 @@ def _h_role(ctx: ExecCtx, step: Step) -> StepOutcome:
1021
1048
  hap.run(m_argv)
1022
1049
  commands.append(m_argv)
1023
1050
 
1051
+ # Grant AI-assistant (chatbot) access. The fine-grained role-create
1052
+ # endpoint has no field for it, so every role would otherwise be unable
1053
+ # to reach the app's assistants. Chatbots are built before roles, so
1054
+ # they already exist in the store here.
1055
+ enables = _role_chatbot_enables(store, role)
1056
+ if role_id and enables:
1057
+ cb_argv = ["app", "role", "set-chatbots", ctx.store.app_id, role_id,
1058
+ "--chatbots-json",
1059
+ json.dumps([{"id": cid, "enable": en}
1060
+ for cid, en in enables.items()],
1061
+ ensure_ascii=False)]
1062
+ hap.run(cb_argv)
1063
+ commands.append(cb_argv)
1064
+
1024
1065
  return StepOutcome(
1025
1066
  created_id=role_id, summary=f"role {name!r} scope={scope}",
1026
1067
  commands=commands, capture_files=[f"role_{role_id}.json", "roles.json"],
@@ -6,9 +6,9 @@ One file validates that file. Multiple files are MERGED (the split-generation
6
6
  workflow: foundation + independently-authored parts) and the combined document
7
7
  is validated as a whole — so cross-part logical-name references are checked.
8
8
 
9
- Two layers run here:
9
+ Three layers run here:
10
10
  1. structural + reference validation — pure, offline (``schema.load_designs``).
11
- 2. icon validation — every ``icon`` field (app / worksheet / page / page
11
+ 2. icon existence — every ``icon`` field (app / worksheet / page / page
12
12
  component / button) must be an EXACT, real catalogue icon. We confirm
13
13
  each via ``hap icon search <icon> --limit 1`` and require the top hit's
14
14
  ``fileName`` to equal the icon byte-for-byte. ``hap icon search`` is a
@@ -17,6 +17,11 @@ Two layers run here:
17
17
  A fabricated icon (``sys_15_3_user``) or a non-canonical short form
18
18
  (``1_2_order`` instead of ``sys_1_2_order``) is rejected — the backend
19
19
  only accepts exact catalogue fileNames.
20
+ 3. icon uniqueness — nav-level entities (top-level worksheets + custom_pages)
21
+ must each carry a DISTINCT icon (the 零冗余 / "不同实体用不同图标" rule).
22
+ They sit side by side in the app's left navigation, so a repeated icon is
23
+ visually confusing. Pure/offline. Button and page-component icons are
24
+ EXEMPT — reuse there (many "新建"/"+" tiles) is legitimate.
20
25
 
21
26
  Exit code 0 = valid, 2 = validation errors (printed with their JSON paths).
22
27
  Run this after authoring a design and before ``build`` so structural and icon
@@ -92,6 +97,44 @@ def _check_icons(doc: Any, runner: Callable[..., Any] = hap.run) -> list[str]:
92
97
  return errors
93
98
 
94
99
 
100
+ def _collect_entity_icons(doc: Any) -> list[tuple[str, str, str]]:
101
+ """Return ``(kind, name, icon)`` for each NAV-LEVEL entity that carries an
102
+ icon — top-level worksheets and custom_pages only. These appear together in
103
+ the app's left navigation, so each needs a visually distinct icon (the
104
+ 零冗余 rule). Deliberately does NOT descend into page components or button
105
+ ``buttons[]``: reusing an icon on action buttons is legitimate and common,
106
+ so those are exempt from the uniqueness check."""
107
+ out: list[tuple[str, str, str]] = []
108
+ if not isinstance(doc, dict):
109
+ return out
110
+ for kind, key in (("工作表", "worksheets"), ("自定义页面", "custom_pages")):
111
+ items = doc.get(key)
112
+ if not isinstance(items, list):
113
+ continue
114
+ for it in items:
115
+ if isinstance(it, dict) and isinstance(it.get("icon"), str):
116
+ out.append((kind, str(it.get("name", "?")), it["icon"]))
117
+ return out
118
+
119
+
120
+ def _check_icon_uniqueness(doc: Any) -> list[str]:
121
+ """Return errors for icons reused across nav-level entities (worksheets +
122
+ custom_pages). Each must be unique among them — 不同实体用不同图标 (零冗余).
123
+ One error per colliding icon, naming every owner. Pure/offline; no catalogue
124
+ lookup. Iteration order is document order, so messages are deterministic."""
125
+ by_icon: dict[str, list[str]] = {}
126
+ for kind, name, icon in _collect_entity_icons(doc):
127
+ by_icon.setdefault(icon, []).append(f"{kind} {name!r}")
128
+ errors: list[str] = []
129
+ for icon, owners in by_icon.items():
130
+ if len(owners) > 1:
131
+ errors.append(
132
+ f"icon {icon!r} is reused by {', '.join(owners)} — give each "
133
+ f"worksheet/page a DISTINCT icon (零冗余: 不同实体用不同图标). "
134
+ f"Find alternatives with `hap icon search <keywords>`.")
135
+ return errors
136
+
137
+
95
138
  def main(argv: list[str] | None = None) -> int:
96
139
  argv = list(sys.argv[1:] if argv is None else argv)
97
140
  if not argv:
@@ -112,6 +155,9 @@ def main(argv: list[str] | None = None) -> int:
112
155
  except HapCommandError as e:
113
156
  print(f"✗ cannot verify icons: {e}", file=sys.stderr)
114
157
  return 2
158
+ # Uniqueness is a pure check (no catalogue lookup) — fold it into the same
159
+ # error bucket so existence + dedup problems print together.
160
+ icon_errors += _check_icon_uniqueness(doc)
115
161
  if icon_errors:
116
162
  joined = "\n - ".join(icon_errors)
117
163
  print(f"✗ design failed icon validation:\n - {joined}", file=sys.stderr)
@@ -1,4 +1,4 @@
1
- """Browser-based authentication flow for MingDAO HAP.
1
+ """Browser-based authentication flow for Mingdao HAP.
2
2
 
3
3
  Implements a local callback server pattern:
4
4
  1. Start local HTTP server on a free port
@@ -139,7 +139,7 @@ def fetch_global_meta(host: str, token: str) -> dict:
139
139
  host: Login URL the user entered at ``hap auth login`` (e.g.
140
140
  'https://www.mingdao.com' or a self-hosted host). The API
141
141
  base is derived from this value. When empty, defaults to
142
- the MingDAO SaaS host (``https://www.mingdao.com``).
142
+ the Mingdao SaaS host (``https://www.mingdao.com``).
143
143
  token: Auth token.
144
144
 
145
145
  Raises:
@@ -1,4 +1,4 @@
1
- """Calendar and schedule management for MingDAO HAP.
1
+ """Calendar and schedule management for Mingdao HAP.
2
2
 
3
3
  Named `calendar_mod.py` to avoid conflict with the Python stdlib
4
4
  `calendar` module. All API field names follow
@@ -1,6 +1,6 @@
1
1
  """Worksheet chart (统计图) backend wrappers.
2
2
 
3
- The MingDAO statistics module exposes chart CRUD under two URL families
3
+ The Mingdao statistics module exposes chart CRUD under two URL families
4
4
  on the report subdomain:
5
5
 
6
6
  * ``/report/list`` GET — list charts of a worksheet