PyBreeze 1.0.26__tar.gz → 1.0.27__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 (188) hide show
  1. {pybreeze-1.0.26 → pybreeze-1.0.27}/PKG-INFO +6 -4
  2. {pybreeze-1.0.26 → pybreeze-1.0.27}/README.md +5 -3
  3. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend_multi_language/extend_english.py +21 -0
  4. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend_multi_language/extend_traditional_chinese.py +21 -0
  5. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/tools/tools_menu.py +20 -0
  6. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/curl_import_gui.py +26 -3
  7. pybreeze-1.0.27/pybreeze/pybreeze_ui/tools_gui/har_import_gui.py +188 -0
  8. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/header_analyzer_gui.py +39 -5
  9. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/json_format_gui.py +6 -1
  10. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/response_inspector_gui.py +28 -6
  11. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/curl_import/curl_parser.py +13 -51
  12. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/curl_import/request_codegen.py +3 -1
  13. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/curl_import/script_templates.py +77 -27
  14. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/exception/exception_tags.py +6 -0
  15. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/exception/exceptions.py +6 -0
  16. pybreeze-1.0.27/pybreeze/utils/har_import/har_codegen.py +138 -0
  17. pybreeze-1.0.27/pybreeze/utils/har_import/har_parser.py +304 -0
  18. pybreeze-1.0.27/pybreeze/utils/header_tools/header_merge.py +71 -0
  19. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/jwt_tools/jwt_decoder.py +21 -0
  20. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/response_inspector/response_analyzer.py +3 -9
  21. pybreeze-1.0.27/pybreeze/utils/url_tools/__init__.py +0 -0
  22. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze.egg-info/PKG-INFO +6 -4
  23. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze.egg-info/SOURCES.txt +5 -0
  24. {pybreeze-1.0.26 → pybreeze-1.0.27}/pyproject.toml +1 -1
  25. {pybreeze-1.0.26 → pybreeze-1.0.27}/LICENSE +0 -0
  26. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/__init__.py +0 -0
  27. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/__main__.py +0 -0
  28. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/__init__.py +0 -0
  29. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/mail_thunder_extend/__init__.py +0 -0
  30. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/mail_thunder_extend/mail_thunder_setting.py +0 -0
  31. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/__init__.py +0 -0
  32. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/api_testka/__init__.py +0 -0
  33. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/api_testka/api_testka_process.py +0 -0
  34. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/auto_control/__init__.py +0 -0
  35. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/auto_control/auto_control_process.py +0 -0
  36. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/file_automation/__init__.py +0 -0
  37. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/file_automation/file_automation_process.py +0 -0
  38. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/file_runner_process.py +0 -0
  39. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/load_density/__init__.py +0 -0
  40. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/load_density/load_density_process.py +0 -0
  41. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/mail_thunder/__init__.py +0 -0
  42. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/mail_thunder/mail_thunder_process.py +0 -0
  43. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/process_executor_utils.py +0 -0
  44. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/python_task_process_manager.py +0 -0
  45. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/queue_pump.py +0 -0
  46. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/test_pioneer/__init__.py +0 -0
  47. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/test_pioneer/test_pioneer_process_manager.py +0 -0
  48. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/web_runner/__init__.py +0 -0
  49. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend/process_executor/web_runner/web_runner_process.py +0 -0
  50. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend_multi_language/__init__.py +0 -0
  51. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/extend_multi_language/update_language_dict.py +0 -0
  52. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/__init__.py +0 -0
  53. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/__init__.py +0 -0
  54. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/ssh/__init__.py +0 -0
  55. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/ssh/ssh_command_widget.py +0 -0
  56. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/ssh/ssh_file_viewer_widget.py +0 -0
  57. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/ssh/ssh_host_key_policy.py +0 -0
  58. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/ssh/ssh_key_loader.py +0 -0
  59. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/ssh/ssh_login_widget.py +0 -0
  60. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/ssh/ssh_main_widget.py +0 -0
  61. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/url/__init__.py +0 -0
  62. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/connect_gui/url/ai_code_review_gui.py +0 -0
  63. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/__init__.py +0 -0
  64. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_commands.py +0 -0
  65. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_editor_widget.py +0 -0
  66. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_items.py +0 -0
  67. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_mermaid_parser.py +0 -0
  68. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_net_utils.py +0 -0
  69. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_property_panel.py +0 -0
  70. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_scene.py +0 -0
  71. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/diagram_editor/diagram_view.py +0 -0
  72. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/editor_main/__init__.py +0 -0
  73. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/editor_main/file_tree_context_menu.py +0 -0
  74. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/editor_main/main_ui.py +0 -0
  75. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/__init__.py +0 -0
  76. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/ai_gui_global_variable.py +0 -0
  77. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/code_review/__init__.py +0 -0
  78. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/code_review/code_review_thread.py +0 -0
  79. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/code_review/cot_code_review_gui.py +0 -0
  80. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/__init__.py +0 -0
  81. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/__init__.py +0 -0
  82. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/code_smell_detector.py +0 -0
  83. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/first_code_review.py +0 -0
  84. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/first_summary_prompt.py +0 -0
  85. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/global_rule.py +0 -0
  86. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/judge.py +0 -0
  87. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/judge_single_review.py +0 -0
  88. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/linter.py +0 -0
  89. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/step_by_step_analysis.py +0 -0
  90. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_code_review_prompt_templates/total_summary.py +0 -0
  91. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/cot_prompt_editor_widget.py +0 -0
  92. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/prompt_file_io.py +0 -0
  93. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/skills_prompt_editor_widget.py +0 -0
  94. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/skills_prompt_templates/__init__.py +0 -0
  95. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/skills_prompt_templates/code_explainer.py +0 -0
  96. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/prompt_edit_gui/skills_prompt_templates/code_review.py +0 -0
  97. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/skills/__init__.py +0 -0
  98. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/extend_ai_gui/skills/skills_send_gui.py +0 -0
  99. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/jupyter_lab_gui/__init__.py +0 -0
  100. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_thread.py +0 -0
  101. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py +0 -0
  102. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/__init__.py +0 -0
  103. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/__init__.py +0 -0
  104. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/api_testka_menu/__init__.py +0 -0
  105. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/api_testka_menu/build_api_testka_menu.py +0 -0
  106. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/auto_control_menu/__init__.py +0 -0
  107. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/auto_control_menu/build_autocontrol_menu.py +0 -0
  108. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/automation_file_menu/__init__.py +0 -0
  109. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/automation_file_menu/build_automation_file_menu.py +0 -0
  110. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/automation_menu_factory.py +0 -0
  111. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/load_density_menu/__init__.py +0 -0
  112. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/load_density_menu/build_load_density_menu.py +0 -0
  113. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/mail_thunder_menu/__init__.py +0 -0
  114. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/mail_thunder_menu/build_mail_thunder_menu.py +0 -0
  115. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/test_pioneer_menu/__init__.py +0 -0
  116. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/test_pioneer_menu/build_test_pioneer_menu.py +0 -0
  117. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/web_runner_menu/__init__.py +0 -0
  118. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/automation_menu/web_runner_menu/build_webrunner_menu.py +0 -0
  119. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/build_menubar.py +0 -0
  120. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/__init__.py +0 -0
  121. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/jupyter_lab_tab.py +0 -0
  122. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/install_menu/__init__.py +0 -0
  123. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/install_menu/automation_menu/__init__.py +0 -0
  124. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/install_menu/automation_menu/build_automation_install_menu.py +0 -0
  125. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/install_menu/install_utils.py +0 -0
  126. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/install_menu/tools_menu/__init__.py +0 -0
  127. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/install_menu/tools_menu/build_tool_install_menu.py +0 -0
  128. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/menu_utils.py +0 -0
  129. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/plugin_menu/__init__.py +0 -0
  130. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/plugin_menu/build_plugin_menu.py +0 -0
  131. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/plugin_menu/build_run_with_menu.py +0 -0
  132. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/menu/tools/__init__.py +0 -0
  133. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/show_code_window/__init__.py +0 -0
  134. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/show_code_window/code_window.py +0 -0
  135. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/syntax/__init__.py +0 -0
  136. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/syntax/syntax_extend.py +0 -0
  137. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/syntax/syntax_keyword.py +0 -0
  138. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/__init__.py +0 -0
  139. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/diff_gui.py +0 -0
  140. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/hash_gui.py +0 -0
  141. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/http_status_gui.py +0 -0
  142. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/jwt_decoder_gui.py +0 -0
  143. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/output_actions.py +0 -0
  144. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/query_json_gui.py +0 -0
  145. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/regex_gui.py +0 -0
  146. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/timestamp_gui.py +0 -0
  147. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/tool_tabs.py +0 -0
  148. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/pybreeze_ui/tools_gui/url_builder_gui.py +0 -0
  149. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/__init__.py +0 -0
  150. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/app_dirs.py +0 -0
  151. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/curl_import/__init__.py +0 -0
  152. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/curl_import/request_body.py +0 -0
  153. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/diff_tools/__init__.py +0 -0
  154. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/diff_tools/text_diff.py +0 -0
  155. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/exception/__init__.py +0 -0
  156. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/file_process/__init__.py +0 -0
  157. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/file_process/get_dir_file_list.py +0 -0
  158. {pybreeze-1.0.26/pybreeze/utils/hash_tools → pybreeze-1.0.27/pybreeze/utils/har_import}/__init__.py +0 -0
  159. {pybreeze-1.0.26/pybreeze/utils/header_tools → pybreeze-1.0.27/pybreeze/utils/hash_tools}/__init__.py +0 -0
  160. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/hash_tools/hash_text.py +0 -0
  161. {pybreeze-1.0.26/pybreeze/utils/http_reference → pybreeze-1.0.27/pybreeze/utils/header_tools}/__init__.py +0 -0
  162. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/header_tools/header_analyzer.py +0 -0
  163. {pybreeze-1.0.26/pybreeze/utils/jwt_tools → pybreeze-1.0.27/pybreeze/utils/http_reference}/__init__.py +0 -0
  164. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/http_reference/status_codes.py +0 -0
  165. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/json_format/__init__.py +0 -0
  166. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/json_format/json_process.py +0 -0
  167. {pybreeze-1.0.26/pybreeze/utils/logging → pybreeze-1.0.27/pybreeze/utils/jwt_tools}/__init__.py +0 -0
  168. {pybreeze-1.0.26/pybreeze/utils/manager → pybreeze-1.0.27/pybreeze/utils/logging}/__init__.py +0 -0
  169. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/logging/logger.py +0 -0
  170. {pybreeze-1.0.26/pybreeze/utils/manager/package_manager → pybreeze-1.0.27/pybreeze/utils/manager}/__init__.py +0 -0
  171. {pybreeze-1.0.26/pybreeze/utils/network → pybreeze-1.0.27/pybreeze/utils/manager/package_manager}/__init__.py +0 -0
  172. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/manager/package_manager/package_manager_class.py +0 -0
  173. {pybreeze-1.0.26/pybreeze/utils/query_tools → pybreeze-1.0.27/pybreeze/utils/network}/__init__.py +0 -0
  174. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/network/http_client.py +0 -0
  175. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/network/url_validation.py +0 -0
  176. {pybreeze-1.0.26/pybreeze/utils/regex_tools → pybreeze-1.0.27/pybreeze/utils/query_tools}/__init__.py +0 -0
  177. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/query_tools/query_convert.py +0 -0
  178. {pybreeze-1.0.26/pybreeze/utils/response_inspector → pybreeze-1.0.27/pybreeze/utils/regex_tools}/__init__.py +0 -0
  179. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/regex_tools/regex_tester.py +0 -0
  180. {pybreeze-1.0.26/pybreeze/utils/timestamp_tools → pybreeze-1.0.27/pybreeze/utils/response_inspector}/__init__.py +0 -0
  181. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/subprocess_util.py +0 -0
  182. {pybreeze-1.0.26/pybreeze/utils/url_tools → pybreeze-1.0.27/pybreeze/utils/timestamp_tools}/__init__.py +0 -0
  183. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/timestamp_tools/timestamp_converter.py +0 -0
  184. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze/utils/url_tools/url_convert.py +0 -0
  185. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze.egg-info/dependency_links.txt +0 -0
  186. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze.egg-info/requires.txt +0 -0
  187. {pybreeze-1.0.26 → pybreeze-1.0.27}/pybreeze.egg-info/top_level.txt +0 -0
  188. {pybreeze-1.0.26 → pybreeze-1.0.27}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybreeze
3
- Version: 1.0.26
3
+ Version: 1.0.27
4
4
  Summary: IDE for multi automation
5
5
  Author-email: JE-Chen <jechenmailman@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/Intergration-Automation-Testing/AutomationEditor
@@ -109,18 +109,19 @@ PyBreeze is not just a code editor — it is a command center for the automation
109
109
  - Mermaid `flowchart` / `graph` import
110
110
  - Save/Open as `.diagram.json`, export to PNG or SVG
111
111
  - Undo/redo, align, distribute, grid, snap, and zoom controls
112
- - **cURL Import** — Paste a `curl` command copied from your browser's dev tools and generate a ready-to-run script for the target of your choice: **Python `requests`**, an **APITestka Python** snippet (`test_api_method_requests(...)`), an **APITestka JSON action** (`[["AT_test_api_method", {...}]]`) that `execute_files` runs directly, or a **LoadDensity** Locust load-test (`start_test(...)`). (These are the HTTP-oriented modules; a curl request has no meaningful mapping to the browser or desktop-GUI automation modules.) Parses the method, URL, headers, body, basic auth, `-G` query parameters, `-F` multipart form fields (file uploads become `files=open(...)`), the `--json` shortcut (which also sets the JSON `Content-Type`/`Accept`), and `-d @file` bodies (which become `open(...).read()`, while `--data-raw` stays literal) — all with multi-line `\` / `^` continuations. It knows the arity of curl's common flags, so value-taking options like `--max-time 30` or `-o out.json` never leak into the URL; it URL-encodes `--data-urlencode` values the way curl does; and it infers `POST` when a body or form is present and sends JSON bodies as JSON. Targets include Python `requests`, a ready-to-run **pytest test** (a named `test_...` function that sends the request and asserts the status), APITestka (Python and JSON action), and a LoadDensity load test. Pick one from the dropdown, then copy the result, **open it straight into a new editor tab**, or **save it to a `.py` / `.json` file** (the extension follows the chosen target). A repeated `-H` is handled the way HTTP handles it — the values are combined into one header (with `; ` for cookies) rather than the last one silently winning, and names are matched case-insensitively so an explicit header always beats one implied by another flag. One click also **hands the parsed URL to the URL parser/builder**, query string and all. The parser is pure logic and never executes the command.
112
+ - **cURL Import** — Paste a `curl` command copied from your browser's dev tools and generate a ready-to-run script for the target of your choice: **Python `requests`**, an **APITestka Python** snippet (`test_api_method_requests(...)`), an **APITestka JSON action** (`[["AT_test_api_method", {...}]]`) that `execute_files` runs directly, or a **LoadDensity** Locust load-test (`start_test(...)`). (These are the HTTP-oriented modules; a curl request has no meaningful mapping to the browser or desktop-GUI automation modules.) Parses the method, URL, headers, body, basic auth, `-G` query parameters, `-F` multipart form fields (file uploads become `files=open(...)`), the `--json` shortcut (which also sets the JSON `Content-Type`/`Accept`), and `-d @file` bodies (which become `open(...).read()`, while `--data-raw` stays literal) — all with multi-line `\` / `^` continuations. It knows the arity of curl's common flags, so value-taking options like `--max-time 30` or `-o out.json` never leak into the URL; it URL-encodes `--data-urlencode` values the way curl does; and it infers `POST` when a body or form is present and sends JSON bodies as JSON. Targets include Python `requests`, a ready-to-run **pytest test** (a named `test_...` function that sends the request and asserts the status), APITestka (Python and JSON action), and a LoadDensity load test. Pick one from the dropdown, then copy the result, **open it straight into a new editor tab**, or **save it to a `.py` / `.json` file** (the extension follows the chosen target). A repeated `-H` is handled the way HTTP handles it — the values are combined into one header (with `; ` for cookies) rather than the last one silently winning, and names are matched case-insensitively so an explicit header always beats one implied by another flag. One click also hands the parsed parts to the tool that specialises in them: the **URL to the URL parser/builder** (query string and all) or the **headers to the header analyzer**. The parser is pure logic and never executes the command.
113
+ - **HAR Import** — "Copy as cURL" captures one request; **Save all as HAR** captures the session. Open that `.har` export and every recorded call is listed with its method, path, status and media type — page furniture (stylesheets, images, fonts) filtered out by default so what remains is the API traffic. Select the calls you want, or take everything listed, and generate one script for the whole set using the same targets as the cURL importer: a **pytest file** with one test per call (repeated endpoints get numbered names so no test silently replaces another), an **APITestka action list** that replays the flow in capture order, a **`requests` walkthrough**, an APITestka Python script, or LoadDensity runs. Recorded headers, cookies, query parameters and bodies (raw, URL-encoded and multipart, including file uploads) all come across; HTTP/2 pseudo-headers are dropped and a `Cookie` header that duplicates the recorded cookie list is removed, so the generated code sends each value once. A single selected request produces exactly what the cURL importer would, so the two tools never disagree. HAR is JSON, so this needs nothing beyond the standard library, and nothing is ever replayed for you.
113
114
  - **JWT Decoder** — Paste a JSON Web Token and read its header and payload as pretty-printed JSON, with standard timestamp claims (`exp`, `iat`, `nbf`, `auth_time`) rendered as readable UTC times. Inspection only — the signature is never verified and the token is never trusted.
114
115
  - **Timestamp Converter** — Enter a Unix epoch value (seconds or milliseconds, auto-detected) or an ISO-8601 date-time (with `Z` or an offset) and get every representation back in UTC. Deterministic and independent of the local time zone.
115
116
  - **Hash Generator** — Compute SHA-256, SHA-512, SHA-1 and MD5 digests of any text at once. A general-purpose checksum tool (MD5/SHA-1 are provided for interoperability with `usedforsecurity=False`, never for security decisions).
116
117
  - **Query ⇄ JSON** — Convert an `application/x-www-form-urlencoded` query string to pretty JSON and back, URL-decoding and -encoding as needed. Repeated keys become JSON arrays and vice versa. Copy the result, open it in an editor tab, or save it to a file.
117
118
  - **URL Parser / Builder** — Paste a URL to see its scheme, host, port, path, query, fragment and credentials as an editable JSON object, tweak any part, and turn it back into a URL. Round-trips both ways, brackets IPv6 literals, and re-encodes query parameters for you. The cURL importer can send a request's URL straight here.
118
- - **HTTP Header Analyzer** — Paste a request or response header block (from dev tools, `curl -i`, or a proxy log) and read back every field plus what is worth knowing about it: names sent more than once, `Set-Cookie` entries missing `Secure` / `HttpOnly` / `SameSite`, a wildcard CORS policy (and the wildcard-plus-credentials combination browsers reject outright), an HSTS `max-age` too short to survive a restart, CSP `unsafe-inline` / `unsafe-eval`, product banners, deprecated headers, and — for responses — the security headers that are absent. Headers carrying credentials are reported by name only; their values are never copied into the report. The Response Inspector hands its headers here in one click.
119
+ - **HTTP Header Analyzer** — Paste a request or response header block (from dev tools, `curl -i`, or a proxy log) and read back every field plus what is worth knowing about it: names sent more than once, `Set-Cookie` entries missing `Secure` / `HttpOnly` / `SameSite`, a wildcard CORS policy (and the wildcard-plus-credentials combination browsers reject outright), an HSTS `max-age` too short to survive a restart, CSP `unsafe-inline` / `unsafe-eval`, product banners, deprecated headers, and — for responses — the security headers that are absent. Headers carrying credentials are reported by name only; their values are never copied into the report. The cURL importer and the Response Inspector both hand their headers here in one click, and a bearer token spotted in any header opens in the JWT decoder, already decoded.
119
120
  - **Regex Tester** — Try a regular expression against sample text with `IGNORECASE` / `MULTILINE` / `DOTALL` / `VERBOSE` flags, and see every match with its offsets, numbered groups and named groups. Invalid patterns report a friendly error instead of crashing.
120
121
  - **HTTP Status Reference** — Search the full HTTP status code table (sourced from the standard library, so it stays current) by code prefix or keyword, with the reason phrase, description and status class for each.
121
122
  - **Text Diff** — Compare two pieces of text (for example an expected vs. actual API response) side by side and get a unified diff plus a one-line summary of how many lines were added or removed. Copy the diff, open it in an editor tab, or save it to a file.
122
123
  - **JSON Format** — Pretty-print or minify a JSON payload, with a clear validation error when the input is not valid JSON. Copy the result, open it in a new editor tab, or save it to a file.
123
- - **Response Inspector** — Paste a raw HTTP response and get a decoded summary in one step: the status code is looked up in the HTTP reference, headers are parsed, a JSON body is pretty-printed, and any JWTs anywhere in the text (for example an `Authorization: Bearer` header) are decoded with their timestamp claims shown in UTC. Ties the HTTP-status, JSON-format and JWT tools together, and can open a found status code, JWT, or the whole header block directly in its dedicated tool tab, pre-filled.
124
+ - **Response Inspector** — Paste a raw HTTP response and get a decoded summary in one step: the status code is looked up in the HTTP reference, headers are parsed, a JSON body is pretty-printed, and any JWTs anywhere in the text (for example an `Authorization: Bearer` header) are decoded with their timestamp claims shown in UTC. Ties the HTTP-status, JSON-format and JWT tools together, and can open a found status code, JWT, the whole header block, or a JSON body directly in its dedicated tool tab, pre-filled.
124
125
  - **File Tree Context Menu** — Right-click any file or folder in the project tree to create files/folders, rename, delete, copy absolute or relative paths, or reveal the item in your platform file manager. Renaming or deleting a file that is currently open in an editor tab keeps the tab in sync.
125
126
  - **Package Manager** — Install automation modules and build tools directly from the IDE menu without leaving the editor.
126
127
  - **Integrated Documentation** — Quick access to documentation and GitHub pages for each automation module directly from the menu bar.
@@ -252,6 +253,7 @@ PyBreeze UI (PySide6)
252
253
  │ ├── Skill Send GUI
253
254
  │ ├── Diagram Editor (WYSIWYG, Mermaid import, PNG/SVG export)
254
255
  │ ├── cURL Import (curl → Python requests code)
256
+ │ ├── HAR Import (browser session → test suite)
255
257
  │ ├── JWT Decoder (header/payload inspection)
256
258
  │ ├── Timestamp Converter (epoch ⇄ ISO-8601)
257
259
  │ ├── Hash Generator (SHA-256/512, SHA-1, MD5)
@@ -76,18 +76,19 @@ PyBreeze is not just a code editor — it is a command center for the automation
76
76
  - Mermaid `flowchart` / `graph` import
77
77
  - Save/Open as `.diagram.json`, export to PNG or SVG
78
78
  - Undo/redo, align, distribute, grid, snap, and zoom controls
79
- - **cURL Import** — Paste a `curl` command copied from your browser's dev tools and generate a ready-to-run script for the target of your choice: **Python `requests`**, an **APITestka Python** snippet (`test_api_method_requests(...)`), an **APITestka JSON action** (`[["AT_test_api_method", {...}]]`) that `execute_files` runs directly, or a **LoadDensity** Locust load-test (`start_test(...)`). (These are the HTTP-oriented modules; a curl request has no meaningful mapping to the browser or desktop-GUI automation modules.) Parses the method, URL, headers, body, basic auth, `-G` query parameters, `-F` multipart form fields (file uploads become `files=open(...)`), the `--json` shortcut (which also sets the JSON `Content-Type`/`Accept`), and `-d @file` bodies (which become `open(...).read()`, while `--data-raw` stays literal) — all with multi-line `\` / `^` continuations. It knows the arity of curl's common flags, so value-taking options like `--max-time 30` or `-o out.json` never leak into the URL; it URL-encodes `--data-urlencode` values the way curl does; and it infers `POST` when a body or form is present and sends JSON bodies as JSON. Targets include Python `requests`, a ready-to-run **pytest test** (a named `test_...` function that sends the request and asserts the status), APITestka (Python and JSON action), and a LoadDensity load test. Pick one from the dropdown, then copy the result, **open it straight into a new editor tab**, or **save it to a `.py` / `.json` file** (the extension follows the chosen target). A repeated `-H` is handled the way HTTP handles it — the values are combined into one header (with `; ` for cookies) rather than the last one silently winning, and names are matched case-insensitively so an explicit header always beats one implied by another flag. One click also **hands the parsed URL to the URL parser/builder**, query string and all. The parser is pure logic and never executes the command.
79
+ - **cURL Import** — Paste a `curl` command copied from your browser's dev tools and generate a ready-to-run script for the target of your choice: **Python `requests`**, an **APITestka Python** snippet (`test_api_method_requests(...)`), an **APITestka JSON action** (`[["AT_test_api_method", {...}]]`) that `execute_files` runs directly, or a **LoadDensity** Locust load-test (`start_test(...)`). (These are the HTTP-oriented modules; a curl request has no meaningful mapping to the browser or desktop-GUI automation modules.) Parses the method, URL, headers, body, basic auth, `-G` query parameters, `-F` multipart form fields (file uploads become `files=open(...)`), the `--json` shortcut (which also sets the JSON `Content-Type`/`Accept`), and `-d @file` bodies (which become `open(...).read()`, while `--data-raw` stays literal) — all with multi-line `\` / `^` continuations. It knows the arity of curl's common flags, so value-taking options like `--max-time 30` or `-o out.json` never leak into the URL; it URL-encodes `--data-urlencode` values the way curl does; and it infers `POST` when a body or form is present and sends JSON bodies as JSON. Targets include Python `requests`, a ready-to-run **pytest test** (a named `test_...` function that sends the request and asserts the status), APITestka (Python and JSON action), and a LoadDensity load test. Pick one from the dropdown, then copy the result, **open it straight into a new editor tab**, or **save it to a `.py` / `.json` file** (the extension follows the chosen target). A repeated `-H` is handled the way HTTP handles it — the values are combined into one header (with `; ` for cookies) rather than the last one silently winning, and names are matched case-insensitively so an explicit header always beats one implied by another flag. One click also hands the parsed parts to the tool that specialises in them: the **URL to the URL parser/builder** (query string and all) or the **headers to the header analyzer**. The parser is pure logic and never executes the command.
80
+ - **HAR Import** — "Copy as cURL" captures one request; **Save all as HAR** captures the session. Open that `.har` export and every recorded call is listed with its method, path, status and media type — page furniture (stylesheets, images, fonts) filtered out by default so what remains is the API traffic. Select the calls you want, or take everything listed, and generate one script for the whole set using the same targets as the cURL importer: a **pytest file** with one test per call (repeated endpoints get numbered names so no test silently replaces another), an **APITestka action list** that replays the flow in capture order, a **`requests` walkthrough**, an APITestka Python script, or LoadDensity runs. Recorded headers, cookies, query parameters and bodies (raw, URL-encoded and multipart, including file uploads) all come across; HTTP/2 pseudo-headers are dropped and a `Cookie` header that duplicates the recorded cookie list is removed, so the generated code sends each value once. A single selected request produces exactly what the cURL importer would, so the two tools never disagree. HAR is JSON, so this needs nothing beyond the standard library, and nothing is ever replayed for you.
80
81
  - **JWT Decoder** — Paste a JSON Web Token and read its header and payload as pretty-printed JSON, with standard timestamp claims (`exp`, `iat`, `nbf`, `auth_time`) rendered as readable UTC times. Inspection only — the signature is never verified and the token is never trusted.
81
82
  - **Timestamp Converter** — Enter a Unix epoch value (seconds or milliseconds, auto-detected) or an ISO-8601 date-time (with `Z` or an offset) and get every representation back in UTC. Deterministic and independent of the local time zone.
82
83
  - **Hash Generator** — Compute SHA-256, SHA-512, SHA-1 and MD5 digests of any text at once. A general-purpose checksum tool (MD5/SHA-1 are provided for interoperability with `usedforsecurity=False`, never for security decisions).
83
84
  - **Query ⇄ JSON** — Convert an `application/x-www-form-urlencoded` query string to pretty JSON and back, URL-decoding and -encoding as needed. Repeated keys become JSON arrays and vice versa. Copy the result, open it in an editor tab, or save it to a file.
84
85
  - **URL Parser / Builder** — Paste a URL to see its scheme, host, port, path, query, fragment and credentials as an editable JSON object, tweak any part, and turn it back into a URL. Round-trips both ways, brackets IPv6 literals, and re-encodes query parameters for you. The cURL importer can send a request's URL straight here.
85
- - **HTTP Header Analyzer** — Paste a request or response header block (from dev tools, `curl -i`, or a proxy log) and read back every field plus what is worth knowing about it: names sent more than once, `Set-Cookie` entries missing `Secure` / `HttpOnly` / `SameSite`, a wildcard CORS policy (and the wildcard-plus-credentials combination browsers reject outright), an HSTS `max-age` too short to survive a restart, CSP `unsafe-inline` / `unsafe-eval`, product banners, deprecated headers, and — for responses — the security headers that are absent. Headers carrying credentials are reported by name only; their values are never copied into the report. The Response Inspector hands its headers here in one click.
86
+ - **HTTP Header Analyzer** — Paste a request or response header block (from dev tools, `curl -i`, or a proxy log) and read back every field plus what is worth knowing about it: names sent more than once, `Set-Cookie` entries missing `Secure` / `HttpOnly` / `SameSite`, a wildcard CORS policy (and the wildcard-plus-credentials combination browsers reject outright), an HSTS `max-age` too short to survive a restart, CSP `unsafe-inline` / `unsafe-eval`, product banners, deprecated headers, and — for responses — the security headers that are absent. Headers carrying credentials are reported by name only; their values are never copied into the report. The cURL importer and the Response Inspector both hand their headers here in one click, and a bearer token spotted in any header opens in the JWT decoder, already decoded.
86
87
  - **Regex Tester** — Try a regular expression against sample text with `IGNORECASE` / `MULTILINE` / `DOTALL` / `VERBOSE` flags, and see every match with its offsets, numbered groups and named groups. Invalid patterns report a friendly error instead of crashing.
87
88
  - **HTTP Status Reference** — Search the full HTTP status code table (sourced from the standard library, so it stays current) by code prefix or keyword, with the reason phrase, description and status class for each.
88
89
  - **Text Diff** — Compare two pieces of text (for example an expected vs. actual API response) side by side and get a unified diff plus a one-line summary of how many lines were added or removed. Copy the diff, open it in an editor tab, or save it to a file.
89
90
  - **JSON Format** — Pretty-print or minify a JSON payload, with a clear validation error when the input is not valid JSON. Copy the result, open it in a new editor tab, or save it to a file.
90
- - **Response Inspector** — Paste a raw HTTP response and get a decoded summary in one step: the status code is looked up in the HTTP reference, headers are parsed, a JSON body is pretty-printed, and any JWTs anywhere in the text (for example an `Authorization: Bearer` header) are decoded with their timestamp claims shown in UTC. Ties the HTTP-status, JSON-format and JWT tools together, and can open a found status code, JWT, or the whole header block directly in its dedicated tool tab, pre-filled.
91
+ - **Response Inspector** — Paste a raw HTTP response and get a decoded summary in one step: the status code is looked up in the HTTP reference, headers are parsed, a JSON body is pretty-printed, and any JWTs anywhere in the text (for example an `Authorization: Bearer` header) are decoded with their timestamp claims shown in UTC. Ties the HTTP-status, JSON-format and JWT tools together, and can open a found status code, JWT, the whole header block, or a JSON body directly in its dedicated tool tab, pre-filled.
91
92
  - **File Tree Context Menu** — Right-click any file or folder in the project tree to create files/folders, rename, delete, copy absolute or relative paths, or reveal the item in your platform file manager. Renaming or deleting a file that is currently open in an editor tab keeps the tab in sync.
92
93
  - **Package Manager** — Install automation modules and build tools directly from the IDE menu without leaving the editor.
93
94
  - **Integrated Documentation** — Quick access to documentation and GitHub pages for each automation module directly from the menu bar.
@@ -219,6 +220,7 @@ PyBreeze UI (PySide6)
219
220
  │ ├── Skill Send GUI
220
221
  │ ├── Diagram Editor (WYSIWYG, Mermaid import, PNG/SVG export)
221
222
  │ ├── cURL Import (curl → Python requests code)
223
+ │ ├── HAR Import (browser session → test suite)
222
224
  │ ├── JWT Decoder (header/payload inspection)
223
225
  │ ├── Timestamp Converter (epoch ⇄ ISO-8601)
224
226
  │ ├── Hash Generator (SHA-256/512, SHA-1, MD5)
@@ -332,6 +332,25 @@ pybreeze_english_word_dict = {
332
332
  "curl_import_target_apitestka_action": "APITestka (JSON action)",
333
333
  "curl_import_target_loaddensity_python": "LoadDensity (Python)",
334
334
  "curl_import_open_url_button": "Open URL in parser / builder",
335
+ "curl_import_open_headers_button": "Open headers in analyzer",
336
+ # HAR Import — Menu
337
+ "extend_tools_menu_har_import_tab_action": "HAR Import Tab",
338
+ "extend_tools_menu_har_import_tab_label": "HAR Import",
339
+ "extend_tools_menu_har_import_dock_action": "HAR Import Dock",
340
+ "extend_tools_menu_har_import_dock_title": "HAR Import",
341
+ # HAR Import — Widget
342
+ "har_import_open_button": "Open .har file...",
343
+ "har_import_file_dialog_title": "Open a HAR export",
344
+ "har_import_file_filter": "HAR export (*.har *.json)",
345
+ "har_import_api_only": "Only API-like requests",
346
+ "har_import_summary": "{total} request(s), {api} API-like — {hosts}",
347
+ "har_import_generate_selected": "Generate for selection",
348
+ "har_import_generate_all": "Generate for all listed",
349
+ "har_import_no_selection": "Select one or more requests, or generate for all listed.",
350
+ "har_import_empty_hint":
351
+ "Open a .har file saved from your browser's dev tools (Network → Save all as HAR).",
352
+ "har_import_error": "Could not read the HAR export: {error}",
353
+ "har_import_read_error": "Could not open the file: {error}",
335
354
  # Shared output actions (copy / open in editor / save to file)
336
355
  "output_actions_copy": "Copy",
337
356
  "output_actions_open_editor": "Open in editor tab",
@@ -469,6 +488,7 @@ pybreeze_english_word_dict = {
469
488
  "response_open_jwt_button": "Open JWT in decoder",
470
489
  "response_open_status_button": "Open status in reference",
471
490
  "response_open_headers_button": "Open headers in analyzer",
491
+ "response_open_body_button": "Open body in JSON format",
472
492
  "response_empty_hint": "Paste a response above, then click analyze.",
473
493
  "response_status_label": "== Status ==",
474
494
  "response_headers_label": "== Headers ==",
@@ -493,6 +513,7 @@ pybreeze_english_word_dict = {
493
513
  "header_analyzer_empty_hint": "Paste a header block above, then click analyze.",
494
514
  "header_analyzer_level_warning": "WARNING",
495
515
  "header_analyzer_level_info": "INFO",
516
+ "header_analyzer_open_jwt_button": "Open token in JWT decoder",
496
517
  # HTTP Header Analyzer — Findings ({header} is the header, {detail} its value)
497
518
  "header_finding_duplicate_header":
498
519
  "{header}: sent {detail} times; the receiver joins the values into one.",
@@ -310,6 +310,25 @@ pybreeze_traditional_chinese_word_dict = {
310
310
  "curl_import_target_apitestka_action": "APITestka(JSON action)",
311
311
  "curl_import_target_loaddensity_python": "LoadDensity(Python)",
312
312
  "curl_import_open_url_button": "在 URL 解析/組建器開啟網址",
313
+ "curl_import_open_headers_button": "在分析器開啟標頭",
314
+ # HAR 匯入 — 選單
315
+ "extend_tools_menu_har_import_tab_action": "HAR 匯入分頁",
316
+ "extend_tools_menu_har_import_tab_label": "HAR 匯入",
317
+ "extend_tools_menu_har_import_dock_action": "HAR 匯入停駐窗格",
318
+ "extend_tools_menu_har_import_dock_title": "HAR 匯入",
319
+ # HAR 匯入 — 介面
320
+ "har_import_open_button": "開啟 .har 檔...",
321
+ "har_import_file_dialog_title": "開啟 HAR 匯出檔",
322
+ "har_import_file_filter": "HAR 匯出檔 (*.har *.json)",
323
+ "har_import_api_only": "只列出像 API 的請求",
324
+ "har_import_summary": "{total} 個請求,其中 {api} 個像 API — {hosts}",
325
+ "har_import_generate_selected": "為選取項產生",
326
+ "har_import_generate_all": "為列出的全部產生",
327
+ "har_import_no_selection": "請選取一個以上的請求,或改用「為列出的全部產生」。",
328
+ "har_import_empty_hint":
329
+ "請開啟從瀏覽器開發者工具存出的 .har 檔(Network → Save all as HAR)。",
330
+ "har_import_error": "無法讀取這份 HAR 匯出檔:{error}",
331
+ "har_import_read_error": "無法開啟檔案:{error}",
313
332
  # 共用輸出動作(複製 / 開成分頁 / 存檔)
314
333
  "output_actions_copy": "複製",
315
334
  "output_actions_open_editor": "開成編輯器分頁",
@@ -447,6 +466,7 @@ pybreeze_traditional_chinese_word_dict = {
447
466
  "response_open_jwt_button": "在解碼器開啟 JWT",
448
467
  "response_open_status_button": "在參考開啟狀態碼",
449
468
  "response_open_headers_button": "在分析器開啟標頭",
469
+ "response_open_body_button": "在 JSON 格式化開啟內容",
450
470
  "response_empty_hint": "請先在上方貼上回應,再按分析。",
451
471
  "response_status_label": "== 狀態 Status ==",
452
472
  "response_headers_label": "== 標頭 Headers ==",
@@ -471,6 +491,7 @@ pybreeze_traditional_chinese_word_dict = {
471
491
  "header_analyzer_empty_hint": "請先在上方貼上標頭,再按分析。",
472
492
  "header_analyzer_level_warning": "警告",
473
493
  "header_analyzer_level_info": "資訊",
494
+ "header_analyzer_open_jwt_button": "在 JWT 解碼器開啟權杖",
474
495
  # HTTP 標頭分析器 — 發現({header} 為標頭名稱,{detail} 為其值)
475
496
  "header_finding_duplicate_header":
476
497
  "{header}:送出 {detail} 次,接收端會把這些值合併成一個。",
@@ -19,6 +19,7 @@ from pybreeze.pybreeze_ui.tools_gui.curl_import_gui import CurlImportGUI
19
19
  from pybreeze.pybreeze_ui.tools_gui.diff_gui import DiffGUI
20
20
  from pybreeze.pybreeze_ui.tools_gui.json_format_gui import JsonFormatGUI
21
21
  from pybreeze.pybreeze_ui.tools_gui.jwt_decoder_gui import JwtDecoderGUI
22
+ from pybreeze.pybreeze_ui.tools_gui.har_import_gui import HarImportGUI
22
23
  from pybreeze.pybreeze_ui.tools_gui.hash_gui import HashGUI
23
24
  from pybreeze.pybreeze_ui.tools_gui.header_analyzer_gui import HeaderAnalyzerGUI
24
25
  from pybreeze.pybreeze_ui.tools_gui.http_status_gui import HttpStatusGUI
@@ -114,6 +115,17 @@ def build_tools_menu(ui_we_want_to_set: PyBreezeMainWindow):
114
115
  ))
115
116
  ui_we_want_to_set.tools_menu.addAction(ui_we_want_to_set.tools_curl_import_action)
116
117
 
118
+ # HAR Import
119
+ ui_we_want_to_set.tools_har_import_action = QAction(language_wrapper.language_word_dict.get(
120
+ "extend_tools_menu_har_import_tab_action"
121
+ ))
122
+ ui_we_want_to_set.tools_har_import_action.triggered.connect(lambda: ui_we_want_to_set.tab_widget.addTab(
123
+ HarImportGUI(ui_we_want_to_set), language_wrapper.language_word_dict.get(
124
+ "extend_tools_menu_har_import_tab_label"
125
+ )
126
+ ))
127
+ ui_we_want_to_set.tools_menu.addAction(ui_we_want_to_set.tools_har_import_action)
128
+
117
129
  # JWT Decoder
118
130
  ui_we_want_to_set.tools_jwt_decoder_action = QAction(language_wrapper.language_word_dict.get(
119
131
  "extend_tools_menu_jwt_decoder_tab_action"
@@ -292,6 +304,13 @@ def extend_dock_menu(ui_we_want_to_set: PyBreezeMainWindow):
292
304
  lambda: add_dock(ui_we_want_to_set, "CurlImport"))
293
305
  ui_we_want_to_set.dock_menu.addAction(ui_we_want_to_set.tools_curl_import_dock_action)
294
306
 
307
+ # HAR Import Dock
308
+ ui_we_want_to_set.tools_har_import_dock_action = QAction(language_wrapper.language_word_dict.get(
309
+ "extend_tools_menu_har_import_dock_action"))
310
+ ui_we_want_to_set.tools_har_import_dock_action.triggered.connect(
311
+ lambda: add_dock(ui_we_want_to_set, "HarImport"))
312
+ ui_we_want_to_set.dock_menu.addAction(ui_we_want_to_set.tools_har_import_dock_action)
313
+
295
314
  # JWT Decoder Dock
296
315
  ui_we_want_to_set.tools_jwt_decoder_dock_action = QAction(language_wrapper.language_word_dict.get(
297
316
  "extend_tools_menu_jwt_decoder_dock_action"))
@@ -385,6 +404,7 @@ _DOCK_WIDGETS: dict[str, tuple[str, Callable[[PyBreezeMainWindow], object]]] = {
385
404
  "extend_tools_menu_diagram_editor_dock_title", lambda _win: DiagramEditorWidget()),
386
405
  "CurlImport": (
387
406
  "extend_tools_menu_curl_import_dock_title", lambda win: CurlImportGUI(win)),
407
+ "HarImport": ("extend_tools_menu_har_import_dock_title", lambda win: HarImportGUI(win)),
388
408
  "JwtDecoder": (
389
409
  "extend_tools_menu_jwt_decoder_dock_title", lambda win: JwtDecoderGUI(main_window=win)),
390
410
  "Timestamp": ("extend_tools_menu_timestamp_dock_title", lambda win: TimestampGUI(win)),
@@ -10,10 +10,11 @@ URL parser/builder tool for further editing.
10
10
  from __future__ import annotations
11
11
 
12
12
  from PySide6.QtWidgets import (
13
- QComboBox, QLabel, QPushButton, QTextEdit, QVBoxLayout, QWidget
13
+ QComboBox, QHBoxLayout, QLabel, QPushButton, QTextEdit, QVBoxLayout, QWidget
14
14
  )
15
15
  from je_editor import language_wrapper
16
16
 
17
+ from pybreeze.pybreeze_ui.tools_gui.header_analyzer_gui import HeaderAnalyzerGUI
17
18
  from pybreeze.pybreeze_ui.tools_gui.output_actions import OutputActions
18
19
  from pybreeze.pybreeze_ui.tools_gui.tool_tabs import open_tool_tab
19
20
  from pybreeze.pybreeze_ui.tools_gui.url_builder_gui import UrlBuilderGUI
@@ -60,10 +61,18 @@ class CurlImportGUI(QWidget):
60
61
  self.output_edit = QTextEdit()
61
62
  self.output_edit.setReadOnly(True)
62
63
 
63
- # Cross-tool action: send the parsed URL to the URL parser/builder.
64
+ # Cross-tool actions: hand the parsed parts to the tool that specialises
65
+ # in them, rather than making the user copy them across.
64
66
  self.open_url_button = QPushButton(word.get("curl_import_open_url_button"))
65
67
  self.open_url_button.clicked.connect(self.open_url_in_builder)
66
68
  self.open_url_button.setEnabled(False)
69
+ self.open_headers_button = QPushButton(word.get("curl_import_open_headers_button"))
70
+ self.open_headers_button.clicked.connect(self.open_headers_in_analyzer)
71
+ self.open_headers_button.setEnabled(False)
72
+
73
+ cross_tool = QHBoxLayout()
74
+ cross_tool.addWidget(self.open_url_button)
75
+ cross_tool.addWidget(self.open_headers_button)
67
76
 
68
77
  # Shared copy / open-in-editor / save actions. The extension and basename
69
78
  # follow the selected target; open/save are no-ops until a valid template.
@@ -77,9 +86,10 @@ class CurlImportGUI(QWidget):
77
86
  for widget in (
78
87
  self.input_label, self.input_edit,
79
88
  self.target_label, self.target_select, self.convert_button,
80
- self.output_label, self.output_edit, self.open_url_button,
89
+ self.output_label, self.output_edit,
81
90
  ):
82
91
  layout.addWidget(widget)
92
+ layout.addLayout(cross_tool)
83
93
  layout.addLayout(self.actions.button_row())
84
94
  self.setLayout(layout)
85
95
 
@@ -97,6 +107,7 @@ class CurlImportGUI(QWidget):
97
107
  self._generated_code = None
98
108
  self._request = None
99
109
  self.open_url_button.setEnabled(False)
110
+ self.open_headers_button.setEnabled(False)
100
111
 
101
112
  def convert(self) -> None:
102
113
  """Parse the input command and show the template for the chosen target."""
@@ -118,6 +129,7 @@ class CurlImportGUI(QWidget):
118
129
  self._generated_code = code
119
130
  self._request = request
120
131
  self.open_url_button.setEnabled(bool(request.url))
132
+ self.open_headers_button.setEnabled(bool(request.headers))
121
133
  self.output_edit.setPlainText(code)
122
134
 
123
135
  def open_url_in_builder(self) -> QWidget | None:
@@ -132,3 +144,14 @@ class CurlImportGUI(QWidget):
132
144
  self._main_window,
133
145
  UrlBuilderGUI(main_window=self._main_window, initial_url=self._request.full_url),
134
146
  "extend_tools_menu_url_builder_tab_label")
147
+
148
+ def open_headers_in_analyzer(self) -> QWidget | None:
149
+ """Open the command's headers in the header analyzer, already analysed."""
150
+ if self._request is None or not self._request.headers:
151
+ return None
152
+ block = "\n".join(
153
+ f"{name}: {value}" for name, value in self._request.headers.items())
154
+ return open_tool_tab(
155
+ self._main_window,
156
+ HeaderAnalyzerGUI(main_window=self._main_window, initial_headers=block),
157
+ "extend_tools_menu_header_analyzer_tab_label")
@@ -0,0 +1,188 @@
1
+ """A tool tab that turns a browser HAR export into automation scripts.
2
+
3
+ "Copy as cURL" captures one request; **Save all as HAR** captures the session.
4
+ This widget reads that export, lists the recorded requests (hiding page assets
5
+ unless asked), and generates a script for the selected ones — a pytest file of
6
+ several tests, an APITestka action list that replays the flow, and so on — using
7
+ the same targets and output actions as the cURL importer.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+
13
+ from PySide6.QtWidgets import (
14
+ QAbstractItemView, QCheckBox, QComboBox, QFileDialog, QHBoxLayout, QLabel,
15
+ QListWidget, QPushButton, QTextEdit, QVBoxLayout, QWidget
16
+ )
17
+ from je_editor import language_wrapper
18
+
19
+ from pybreeze.pybreeze_ui.tools_gui.output_actions import OutputActions
20
+ from pybreeze.utils.curl_import.script_templates import TEMPLATE_TARGETS
21
+ from pybreeze.utils.exception.exceptions import HarParseException
22
+ from pybreeze.utils.har_import.har_codegen import generate_har_script
23
+ from pybreeze.utils.har_import.har_parser import HarEntry, api_entries, parse_har, summarize
24
+ from pybreeze.utils.logging.logger import pybreeze_logger
25
+
26
+ # The single target that generates JSON rather than Python
27
+ _JSON_TARGET = "apitestka_action"
28
+ # Separator between hosts in the summary line
29
+ _HOST_SEPARATOR = ", "
30
+ # Hosts listed before the summary is shortened
31
+ _MAX_LISTED_HOSTS = 3
32
+
33
+
34
+ class HarImportGUI(QWidget):
35
+ """Open a ``.har`` export, pick recorded requests, and generate a script."""
36
+
37
+ def __init__(self, main_window=None) -> None:
38
+ """
39
+ :param main_window: the window whose ``tab_widget`` "open in editor" uses
40
+ """
41
+ super().__init__()
42
+ # Every request in the export, and the subset currently listed.
43
+ self._entries: list[HarEntry] = []
44
+ self._shown: list[HarEntry] = []
45
+ self._generated_code: str | None = None
46
+ word = language_wrapper.language_word_dict
47
+
48
+ self.open_button = QPushButton(word.get("har_import_open_button"))
49
+ self.open_button.clicked.connect(self.open_file)
50
+ self.api_only_check = QCheckBox(word.get("har_import_api_only"))
51
+ self.api_only_check.setChecked(True)
52
+ self.api_only_check.stateChanged.connect(self._refresh_entry_list)
53
+
54
+ top_row = QHBoxLayout()
55
+ top_row.addWidget(self.open_button)
56
+ top_row.addWidget(self.api_only_check)
57
+
58
+ self.summary_label = QLabel(word.get("har_import_empty_hint"))
59
+ self.entry_list = QListWidget()
60
+ self.entry_list.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
61
+
62
+ self.target_label = QLabel(word.get("curl_import_target_label"))
63
+ self.target_select = QComboBox()
64
+ for target_key, label_key in TEMPLATE_TARGETS:
65
+ self.target_select.addItem(word.get(label_key), target_key)
66
+
67
+ self.generate_selected_button = QPushButton(word.get("har_import_generate_selected"))
68
+ self.generate_selected_button.clicked.connect(self.generate_selected)
69
+ self.generate_all_button = QPushButton(word.get("har_import_generate_all"))
70
+ self.generate_all_button.clicked.connect(self.generate_all)
71
+
72
+ generate_row = QHBoxLayout()
73
+ generate_row.addWidget(self.generate_selected_button)
74
+ generate_row.addWidget(self.generate_all_button)
75
+
76
+ self.output_label = QLabel(word.get("curl_import_output_label"))
77
+ self.output_edit = QTextEdit()
78
+ self.output_edit.setReadOnly(True)
79
+
80
+ self.actions = OutputActions(
81
+ self, self.output_edit, main_window=main_window,
82
+ basename=lambda: "actions" if self.selected_target() == _JSON_TARGET else "session",
83
+ extension=lambda: "json" if self.selected_target() == _JSON_TARGET else "py",
84
+ is_valid=lambda: self._generated_code is not None)
85
+
86
+ layout = QVBoxLayout()
87
+ layout.addLayout(top_row)
88
+ layout.addWidget(self.summary_label)
89
+ layout.addWidget(self.entry_list)
90
+ layout.addWidget(self.target_label)
91
+ layout.addWidget(self.target_select)
92
+ layout.addLayout(generate_row)
93
+ layout.addWidget(self.output_label)
94
+ layout.addWidget(self.output_edit)
95
+ layout.addLayout(self.actions.button_row())
96
+ self.setLayout(layout)
97
+
98
+ def selected_target(self) -> str:
99
+ """Return the template key of the currently selected target."""
100
+ return self.target_select.currentData()
101
+
102
+ def open_file(self) -> str | None:
103
+ """Ask for a ``.har`` file, then load it; return the path or ``None``."""
104
+ word = language_wrapper.language_word_dict
105
+ path, _selected = QFileDialog.getOpenFileName(
106
+ self, word.get("har_import_file_dialog_title"), "",
107
+ word.get("har_import_file_filter"))
108
+ if not path:
109
+ return None
110
+ try:
111
+ text = Path(path).read_text(encoding="utf-8")
112
+ except OSError as error:
113
+ pybreeze_logger.info("har_import_gui.py read failed: %r", error)
114
+ self._report_error(word.get("har_import_read_error").format(error=str(error)))
115
+ return None
116
+ self.load_text(text)
117
+ return path
118
+
119
+ def load_text(self, text: str) -> bool:
120
+ """Parse *text* as a HAR export and list what it recorded.
121
+
122
+ :param text: the contents of a ``.har`` file
123
+ :return: whether the export parsed
124
+ """
125
+ try:
126
+ entries = parse_har(text)
127
+ except HarParseException as error:
128
+ pybreeze_logger.info("har_import_gui.py parse failed: %r", error)
129
+ self._entries = []
130
+ self._refresh_entry_list()
131
+ self._report_error(
132
+ language_wrapper.language_word_dict.get("har_import_error").format(
133
+ error=str(error)))
134
+ return False
135
+ self._entries = entries
136
+ self._refresh_entry_list()
137
+ return True
138
+
139
+ def _report_error(self, message: str) -> None:
140
+ """Show *message* as the summary and clear any generated output."""
141
+ self._generated_code = None
142
+ self.summary_label.setText(message)
143
+ self.output_edit.setPlainText(message)
144
+
145
+ def _summary_text(self) -> str:
146
+ """Return the counts-and-hosts line describing the loaded export."""
147
+ word = language_wrapper.language_word_dict
148
+ if not self._entries:
149
+ return word.get("har_import_empty_hint")
150
+ summary = summarize(self._entries)
151
+ hosts = _HOST_SEPARATOR.join(summary.hosts[:_MAX_LISTED_HOSTS])
152
+ if len(summary.hosts) > _MAX_LISTED_HOSTS:
153
+ hosts = f"{hosts}…"
154
+ return word.get("har_import_summary").format(
155
+ total=summary.total, api=summary.api, hosts=hosts)
156
+
157
+ def _refresh_entry_list(self) -> None:
158
+ """Rebuild the list from the current export and the API-only filter."""
159
+ self._shown = api_entries(self._entries) if self.api_only_check.isChecked() \
160
+ else list(self._entries)
161
+ self.entry_list.clear()
162
+ self.entry_list.addItems([entry.summary() for entry in self._shown])
163
+ self.summary_label.setText(self._summary_text())
164
+
165
+ def selected_entries(self) -> list[HarEntry]:
166
+ """Return the listed entries the user selected, in capture order."""
167
+ rows = sorted(index.row() for index in self.entry_list.selectedIndexes())
168
+ return [self._shown[row] for row in rows if 0 <= row < len(self._shown)]
169
+
170
+ def _generate(self, entries: list[HarEntry], empty_hint_key: str) -> None:
171
+ """Generate a script for *entries*, or show the hint when there are none."""
172
+ word = language_wrapper.language_word_dict
173
+ if not entries:
174
+ self._generated_code = None
175
+ self.output_edit.setPlainText(word.get(empty_hint_key))
176
+ return
177
+ code = generate_har_script(
178
+ self.selected_target(), [entry.request for entry in entries])
179
+ self._generated_code = code
180
+ self.output_edit.setPlainText(code)
181
+
182
+ def generate_selected(self) -> None:
183
+ """Generate a script covering the selected requests."""
184
+ self._generate(self.selected_entries(), "har_import_no_selection")
185
+
186
+ def generate_all(self) -> None:
187
+ """Generate a script covering every request currently listed."""
188
+ self._generate(self._shown, "har_import_empty_hint")
@@ -10,10 +10,13 @@ from __future__ import annotations
10
10
  from PySide6.QtWidgets import QLabel, QPushButton, QTextEdit, QVBoxLayout, QWidget
11
11
  from je_editor import language_wrapper
12
12
 
13
+ from pybreeze.pybreeze_ui.tools_gui.jwt_decoder_gui import JwtDecoderGUI
13
14
  from pybreeze.pybreeze_ui.tools_gui.output_actions import OutputActions
15
+ from pybreeze.pybreeze_ui.tools_gui.tool_tabs import open_tool_tab
14
16
  from pybreeze.utils.header_tools.header_analyzer import (
15
17
  HeaderAnalysis, HeaderFinding, analyze_headers
16
18
  )
19
+ from pybreeze.utils.jwt_tools.jwt_decoder import find_tokens
17
20
 
18
21
 
19
22
  def _finding_line(finding: HeaderFinding) -> str:
@@ -58,6 +61,7 @@ class HeaderAnalyzerGUI(QWidget):
58
61
  :param initial_headers: a header block to pre-fill and analyse on open
59
62
  """
60
63
  super().__init__()
64
+ self._main_window = main_window
61
65
  self._analysis: HeaderAnalysis | None = None
62
66
  word = language_wrapper.language_word_dict
63
67
 
@@ -73,6 +77,12 @@ class HeaderAnalyzerGUI(QWidget):
73
77
  self.output_edit = QTextEdit()
74
78
  self.output_edit.setReadOnly(True)
75
79
 
80
+ # Cross-tool action: a bearer token in a header is worth reading, and the
81
+ # decoder is the tool that reads it.
82
+ self.open_jwt_button = QPushButton(word.get("header_analyzer_open_jwt_button"))
83
+ self.open_jwt_button.clicked.connect(self.open_jwt_in_decoder)
84
+ self.open_jwt_button.setEnabled(False)
85
+
76
86
  self.actions = OutputActions(
77
87
  self, self.output_edit, main_window=main_window,
78
88
  basename="headers", extension="txt",
@@ -81,7 +91,7 @@ class HeaderAnalyzerGUI(QWidget):
81
91
  layout = QVBoxLayout()
82
92
  for widget in (
83
93
  self.input_label, self.input_edit, self.analyze_button,
84
- self.output_label, self.output_edit,
94
+ self.output_label, self.output_edit, self.open_jwt_button,
85
95
  ):
86
96
  layout.addWidget(widget)
87
97
  layout.addLayout(self.actions.button_row())
@@ -91,18 +101,42 @@ class HeaderAnalyzerGUI(QWidget):
91
101
  self.input_edit.setPlainText(initial_headers)
92
102
  self.analyze()
93
103
 
104
+ def _clear_analysis(self, message: str) -> None:
105
+ """Forget the analysis and show *message* instead of a report."""
106
+ self._analysis = None
107
+ self.open_jwt_button.setEnabled(False)
108
+ self.output_edit.setPlainText(message)
109
+
94
110
  def analyze(self) -> None:
95
111
  """Analyse the pasted headers and show the report."""
96
112
  word = language_wrapper.language_word_dict
97
113
  text = self.input_edit.toPlainText().strip()
98
114
  if not text:
99
- self._analysis = None
100
- self.output_edit.setPlainText(word.get("header_analyzer_empty_hint"))
115
+ self._clear_analysis(word.get("header_analyzer_empty_hint"))
101
116
  return
102
117
  analysis = analyze_headers(text)
103
118
  if not analysis.fields:
104
- self._analysis = None
105
- self.output_edit.setPlainText(word.get("header_analyzer_no_headers"))
119
+ self._clear_analysis(word.get("header_analyzer_no_headers"))
106
120
  return
107
121
  self._analysis = analysis
122
+ self.open_jwt_button.setEnabled(bool(self.header_tokens()))
108
123
  self.output_edit.setPlainText(build_header_report(analysis))
124
+
125
+ def header_tokens(self) -> list[str]:
126
+ """Return the JWT-looking tokens carried by the analysed headers."""
127
+ if self._analysis is None:
128
+ return []
129
+ tokens: list[str] = []
130
+ for header in self._analysis.fields:
131
+ tokens.extend(token for token in find_tokens(header.value) if token not in tokens)
132
+ return tokens
133
+
134
+ def open_jwt_in_decoder(self) -> QWidget | None:
135
+ """Open the first token found in a header in the JWT decoder, decoded."""
136
+ tokens = self.header_tokens()
137
+ if not tokens:
138
+ return None
139
+ return open_tool_tab(
140
+ self._main_window,
141
+ JwtDecoderGUI(initial_token=tokens[0], main_window=self._main_window),
142
+ "extend_tools_menu_jwt_decoder_tab_label")
@@ -15,9 +15,10 @@ from pybreeze.utils.logging.logger import pybreeze_logger
15
15
  class JsonFormatGUI(QWidget):
16
16
  """Paste JSON, then format it, minify it, or read its validation error."""
17
17
 
18
- def __init__(self, main_window=None) -> None:
18
+ def __init__(self, main_window=None, initial_json: str | None = None) -> None:
19
19
  """
20
20
  :param main_window: window whose ``tab_widget`` "open in editor" uses
21
+ :param initial_json: JSON to pre-fill and format on open, if given
21
22
  """
22
23
  super().__init__()
23
24
  # The last valid result (not an error/hint), gating open/save.
@@ -56,6 +57,10 @@ class JsonFormatGUI(QWidget):
56
57
  layout.addLayout(self.actions.button_row())
57
58
  self.setLayout(layout)
58
59
 
60
+ if initial_json:
61
+ self.input_edit.setPlainText(initial_json)
62
+ self.format_json()
63
+
59
64
  def _run(self, transform) -> None:
60
65
  """Apply a JSON transform, showing the result or a friendly error."""
61
66
  word = language_wrapper.language_word_dict